@aifabrix/miso-client 3.8.2 → 4.0.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 (126) hide show
  1. package/CHANGELOG.md +1116 -1007
  2. package/dist/api/encryption.api.d.ts +31 -0
  3. package/dist/api/encryption.api.d.ts.map +1 -0
  4. package/dist/api/encryption.api.js +61 -0
  5. package/dist/api/encryption.api.js.map +1 -0
  6. package/dist/api/index.d.ts +5 -0
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/api/index.js +2 -0
  9. package/dist/api/index.js.map +1 -1
  10. package/dist/api/types/encryption.types.d.ts +43 -0
  11. package/dist/api/types/encryption.types.d.ts.map +1 -0
  12. package/dist/api/types/encryption.types.js +7 -0
  13. package/dist/api/types/encryption.types.js.map +1 -0
  14. package/dist/express/index.d.ts +0 -1
  15. package/dist/express/index.d.ts.map +1 -1
  16. package/dist/express/index.js +1 -4
  17. package/dist/express/index.js.map +1 -1
  18. package/dist/index.d.ts +43 -158
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +50 -167
  21. package/dist/index.js.map +1 -1
  22. package/dist/services/auth-error-handler.d.ts +21 -0
  23. package/dist/services/auth-error-handler.d.ts.map +1 -0
  24. package/dist/services/auth-error-handler.js +68 -0
  25. package/dist/services/auth-error-handler.js.map +1 -0
  26. package/dist/services/auth.service.d.ts +0 -1
  27. package/dist/services/auth.service.d.ts.map +1 -1
  28. package/dist/services/auth.service.js +31 -298
  29. package/dist/services/auth.service.js.map +1 -1
  30. package/dist/services/encryption.service.d.ts +47 -0
  31. package/dist/services/encryption.service.d.ts.map +1 -0
  32. package/dist/services/encryption.service.js +64 -0
  33. package/dist/services/encryption.service.js.map +1 -0
  34. package/dist/services/logger/logger.service.d.ts +8 -80
  35. package/dist/services/logger/logger.service.d.ts.map +1 -1
  36. package/dist/services/logger/logger.service.js +8 -80
  37. package/dist/services/logger/logger.service.js.map +1 -1
  38. package/dist/services/permission.service.d.ts +0 -4
  39. package/dist/services/permission.service.d.ts.map +1 -1
  40. package/dist/services/permission.service.js +2 -23
  41. package/dist/services/permission.service.js.map +1 -1
  42. package/dist/services/role.service.d.ts +0 -4
  43. package/dist/services/role.service.d.ts.map +1 -1
  44. package/dist/services/role.service.js +2 -23
  45. package/dist/services/role.service.js.map +1 -1
  46. package/dist/types/config.types.d.ts +0 -1
  47. package/dist/types/config.types.d.ts.map +1 -1
  48. package/dist/types/config.types.js.map +1 -1
  49. package/dist/types/filter-schema.types.d.ts +104 -0
  50. package/dist/types/filter-schema.types.d.ts.map +1 -0
  51. package/dist/types/filter-schema.types.js +40 -0
  52. package/dist/types/filter-schema.types.js.map +1 -0
  53. package/dist/types/filter.types.d.ts +1 -1
  54. package/dist/types/filter.types.d.ts.map +1 -1
  55. package/dist/types/filter.types.js.map +1 -1
  56. package/dist/utils/config-loader.d.ts.map +1 -1
  57. package/dist/utils/config-loader.js +0 -4
  58. package/dist/utils/config-loader.js.map +1 -1
  59. package/dist/utils/data-client-auth.d.ts +5 -37
  60. package/dist/utils/data-client-auth.d.ts.map +1 -1
  61. package/dist/utils/data-client-auth.js +98 -377
  62. package/dist/utils/data-client-auth.js.map +1 -1
  63. package/dist/utils/data-client-init.d.ts +46 -0
  64. package/dist/utils/data-client-init.d.ts.map +1 -0
  65. package/dist/utils/data-client-init.js +128 -0
  66. package/dist/utils/data-client-init.js.map +1 -0
  67. package/dist/utils/data-client-oauth.d.ts +20 -0
  68. package/dist/utils/data-client-oauth.d.ts.map +1 -0
  69. package/dist/utils/data-client-oauth.js +138 -0
  70. package/dist/utils/data-client-oauth.js.map +1 -0
  71. package/dist/utils/data-client-permissions.d.ts +63 -0
  72. package/dist/utils/data-client-permissions.d.ts.map +1 -0
  73. package/dist/utils/data-client-permissions.js +123 -0
  74. package/dist/utils/data-client-permissions.js.map +1 -0
  75. package/dist/utils/data-client-request.d.ts +1 -1
  76. package/dist/utils/data-client-request.d.ts.map +1 -1
  77. package/dist/utils/data-client-request.js +35 -235
  78. package/dist/utils/data-client-request.js.map +1 -1
  79. package/dist/utils/data-client-response.d.ts +40 -0
  80. package/dist/utils/data-client-response.d.ts.map +1 -0
  81. package/dist/utils/data-client-response.js +144 -0
  82. package/dist/utils/data-client-response.js.map +1 -0
  83. package/dist/utils/data-client-roles.d.ts +63 -0
  84. package/dist/utils/data-client-roles.d.ts.map +1 -0
  85. package/dist/utils/data-client-roles.js +123 -0
  86. package/dist/utils/data-client-roles.js.map +1 -0
  87. package/dist/utils/data-client.d.ts +0 -185
  88. package/dist/utils/data-client.d.ts.map +1 -1
  89. package/dist/utils/data-client.js +66 -505
  90. package/dist/utils/data-client.js.map +1 -1
  91. package/dist/utils/encryption-error.d.ts +24 -0
  92. package/dist/utils/encryption-error.d.ts.map +1 -0
  93. package/dist/utils/encryption-error.js +31 -0
  94. package/dist/utils/encryption-error.js.map +1 -0
  95. package/dist/utils/filter-colon.utils.d.ts +26 -0
  96. package/dist/utils/filter-colon.utils.d.ts.map +1 -0
  97. package/dist/utils/filter-colon.utils.js +112 -0
  98. package/dist/utils/filter-colon.utils.js.map +1 -0
  99. package/dist/utils/filter-schema.utils.d.ts +84 -0
  100. package/dist/utils/filter-schema.utils.d.ts.map +1 -0
  101. package/dist/utils/filter-schema.utils.js +381 -0
  102. package/dist/utils/filter-schema.utils.js.map +1 -0
  103. package/dist/utils/filter.utils.d.ts +9 -85
  104. package/dist/utils/filter.utils.d.ts.map +1 -1
  105. package/dist/utils/filter.utils.js +79 -138
  106. package/dist/utils/filter.utils.js.map +1 -1
  107. package/dist/utils/http-error-handler.d.ts +22 -0
  108. package/dist/utils/http-error-handler.d.ts.map +1 -0
  109. package/dist/utils/http-error-handler.js +84 -0
  110. package/dist/utils/http-error-handler.js.map +1 -0
  111. package/dist/utils/http-response-validator.d.ts +15 -0
  112. package/dist/utils/http-response-validator.d.ts.map +1 -0
  113. package/dist/utils/http-response-validator.js +42 -0
  114. package/dist/utils/http-response-validator.js.map +1 -0
  115. package/dist/utils/internal-http-client.d.ts +4 -22
  116. package/dist/utils/internal-http-client.d.ts.map +1 -1
  117. package/dist/utils/internal-http-client.js +53 -337
  118. package/dist/utils/internal-http-client.js.map +1 -1
  119. package/dist/utils/token-utils.d.ts.map +1 -1
  120. package/dist/utils/token-utils.js +1 -29
  121. package/dist/utils/token-utils.js.map +1 -1
  122. package/package.json +84 -84
  123. package/dist/express/encryption.d.ts +0 -29
  124. package/dist/express/encryption.d.ts.map +0 -1
  125. package/dist/express/encryption.js +0 -95
  126. package/dist/express/encryption.js.map +0 -1
@@ -46,21 +46,14 @@ const data_client_cache_1 = require("./data-client-cache");
46
46
  const data_client_request_1 = require("./data-client-request");
47
47
  const data_client_auth_1 = require("./data-client-auth");
48
48
  const data_client_redirect_1 = require("./data-client-redirect");
49
- const browser_permission_service_1 = require("../services/browser-permission.service");
50
- const browser_role_service_1 = require("../services/browser-role.service");
51
- const cache_service_1 = require("../services/cache.service");
52
- const http_client_1 = require("../utils/http-client");
53
- const internal_http_client_1 = require("../utils/internal-http-client");
54
- const api_1 = require("../api");
55
- const logger_1 = require("../services/logger");
56
- const redis_service_1 = require("../services/redis.service");
49
+ const data_client_init_1 = require("./data-client-init");
50
+ const permissionHelpers = __importStar(require("./data-client-permissions"));
51
+ const roleHelpers = __importStar(require("./data-client-roles"));
57
52
  class DataClient {
58
53
  constructor(config) {
59
54
  this.misoClient = null;
60
55
  this.cache = new Map();
61
56
  this.pendingRequests = new Map();
62
- // Track failed requests to prevent rapid re-requests (deduplication for failures)
63
- // Extended cooldown period to prevent retry storms
64
57
  this.failedRequests = new Map();
65
58
  this.interceptors = {};
66
59
  this.metrics = {
@@ -72,227 +65,85 @@ class DataClient {
72
65
  };
73
66
  this.permissionService = null;
74
67
  this.roleService = null;
75
- this.config = {
76
- tokenKeys: ["token", "accessToken", "authToken"],
77
- loginUrl: "/login",
78
- timeout: 30000,
79
- cache: {
80
- enabled: true,
81
- defaultTTL: 300,
82
- maxSize: 100,
83
- },
84
- retry: {
85
- enabled: true,
86
- maxRetries: 3,
87
- baseDelay: 1000,
88
- maxDelay: 10000,
89
- },
90
- audit: {
91
- enabled: true,
92
- level: "standard",
93
- batchSize: 10,
94
- maxResponseSize: 10000,
95
- maxMaskingSize: 50000,
96
- skipEndpoints: [],
97
- },
98
- ...config,
99
- };
100
- // Security: Warn if clientSecret is provided in browser environment
101
- // This is a security risk as clientSecret should never be exposed in client-side code
102
- if ((0, data_client_utils_1.isBrowser)() && this.config.misoConfig?.clientSecret) {
103
- console.warn("⚠️ SECURITY WARNING: clientSecret detected in browser environment. " +
104
- "Client secrets should NEVER be exposed in client-side code. " +
105
- "Use the client token pattern instead (clientToken + onClientTokenRefresh). " +
106
- "See documentation for browser-safe configuration.");
107
- }
68
+ this.config = (0, data_client_init_1.createDefaultConfig)(config);
69
+ (0, data_client_init_1.warnIfClientSecretInBrowser)(this.config);
108
70
  // Initialize MisoClient if config provided
109
- let misoConfigWithRefresh;
110
- if (this.config.misoConfig) {
111
- // Automatically bridge DataClient.getEnvironmentToken() to MisoClient
112
- // This allows MisoClient's logger service to get client tokens automatically
113
- // Users don't need to manually provide onClientTokenRefresh!
114
- misoConfigWithRefresh = {
115
- ...this.config.misoConfig,
116
- // Only auto-bridge if:
117
- // 1. User hasn't provided onClientTokenRefresh (allow override)
118
- // 2. We're in browser (server-side uses clientSecret)
119
- // 3. No clientSecret provided (would use that instead)
120
- onClientTokenRefresh: this.config.misoConfig.onClientTokenRefresh ||
121
- ((0, data_client_utils_1.isBrowser)() && !this.config.misoConfig.clientSecret
122
- ? async () => {
123
- const token = await this.getEnvironmentToken();
124
- if (!token) {
125
- throw new Error("Failed to get client token");
126
- }
127
- // Get expiration from localStorage (set by getEnvironmentToken)
128
- const expiresAtStr = (0, data_client_utils_1.getLocalStorage)("miso:client-token-expires-at");
129
- const expiresAt = expiresAtStr
130
- ? parseInt(expiresAtStr, 10)
131
- : Date.now() + 3600000; // Default 1 hour
132
- const expiresIn = Math.floor((expiresAt - Date.now()) / 1000);
133
- return {
134
- token,
135
- expiresIn: expiresIn > 0 ? expiresIn : 3600, // Default 1 hour if invalid
136
- };
137
- }
138
- : undefined),
139
- };
71
+ const misoConfigWithRefresh = (0, data_client_init_1.createMisoConfigWithRefresh)(this.config, () => this.getEnvironmentToken());
72
+ if (misoConfigWithRefresh) {
140
73
  this.misoClient = new index_1.MisoClient(misoConfigWithRefresh);
141
74
  }
142
75
  // Initialize DataMasker with config path if provided
143
76
  if (this.config.misoConfig?.sensitiveFieldsConfig) {
144
77
  data_masker_1.DataMasker.setConfigPath(this.config.misoConfig.sensitiveFieldsConfig);
145
78
  }
146
- // Initialize browser-compatible permission and role services
147
- // These services need HttpClient and CacheService, so they're initialized after MisoClient
148
- if (this.misoClient && misoConfigWithRefresh) {
149
- // Create InternalHttpClient first (base HTTP functionality)
150
- // Use misoConfigWithRefresh which includes onClientTokenRefresh callback
151
- const internalClient = new internal_http_client_1.InternalHttpClient(misoConfigWithRefresh);
152
- // Create Redis service (will be undefined for browser, but needed for LoggerService)
153
- const redis = new redis_service_1.RedisService(misoConfigWithRefresh.redis);
154
- // Create LoggerService with InternalHttpClient (needs httpClient.request() and httpClient.config)
155
- const logger = new logger_1.LoggerService(internalClient, redis);
156
- // Create HttpClient that wraps InternalHttpClient with logger
157
- // Use misoConfigWithRefresh which includes onClientTokenRefresh callback
158
- const httpClient = new http_client_1.HttpClient(misoConfigWithRefresh, logger);
159
- // Update LoggerService to use the new HttpClient (for logging)
160
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
- logger.httpClient = httpClient;
162
- // Create ApiClient that wraps HttpClient (provides typed API interfaces)
163
- const apiClient = new api_1.ApiClient(httpClient);
164
- // Set ApiClient in LoggerService (resolves circular dependency)
165
- logger.setApiClient(apiClient);
166
- // Create CacheService without Redis (in-memory only for browser)
167
- const cacheService = new cache_service_1.CacheService(undefined);
168
- // Create browser-compatible services (pass both httpClient and apiClient)
169
- this.permissionService = new browser_permission_service_1.BrowserPermissionService(httpClient, apiClient, cacheService);
170
- this.roleService = new browser_role_service_1.BrowserRoleService(httpClient, apiClient, cacheService);
171
- }
79
+ // Initialize browser services
80
+ const services = (0, data_client_init_1.initializeBrowserServices)(this.misoClient, misoConfigWithRefresh);
81
+ this.permissionService = services.permissionService;
82
+ this.roleService = services.roleService;
172
83
  // Auto-handle OAuth callback on initialization (browser only)
173
- // This ensures tokens are extracted immediately when DataClient is created
174
84
  if ((0, data_client_utils_1.isBrowser)()) {
175
85
  this.handleOAuthCallback();
176
86
  }
177
87
  }
178
- /**
179
- * Get authentication token from localStorage
180
- */
88
+ // ==================== PRIVATE HELPERS ====================
181
89
  getToken() {
182
90
  return (0, data_client_auth_1.getToken)(this.config.tokenKeys);
183
91
  }
184
- /**
185
- * Check if client token is available (from localStorage cache or config)
186
- */
187
92
  hasClientToken() {
188
93
  return (0, data_client_auth_1.hasClientToken)(this.misoClient, this.config.misoConfig);
189
94
  }
190
- /**
191
- * Check if any authentication token is available (user token OR client token)
192
- */
193
95
  hasAnyToken() {
194
96
  return (0, data_client_auth_1.hasAnyToken)(this.config.tokenKeys, this.misoClient, this.config.misoConfig);
195
97
  }
196
- /**
197
- * Get client token for requests
198
- * Checks localStorage cache first, then config, then calls getEnvironmentToken() if needed
199
- * @returns Client token string or null if unavailable
200
- */
201
98
  async getClientToken() {
202
99
  return (0, data_client_auth_1.getClientToken)(this.config.misoConfig, this.config.baseUrl, () => this.getEnvironmentToken());
203
100
  }
204
- /**
205
- * Build controller URL from configuration
206
- * Uses controllerPublicUrl (browser) or controllerUrl (fallback)
207
- * @returns Controller base URL or null if not configured
208
- */
209
101
  getControllerUrl() {
210
102
  return (0, data_client_auth_1.getControllerUrl)(this.config.misoConfig);
211
103
  }
212
- /**
213
- * Check if user is authenticated
214
- */
104
+ // ==================== PUBLIC AUTH METHODS ====================
215
105
  isAuthenticated() {
216
106
  return this.getToken() !== null;
217
107
  }
218
- /**
219
- * Handle OAuth callback from authentication redirect
220
- * Extracts token from URL hash fragment and stores securely
221
- * ISO 27001 compliant with immediate cleanup and validation
222
- *
223
- * @returns Extracted token or null if not found/invalid
224
- */
225
108
  handleOAuthCallback() {
226
109
  return (0, data_client_auth_1.handleOAuthCallback)(this.config);
227
110
  }
228
- /**
229
- * Redirect to login page via controller
230
- * Calls the controller login endpoint with redirect parameter and x-client-token header
231
- * @param redirectUrl - Optional redirect URL to return to after login (defaults to current page URL)
232
- */
233
111
  async redirectToLogin(redirectUrl) {
234
112
  return (0, data_client_redirect_1.redirectToLogin)(this.config, () => this.getClientToken(), redirectUrl);
235
113
  }
236
- /**
237
- * Logout user and redirect
238
- * Calls logout API with x-client-token header, clears tokens from localStorage, clears cache, and redirects
239
- * @param redirectUrl - Optional redirect URL after logout (defaults to logoutUrl or loginUrl)
240
- */
241
114
  async logout(redirectUrl) {
242
115
  return (0, data_client_auth_1.logout)(this.config, () => this.getToken(), () => this.getClientToken(), () => this.clearCache(), redirectUrl, this.misoClient);
243
116
  }
244
- /**
245
- * Set interceptors
246
- */
117
+ // ==================== CONFIGURATION ====================
247
118
  setInterceptors(config) {
248
119
  this.interceptors = { ...this.interceptors, ...config };
249
120
  }
250
- /**
251
- * Set audit configuration
252
- */
253
121
  setAuditConfig(config) {
254
122
  this.config.audit = { ...this.config.audit, ...config };
255
123
  }
256
- /**
257
- * Set log level for MisoClient logger
258
- * Note: This updates the MisoClient config logLevel if MisoClient is initialized
259
- * @param level - Log level ('debug' | 'info' | 'warn' | 'error')
260
- */
261
124
  setLogLevel(level) {
262
125
  if (this.misoClient && this.config.misoConfig) {
263
- // Update the config's logLevel
264
- // Note: TypeScript readonly doesn't prevent runtime updates
265
126
  this.config.misoConfig.logLevel = level;
266
- // Also try to update MisoClient's internal config if accessible
267
127
  try {
268
128
  const misoConfig = this.misoClient.config;
269
- if (misoConfig) {
129
+ if (misoConfig)
270
130
  misoConfig.logLevel = level;
271
- }
272
131
  }
273
132
  catch {
274
133
  // Silently ignore if config is not accessible
275
134
  }
276
135
  }
277
136
  }
278
- /**
279
- * Clear all cached responses
280
- */
281
137
  clearCache() {
282
138
  this.cache.clear();
283
139
  }
284
- /**
285
- * Get request metrics
286
- */
287
140
  getMetrics() {
288
141
  const responseTimes = (this.metrics.responseTimes || []).sort((a, b) => a - b);
289
142
  const len = responseTimes.length;
290
143
  return {
291
144
  totalRequests: this.metrics.totalRequests,
292
145
  totalFailures: this.metrics.totalFailures,
293
- averageResponseTime: len > 0
294
- ? responseTimes.reduce((a, b) => a + b, 0) / len
295
- : 0,
146
+ averageResponseTime: len > 0 ? responseTimes.reduce((a, b) => a + b, 0) / len : 0,
296
147
  responseTimeDistribution: {
297
148
  min: len > 0 ? responseTimes[0] : 0,
298
149
  max: len > 0 ? responseTimes[len - 1] : 0,
@@ -300,128 +151,84 @@ class DataClient {
300
151
  p95: len > 0 ? responseTimes[Math.floor(len * 0.95)] : 0,
301
152
  p99: len > 0 ? responseTimes[Math.floor(len * 0.99)] : 0,
302
153
  },
303
- errorRate: this.metrics.totalRequests > 0
304
- ? this.metrics.totalFailures / this.metrics.totalRequests
305
- : 0,
154
+ errorRate: this.metrics.totalRequests > 0 ? this.metrics.totalFailures / this.metrics.totalRequests : 0,
306
155
  cacheHitRate: this.metrics.cacheHits + this.metrics.cacheMisses > 0
307
- ? this.metrics.cacheHits /
308
- (this.metrics.cacheHits + this.metrics.cacheMisses)
156
+ ? this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses)
309
157
  : 0,
310
158
  };
311
159
  }
312
- /**
313
- * Make HTTP request with all features (caching, retry, deduplication, audit)
314
- */
160
+ // ==================== HTTP REQUEST CORE ====================
315
161
  async request(method, endpoint, options) {
316
162
  const startTime = Date.now();
317
163
  const fullUrl = `${this.config.baseUrl}${endpoint}`;
318
- // Generate cache key with explicit method to ensure consistency
319
- // Use method parameter (from get/post/etc) not options.method which might be undefined
320
164
  const cacheKeyOptions = { ...options, method: method.toUpperCase() };
321
165
  const cacheKey = (0, data_client_cache_1.getCacheKeyForRequest)(endpoint, cacheKeyOptions);
322
166
  const isGetRequest = method.toUpperCase() === "GET";
323
167
  const cacheEnabled = (0, data_client_cache_1.isCacheEnabled)(method, this.config.cache, options);
324
- // CRITICAL: Check circuit breaker FIRST (before cache/pending checks)
325
- // This prevents any requests to failing endpoints, even if cache is cleared
168
+ // Circuit breaker check
326
169
  const failedRequest = this.failedRequests.get(cacheKey);
327
170
  if (failedRequest) {
328
171
  const timeSinceFailure = Date.now() - failedRequest.timestamp;
329
172
  const failureCount = failedRequest.count || 1;
330
- // Exponential backoff: 5s, 15s, 30s, 30s...
331
173
  const cooldownPeriod = failureCount === 1 ? 5000 : failureCount === 2 ? 15000 : 30000;
332
174
  if (timeSinceFailure < cooldownPeriod) {
333
- // Return rejected promise with the same error to prevent duplicate requests
334
- // This prevents React Query and other retry mechanisms from hammering the server
335
- // Re-throw the same error to maintain error consistency
336
175
  throw failedRequest.error;
337
176
  }
338
177
  else {
339
- // Clean up old failed request entry after cooldown expires
340
178
  this.failedRequests.delete(cacheKey);
341
179
  }
342
180
  }
343
- // Check cache for GET requests (after circuit breaker check)
181
+ // Check cache for GET requests
344
182
  if (cacheEnabled) {
345
183
  const cached = (0, data_client_cache_1.getCachedEntry)(this.cache, cacheKey, this.metrics);
346
- if (cached !== null) {
184
+ if (cached !== null)
347
185
  return cached;
348
- }
349
186
  }
350
- // CRITICAL: Check for duplicate concurrent requests BEFORE creating promise
351
- // This must be atomic to prevent race conditions when multiple requests arrive simultaneously
187
+ // Check for duplicate concurrent requests
352
188
  if (isGetRequest) {
353
189
  const pendingRequest = this.pendingRequests.get(cacheKey);
354
- if (pendingRequest) {
190
+ if (pendingRequest)
355
191
  return pendingRequest;
356
- }
357
192
  }
358
- // Create request promise wrapper that ensures atomic storage
359
- // This prevents race conditions where multiple requests check before any are stored
193
+ // Execute request
360
194
  let requestPromise;
361
195
  if (isGetRequest) {
362
- // For GET requests, create a promise that wraps the actual execution
363
- // Store it immediately to prevent race conditions
364
196
  requestPromise = (async () => {
365
197
  try {
366
198
  return await (0, data_client_request_1.executeHttpRequest)(method, fullUrl, endpoint, this.config, this.cache, cacheKey, cacheEnabled, startTime, this.misoClient, () => this.hasAnyToken(), () => this.getToken(), () => this.handleAuthError(), () => this.refreshUserToken(), this.interceptors, this.metrics, options);
367
199
  }
368
200
  finally {
369
- // Cleanup pending request when done (success or failure)
370
201
  this.pendingRequests.delete(cacheKey);
371
202
  }
372
203
  })();
373
- // Store immediately - this is atomic and prevents race conditions
374
- // If another request comes in now, it will see this promise and reuse it
375
204
  this.pendingRequests.set(cacheKey, requestPromise);
376
205
  }
377
206
  else {
378
- // For non-GET requests, execute directly (no deduplication)
379
207
  requestPromise = (0, data_client_request_1.executeHttpRequest)(method, fullUrl, endpoint, this.config, this.cache, cacheKey, cacheEnabled, startTime, this.misoClient, () => this.hasAnyToken(), () => this.getToken(), () => this.handleAuthError(), () => this.refreshUserToken(), this.interceptors, this.metrics, options);
380
208
  }
381
209
  try {
382
210
  const result = await requestPromise;
383
- // Clean up failed request entry on success (works for all methods)
384
211
  this.failedRequests.delete(cacheKey);
385
212
  return result;
386
213
  }
387
214
  catch (error) {
388
- // Store failed request to prevent rapid re-requests (works for all methods)
389
- // Circuit breaker pattern: track failure count for exponential backoff
390
215
  const existingFailure = this.failedRequests.get(cacheKey);
391
216
  const failureCount = existingFailure ? (existingFailure.count || 1) + 1 : 1;
392
- this.failedRequests.set(cacheKey, {
393
- timestamp: Date.now(),
394
- error: error,
395
- count: failureCount,
396
- });
397
- // Clean up after maximum cooldown period (30 seconds)
398
- // This prevents memory leaks while still providing protection
399
- setTimeout(() => {
400
- this.failedRequests.delete(cacheKey);
401
- }, 30000);
217
+ this.failedRequests.set(cacheKey, { timestamp: Date.now(), error: error, count: failureCount });
218
+ setTimeout(() => this.failedRequests.delete(cacheKey), 30000);
402
219
  throw error;
403
220
  }
404
221
  }
405
- /**
406
- * Refresh user token using onTokenRefresh callback
407
- * @returns New token and expiration, or null if refresh failed
408
- */
409
222
  async refreshUserToken() {
410
- if (!this.config.onTokenRefresh) {
223
+ if (!this.config.onTokenRefresh)
411
224
  return null;
412
- }
413
225
  try {
414
226
  const result = await this.config.onTokenRefresh();
415
- // Update token in localStorage
416
227
  if ((0, data_client_utils_1.isBrowser)() && result.token) {
417
228
  const { setLocalStorage } = await Promise.resolve().then(() => __importStar(require("./data-client-utils")));
418
- const tokenKeys = this.config.tokenKeys || ["token", "accessToken", "authToken"];
419
- // Store token in all configured keys
420
- for (const key of tokenKeys) {
229
+ for (const key of this.config.tokenKeys || ["token", "accessToken", "authToken"]) {
421
230
  setLocalStorage(key, result.token);
422
231
  }
423
- // Note: Refresh token is NOT stored in localStorage for security
424
- // The backend endpoint handles refresh token securely (httpOnly cookie or session)
425
232
  }
426
233
  return result;
427
234
  }
@@ -430,360 +237,114 @@ class DataClient {
430
237
  return null;
431
238
  }
432
239
  }
433
- /**
434
- * Handle authentication error
435
- */
436
240
  handleAuthError() {
437
241
  if ((0, data_client_utils_1.isBrowser)()) {
438
- // Fire and forget - redirect doesn't need to complete before throwing error
439
- this.redirectToLogin().catch((error) => {
440
- console.error("Failed to redirect to login:", error);
441
- });
242
+ this.redirectToLogin().catch((error) => console.error("Failed to redirect to login:", error));
442
243
  }
443
244
  }
444
- /**
445
- * Apply request interceptor
446
- */
447
245
  async applyRequestInterceptor(url, options) {
448
- if (this.interceptors.onRequest) {
449
- return await this.interceptors.onRequest(url, options);
450
- }
451
- return options;
246
+ return this.interceptors.onRequest ? await this.interceptors.onRequest(url, options) : options;
452
247
  }
453
248
  // ==================== HTTP METHODS ====================
454
- /**
455
- * GET request
456
- */
457
249
  async get(endpoint, options) {
458
- const finalOptions = await this.applyRequestInterceptor(endpoint, {
459
- ...options,
460
- method: "GET",
461
- });
250
+ const finalOptions = await this.applyRequestInterceptor(endpoint, { ...options, method: "GET" });
462
251
  return this.request("GET", endpoint, finalOptions);
463
252
  }
464
- /**
465
- * POST request
466
- */
467
253
  async post(endpoint, data, options) {
468
254
  const finalOptions = await this.applyRequestInterceptor(endpoint, {
469
- ...options,
470
- method: "POST",
471
- body: data ? JSON.stringify(data) : undefined,
472
- headers: {
473
- "Content-Type": "application/json",
474
- ...options?.headers,
475
- },
255
+ ...options, method: "POST", body: data ? JSON.stringify(data) : undefined,
256
+ headers: { "Content-Type": "application/json", ...options?.headers },
476
257
  });
477
258
  return this.request("POST", endpoint, finalOptions);
478
259
  }
479
- /**
480
- * PUT request
481
- */
482
260
  async put(endpoint, data, options) {
483
261
  const finalOptions = await this.applyRequestInterceptor(endpoint, {
484
- ...options,
485
- method: "PUT",
486
- body: data ? JSON.stringify(data) : undefined,
487
- headers: {
488
- "Content-Type": "application/json",
489
- ...options?.headers,
490
- },
262
+ ...options, method: "PUT", body: data ? JSON.stringify(data) : undefined,
263
+ headers: { "Content-Type": "application/json", ...options?.headers },
491
264
  });
492
265
  return this.request("PUT", endpoint, finalOptions);
493
266
  }
494
- /**
495
- * PATCH request (uses fetch fallback since MisoClient doesn't support PATCH)
496
- */
497
267
  async patch(endpoint, data, options) {
498
268
  const finalOptions = await this.applyRequestInterceptor(endpoint, {
499
- ...options,
500
- method: "PATCH",
501
- body: data ? JSON.stringify(data) : undefined,
502
- headers: {
503
- "Content-Type": "application/json",
504
- ...options?.headers,
505
- },
269
+ ...options, method: "PATCH", body: data ? JSON.stringify(data) : undefined,
270
+ headers: { "Content-Type": "application/json", ...options?.headers },
506
271
  });
507
272
  return this.request("PATCH", endpoint, finalOptions);
508
273
  }
509
- /**
510
- * DELETE request
511
- */
512
274
  async delete(endpoint, options) {
513
- const finalOptions = await this.applyRequestInterceptor(endpoint, {
514
- ...options,
515
- method: "DELETE",
516
- });
275
+ const finalOptions = await this.applyRequestInterceptor(endpoint, { ...options, method: "DELETE" });
517
276
  return this.request("DELETE", endpoint, finalOptions);
518
277
  }
519
- // ==================== AUTHORIZATION METHODS ====================
520
- /**
521
- * Get user permissions (uses token from localStorage if not provided)
522
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
523
- * @returns Array of permission strings
524
- */
278
+ // ==================== PERMISSION METHODS ====================
525
279
  async getPermissions(token) {
526
- if (!this.misoClient || !this.permissionService) {
527
- return [];
528
- }
529
- const userToken = token || this.getToken();
530
- if (!userToken) {
531
- return [];
532
- }
533
- return this.permissionService.getPermissions(userToken);
534
- }
535
- /**
536
- * Check if user has specific permission
537
- * @param permission - Permission to check
538
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
539
- * @returns True if user has the permission
540
- */
280
+ return permissionHelpers.getPermissions(this.permissionService, this.misoClient, () => this.getToken(), token);
281
+ }
541
282
  async hasPermission(permission, token) {
542
- if (!this.misoClient || !this.permissionService) {
543
- return false;
544
- }
545
- const userToken = token || this.getToken();
546
- if (!userToken) {
547
- return false;
548
- }
549
- return this.permissionService.hasPermission(userToken, permission);
550
- }
551
- /**
552
- * Check if user has any of the specified permissions
553
- * @param permissions - Permissions to check
554
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
555
- * @returns True if user has any of the permissions
556
- */
283
+ return permissionHelpers.hasPermission(this.permissionService, this.misoClient, () => this.getToken(), permission, token);
284
+ }
557
285
  async hasAnyPermission(permissions, token) {
558
- if (!this.misoClient || !this.permissionService) {
559
- return false;
560
- }
561
- const userToken = token || this.getToken();
562
- if (!userToken) {
563
- return false;
564
- }
565
- return this.permissionService.hasAnyPermission(userToken, permissions);
566
- }
567
- /**
568
- * Check if user has all of the specified permissions
569
- * @param permissions - Permissions to check
570
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
571
- * @returns True if user has all of the permissions
572
- */
286
+ return permissionHelpers.hasAnyPermission(this.permissionService, this.misoClient, () => this.getToken(), permissions, token);
287
+ }
573
288
  async hasAllPermissions(permissions, token) {
574
- if (!this.misoClient || !this.permissionService) {
575
- return false;
576
- }
577
- const userToken = token || this.getToken();
578
- if (!userToken) {
579
- return false;
580
- }
581
- return this.permissionService.hasAllPermissions(userToken, permissions);
289
+ return permissionHelpers.hasAllPermissions(this.permissionService, this.misoClient, () => this.getToken(), permissions, token);
582
290
  }
583
- /**
584
- * Force refresh permissions from controller (bypass cache)
585
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
586
- * @returns Array of permission strings
587
- */
588
291
  async refreshPermissions(token) {
589
- if (!this.misoClient || !this.permissionService) {
590
- return [];
591
- }
592
- const userToken = token || this.getToken();
593
- if (!userToken) {
594
- return [];
595
- }
596
- return this.permissionService.refreshPermissions(userToken);
292
+ return permissionHelpers.refreshPermissions(this.permissionService, this.misoClient, () => this.getToken(), token);
597
293
  }
598
- /**
599
- * Clear cached permissions for a user
600
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
601
- */
602
294
  async clearPermissionsCache(token) {
603
- if (!this.misoClient || !this.permissionService) {
604
- return;
605
- }
606
- const userToken = token || this.getToken();
607
- if (!userToken) {
608
- return;
609
- }
610
- return this.permissionService.clearPermissionsCache(userToken);
295
+ return permissionHelpers.clearPermissionsCache(this.permissionService, this.misoClient, () => this.getToken(), token);
611
296
  }
612
- /**
613
- * Get user roles (uses token from localStorage if not provided)
614
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
615
- * @returns Array of role strings
616
- */
297
+ // ==================== ROLE METHODS ====================
617
298
  async getRoles(token) {
618
- if (!this.misoClient || !this.roleService) {
619
- return [];
620
- }
621
- const userToken = token || this.getToken();
622
- if (!userToken) {
623
- return [];
624
- }
625
- return this.roleService.getRoles(userToken);
626
- }
627
- /**
628
- * Check if user has specific role
629
- * @param role - Role to check
630
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
631
- * @returns True if user has the role
632
- */
299
+ return roleHelpers.getRoles(this.roleService, this.misoClient, () => this.getToken(), token);
300
+ }
633
301
  async hasRole(role, token) {
634
- if (!this.misoClient || !this.roleService) {
635
- return false;
636
- }
637
- const userToken = token || this.getToken();
638
- if (!userToken) {
639
- return false;
640
- }
641
- return this.roleService.hasRole(userToken, role);
642
- }
643
- /**
644
- * Check if user has any of the specified roles
645
- * @param roles - Roles to check
646
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
647
- * @returns True if user has any of the roles
648
- */
302
+ return roleHelpers.hasRole(this.roleService, this.misoClient, () => this.getToken(), role, token);
303
+ }
649
304
  async hasAnyRole(roles, token) {
650
- if (!this.misoClient || !this.roleService) {
651
- return false;
652
- }
653
- const userToken = token || this.getToken();
654
- if (!userToken) {
655
- return false;
656
- }
657
- return this.roleService.hasAnyRole(userToken, roles);
658
- }
659
- /**
660
- * Check if user has all of the specified roles
661
- * @param roles - Roles to check
662
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
663
- * @returns True if user has all of the roles
664
- */
305
+ return roleHelpers.hasAnyRole(this.roleService, this.misoClient, () => this.getToken(), roles, token);
306
+ }
665
307
  async hasAllRoles(roles, token) {
666
- if (!this.misoClient || !this.roleService) {
667
- return false;
668
- }
669
- const userToken = token || this.getToken();
670
- if (!userToken) {
671
- return false;
672
- }
673
- return this.roleService.hasAllRoles(userToken, roles);
308
+ return roleHelpers.hasAllRoles(this.roleService, this.misoClient, () => this.getToken(), roles, token);
674
309
  }
675
- /**
676
- * Force refresh roles from controller (bypass cache)
677
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
678
- * @returns Array of role strings
679
- */
680
310
  async refreshRoles(token) {
681
- if (!this.misoClient || !this.roleService) {
682
- return [];
683
- }
684
- const userToken = token || this.getToken();
685
- if (!userToken) {
686
- return [];
687
- }
688
- return this.roleService.refreshRoles(userToken);
311
+ return roleHelpers.refreshRoles(this.roleService, this.misoClient, () => this.getToken(), token);
689
312
  }
690
- /**
691
- * Clear cached roles for a user
692
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
693
- */
694
313
  async clearRolesCache(token) {
695
- if (!this.misoClient || !this.roleService) {
696
- return;
697
- }
698
- const userToken = token || this.getToken();
699
- if (!userToken) {
700
- return;
701
- }
702
- return this.roleService.clearRolesCache(userToken);
314
+ return roleHelpers.clearRolesCache(this.roleService, this.misoClient, () => this.getToken(), token);
703
315
  }
704
316
  // ==================== AUTHENTICATION METHODS ====================
705
- /**
706
- * Validate token (uses localStorage token if not provided)
707
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
708
- * @returns True if token is valid
709
- */
710
317
  async validateToken(token) {
711
- if (!this.misoClient) {
318
+ if (!this.misoClient)
712
319
  return false;
713
- }
714
320
  const userToken = token || this.getToken();
715
- if (!userToken) {
716
- return false;
717
- }
718
- return this.misoClient.validateToken(userToken);
321
+ return userToken ? this.misoClient.validateToken(userToken) : false;
719
322
  }
720
- /**
721
- * Get user info from token (uses localStorage token if not provided)
722
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
723
- * @returns User info or null if not authenticated
724
- */
725
323
  async getUser(token) {
726
- if (!this.misoClient) {
324
+ if (!this.misoClient)
727
325
  return null;
728
- }
729
326
  const userToken = token || this.getToken();
730
- if (!userToken) {
731
- return null;
732
- }
733
- return this.misoClient.getUser(userToken);
327
+ return userToken ? this.misoClient.getUser(userToken) : null;
734
328
  }
735
- /**
736
- * Get user info from API endpoint (uses localStorage token if not provided)
737
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
738
- * @returns User info or null if not authenticated
739
- */
740
329
  async getUserInfo(token) {
741
- if (!this.misoClient) {
330
+ if (!this.misoClient)
742
331
  return null;
743
- }
744
332
  const userToken = token || this.getToken();
745
- if (!userToken) {
746
- return null;
747
- }
748
- return this.misoClient.getUserInfo(userToken);
333
+ return userToken ? this.misoClient.getUserInfo(userToken) : null;
749
334
  }
750
- /**
751
- * Check if authenticated (alias for validateToken)
752
- * @param token - Optional user authentication token (auto-retrieved from localStorage if not provided)
753
- * @returns True if authenticated
754
- */
755
335
  async isAuthenticatedAsync(token) {
756
336
  return this.validateToken(token);
757
337
  }
758
- /**
759
- * Get environment token (browser-side)
760
- * Checks localStorage cache first, then calls backend endpoint if needed
761
- * Uses clientTokenUri from config or defaults to /api/v1/auth/client-token
762
- *
763
- * @returns Client token string
764
- * @throws Error if token fetch fails
765
- */
766
338
  async getEnvironmentToken() {
767
339
  return (0, data_client_auth_1.getEnvironmentToken)(this.config, this.misoClient);
768
340
  }
769
- /**
770
- * Get client token information (browser-side)
771
- * Extracts application and environment info from client token
772
- *
773
- * @returns Client token info or null if token not available
774
- */
775
341
  getClientTokenInfo() {
776
342
  return (0, data_client_auth_1.getClientTokenInfo)(this.config.misoConfig);
777
343
  }
778
344
  }
779
345
  exports.DataClient = DataClient;
780
- /**
781
- * Singleton instance factory
782
- */
346
+ // ==================== SINGLETON FACTORY ====================
783
347
  let defaultDataClient = null;
784
- /**
785
- * Get or create default DataClient instance
786
- */
787
348
  function dataClient(config) {
788
349
  if (!defaultDataClient && config) {
789
350
  defaultDataClient = new DataClient(config);