@acontplus/ng-infrastructure 2.0.10 → 2.0.12

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.
@@ -1,225 +1,178 @@
1
1
  import { HttpContextToken, HttpResponse, HttpContext, HttpClient } from '@angular/common/http';
2
2
  import * as i0 from '@angular/core';
3
3
  import { inject, Injectable, signal, computed, InjectionToken } from '@angular/core';
4
- import { retry, map, catchError, finalize } from 'rxjs/operators';
5
- import { throwError, catchError as catchError$1, switchMap, lastValueFrom } from 'rxjs';
4
+ import { retry, timer, switchMap, throwError, of, catchError, lastValueFrom } from 'rxjs';
6
5
  import { NotificationService } from '@acontplus/ng-notifications';
7
6
  import { AUTH_TOKEN, ENVIRONMENT, CORE_CONFIG, DEFAULT_CONFIG as DEFAULT_CONFIG$1 } from '@acontplus/ng-config';
8
7
  import { Router } from '@angular/router';
8
+ import { finalize, catchError as catchError$1 } from 'rxjs/operators';
9
9
  import { OverlayService } from '@acontplus/ng-components';
10
10
 
11
- // A token to use with HttpContext for skipping notifications on specific requests.
11
+ const RETRY_COUNT = 2;
12
+ // ---------------------------------------------------------------------------
13
+ // HTTP Context Tokens
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Skip all toast notifications for a specific request.
17
+ * Usage: new HttpContext().set(SKIP_NOTIFICATION, true)
18
+ */
12
19
  const SKIP_NOTIFICATION = new HttpContextToken(() => false);
13
- // A token to use with HttpContext for forcing notifications on specific requests (overrides exclusion patterns).
20
+ /**
21
+ * Force-show or force-hide notifications, overriding URL/method exclusion logic.
22
+ * Usage: new HttpContext().set(SHOW_NOTIFICATIONS, true)
23
+ */
14
24
  const SHOW_NOTIFICATIONS = new HttpContextToken(() => undefined);
25
+ // ---------------------------------------------------------------------------
26
+ // Interceptor
27
+ // ---------------------------------------------------------------------------
15
28
  const apiInterceptor = (req, next) => {
16
29
  const toastr = inject(NotificationService);
17
30
  const tokenProvider = inject(AUTH_TOKEN, { optional: true });
18
- // Add authorization header if token is available
19
- let modifiedReq = req;
31
+ // Attach Bearer token when available
20
32
  const token = tokenProvider?.getToken();
21
- if (token) {
22
- modifiedReq = req.clone({
23
- setHeaders: { Authorization: `Bearer ${token}` },
24
- });
25
- }
33
+ const modifiedReq = token ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : req;
26
34
  return next(modifiedReq).pipe(
27
- // Retries the request up to 2 times on failure with a 1-second delay.
28
- retry({ count: 2, delay: 1000 }),
29
- // Use the `map` operator to handle successful responses.
30
- map((event) => {
35
+ // Retry only on network errors or 5xx never on 4xx client errors.
36
+ // Shows toast + updates spinner message so the user knows what is happening.
37
+ retry({
38
+ count: RETRY_COUNT,
39
+ delay: (error, attempt) => {
40
+ const isRetryable = error.status === 0 || (error.status >= 500 && error.status < 600);
41
+ if (!isRetryable)
42
+ throw error; // 4xx — bubble up immediately, no retry
43
+ // Toast informa al usuario
44
+ toastr.warning({
45
+ message: `Reintentando solicitud (${attempt} de ${RETRY_COUNT})...`,
46
+ title: 'Conexión inestable',
47
+ config: { duration: 2500 },
48
+ });
49
+ return timer(1000 * attempt); // attempt 1 → 1 s, attempt 2 → 2 s
50
+ },
51
+ }),
52
+ // Handle successful responses via switchMap so we can return an Observable
53
+ // (needed to properly throw ApiResponse errors into the RxJS stream).
54
+ switchMap((event) => {
31
55
  if (event instanceof HttpResponse) {
32
- return handleSuccessResponse(event, toastr, req);
56
+ const standardized = standardizeApiResponse(event.body);
57
+ handleToastNotifications(standardized, toastr, req);
58
+ if (standardized.status === 'error') {
59
+ handleApiResponseError(standardized, toastr, req);
60
+ return throwError(() => standardized);
61
+ }
62
+ return of(transformResponseForConsumers(standardized, event));
33
63
  }
34
- return event;
64
+ return of(event);
35
65
  }),
36
- // Use the `catchError` operator to handle any errors that occur.
37
- catchError((error) => handleErrorResponse(error, toastr)));
66
+ // Handle HTTP-level errors show notification for critical errors, always re-throw.
67
+ catchError((error) => {
68
+ return handleHttpError(error, toastr);
69
+ }));
38
70
  };
39
- // --- Helper Functions ---
40
- /**
41
- * Handles successful HTTP responses, standardizes them, shows notifications, and transforms for consumers.
42
- */
43
- function handleSuccessResponse(event, notificationService, req) {
44
- const body = event.body;
45
- // Standardize the response to always have ApiResponse structure
46
- const standardizedResponse = standardizeApiResponse(body);
47
- // Handle toast notifications based on standardized response
48
- handleToastNotifications(standardizedResponse, notificationService, req);
49
- // Return the appropriate data based on response type
50
- return transformResponseForConsumers(standardizedResponse, event);
51
- }
52
- /**
53
- * Handles HTTP errors (from the interceptor chain, not backend ApiResponse).
54
- */
55
- function handleErrorResponse(error, notificationService) {
56
- const status = error.status;
57
- // Only show notifications for critical HTTP-level errors.
58
- // We avoid showing toasts for 4xx errors, which are handled by the component.
59
- if (status !== null && shouldShowCriticalErrorNotification(status)) {
60
- const message = getCriticalErrorMessage(error);
61
- const title = getErrorTitle(status);
62
- notificationService.error({
63
- message: message,
64
- title: title,
65
- config: { duration: 5000 },
66
- });
67
- }
68
- // Always re-throw the error so components/services can handle it.
69
- return throwError(() => error);
70
- }
71
- /**
72
- * Standardizes any response to follow the ApiResponse structure
73
- */
71
+ // ---------------------------------------------------------------------------
72
+ // Standardisation
73
+ // ---------------------------------------------------------------------------
74
74
  function standardizeApiResponse(body) {
75
- // If it's already a proper ApiResponse, return as is
76
- if (isValidApiResponse(body)) {
75
+ if (isValidApiResponse(body))
77
76
  return body;
77
+ // Raw object without ApiResponse wrapper
78
+ if (body !== null && body !== undefined && typeof body === 'object' && !('status' in body)) {
79
+ return wrapSuccess(body);
78
80
  }
79
- // If it's a raw data response (no wrapper), wrap it
80
- if (body && typeof body === 'object' && !('status' in body)) {
81
- return {
82
- status: 'success',
83
- code: '200',
84
- message: 'Operation completed successfully',
85
- data: body,
86
- timestamp: new Date().toISOString(),
87
- };
88
- }
89
- // If it's a primitive value, wrap it
81
+ // Primitive value
90
82
  if (body !== null && body !== undefined && typeof body !== 'object') {
91
- return {
92
- status: 'success',
93
- code: '200',
94
- message: 'Operation completed successfully',
95
- data: body,
96
- timestamp: new Date().toISOString(),
97
- };
83
+ return wrapSuccess(body);
98
84
  }
99
- // If it's null/undefined, create a success response without data
85
+ // null / undefined
86
+ return wrapSuccess(undefined);
87
+ }
88
+ function wrapSuccess(data) {
100
89
  return {
101
90
  status: 'success',
102
91
  code: '200',
103
92
  message: 'Operation completed successfully',
93
+ data,
104
94
  timestamp: new Date().toISOString(),
105
95
  };
106
96
  }
107
- /**
108
- * Handles toast notifications based on standardized response
109
- */
97
+ function isValidApiResponse(response) {
98
+ if (response === null || typeof response !== 'object')
99
+ return false;
100
+ const r = response;
101
+ return ('status' in r &&
102
+ 'code' in r &&
103
+ typeof r['status'] === 'string' &&
104
+ ['success', 'error', 'warning'].includes(r['status']));
105
+ }
106
+ // ---------------------------------------------------------------------------
107
+ // Notifications
108
+ // ---------------------------------------------------------------------------
110
109
  function handleToastNotifications(response, notificationService, req) {
111
- const shouldShowToast = shouldShowSuccessToast(req);
110
+ // SKIP_NOTIFICATION always wins — even over SHOW_NOTIFICATIONS
111
+ if (req.context.get(SKIP_NOTIFICATION))
112
+ return;
112
113
  const forceShow = req.context.get(SHOW_NOTIFICATIONS);
113
- const skipNotification = req.context.get(SKIP_NOTIFICATION);
114
- // Determine if we should show notifications, considering overrides
115
- const showNotifications = forceShow !== undefined ? forceShow : shouldShowToast;
116
- if (skipNotification)
114
+ const autoShow = shouldShowSuccessToast(req);
115
+ const showNotifications = forceShow !== undefined ? forceShow : autoShow;
116
+ if (!showNotifications)
117
117
  return;
118
- // Dynamic handling: Use show() for runtime type selection
119
- if (response.message &&
120
- showNotifications &&
121
- ['success', 'warning', 'error'].includes(response.status)) {
122
- notificationService.show({
123
- type: response.status,
124
- message: response.message,
125
- });
126
- }
127
- // Handle warnings separately if needed
128
- if (response.status === 'warning' && response.warnings && response.warnings.length > 0) {
129
- response.warnings.forEach((warning) => {
130
- notificationService.show({
131
- type: 'warning',
132
- message: warning.message,
133
- });
134
- });
118
+ if (!response.message)
119
+ return;
120
+ if (!['success', 'warning', 'error'].includes(response.status))
121
+ return;
122
+ // Primary notification — show main message once
123
+ notificationService.show({
124
+ type: response.status,
125
+ message: response.message,
126
+ });
127
+ // Secondary: show individual warnings only when no primary message covered them
128
+ if (response.status === 'warning' && response.warnings?.length && !response.message) {
129
+ response.warnings.forEach((w) => notificationService.show({ type: 'warning', message: w.message }));
135
130
  }
136
- // Handle errors separately if needed
137
- if (response.status === 'error' && response.errors && response.errors.length > 0) {
138
- response.errors.forEach((error) => {
139
- notificationService.show({
140
- type: 'error',
141
- message: error.message,
142
- });
143
- });
131
+ // Secondary: show individual errors only when no primary message covered them
132
+ if (response.status === 'error' && response.errors?.length && !response.message) {
133
+ response.errors.forEach((e) => notificationService.show({ type: 'error', message: e.message }));
144
134
  }
145
135
  }
146
- /**
147
- * Transforms the standardized response for consumers
148
- */
136
+ function handleApiResponseError(response, notificationService, req) {
137
+ if (req.context.get(SKIP_NOTIFICATION))
138
+ return;
139
+ const message = response.message || response.errors?.[0]?.message || 'An unexpected error occurred';
140
+ notificationService.error({ message, config: { duration: 5000 } });
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // Response transformation
144
+ // ---------------------------------------------------------------------------
149
145
  function transformResponseForConsumers(response, originalEvent) {
150
146
  switch (response.status) {
151
147
  case 'success':
152
- // For success responses, return the data if it exists, otherwise the full response
153
- if (response.data !== undefined && response.data !== null) {
154
- return originalEvent.clone({ body: response.data });
155
- }
156
- // For message-only success responses, return the full response
157
- return originalEvent.clone({ body: response });
158
148
  case 'warning':
159
- // For warnings, return data if it exists, otherwise the full response
160
- if (response.data !== undefined && response.data !== null) {
161
- return originalEvent.clone({ body: response.data });
162
- }
163
- return originalEvent.clone({ body: response });
149
+ // Unwrap data for consumers; fall back to full envelope when no data
150
+ return response.data !== undefined && response.data !== null
151
+ ? originalEvent.clone({ body: response.data })
152
+ : originalEvent.clone({ body: response });
164
153
  case 'error':
165
- // Errors should be thrown, not returned
166
- throw response;
154
+ // Already handled in switchMap this is a safety fallback
155
+ return originalEvent.clone({ body: response });
167
156
  default:
168
157
  return originalEvent.clone({ body: response });
169
158
  }
170
159
  }
171
- /**
172
- * Checks if a response is a valid ApiResponse
173
- */
174
- function isValidApiResponse(response) {
175
- if (response === null || typeof response !== 'object')
176
- return false;
177
- const r = response;
178
- return ('status' in r &&
179
- 'code' in r &&
180
- typeof r['status'] === 'string' &&
181
- ['success', 'error', 'warning'].includes(r['status']));
182
- }
183
- /**
184
- * Determines whether to show success toast notifications based on the request type.
185
- */
186
- function shouldShowSuccessToast(req) {
187
- const url = req.url?.toLowerCase() || '';
188
- const method = req.method?.toLowerCase() || '';
189
- // Never show for these cases
190
- const excludedPatterns = [
191
- 'get',
192
- '/list',
193
- '/search',
194
- '/query',
195
- '/page',
196
- '/paginated',
197
- '/health',
198
- '/status',
199
- '/ping',
200
- ];
201
- if (excludedPatterns.some((pattern) => method === pattern || url.includes(pattern))) {
202
- return false;
203
- }
204
- // Always show for these methods
205
- if (['post', 'put', 'patch', 'delete'].includes(method)) {
206
- return true;
160
+ // ---------------------------------------------------------------------------
161
+ // Error handling
162
+ // ---------------------------------------------------------------------------
163
+ function handleHttpError(error, notificationService) {
164
+ if (shouldShowCriticalErrorNotification(error.status)) {
165
+ notificationService.error({
166
+ message: getCriticalErrorMessage(error),
167
+ title: getErrorTitle(error.status),
168
+ config: { duration: 5000 },
169
+ });
207
170
  }
208
- // Default behavior
209
- return method !== 'get';
171
+ return throwError(() => error);
210
172
  }
211
- /**
212
- * Determines if we should show a notification for this HTTP error.
213
- */
214
173
  function shouldShowCriticalErrorNotification(status) {
215
- // Show notifications for:
216
- // - Network errors (status 0)
217
- // - Server errors (5xx)
218
174
  return status === 0 || (status >= 500 && status < 600);
219
175
  }
220
- /**
221
- * Gets the appropriate title for error notifications.
222
- */
223
176
  function getErrorTitle(status) {
224
177
  if (status === 0)
225
178
  return 'Connection Error';
@@ -227,17 +180,37 @@ function getErrorTitle(status) {
227
180
  return 'Server Error';
228
181
  return 'Error';
229
182
  }
230
- /**
231
- * Gets the appropriate message for critical errors.
232
- */
233
183
  function getCriticalErrorMessage(error) {
234
184
  if (error.status === 0) {
235
185
  return 'Unable to connect to the server. Please check your network connection.';
236
186
  }
237
187
  if (error.status >= 500) {
238
- return (error.error?.message || error.message || 'A server error occurred. Please try again later.');
239
- }
240
- return error.error?.message || error.message || 'An unexpected error occurred';
188
+ return (error.error?.message ?? error.message ?? 'A server error occurred. Please try again later.');
189
+ }
190
+ return error.error?.message ?? error.message ?? 'An unexpected error occurred';
191
+ }
192
+ // ---------------------------------------------------------------------------
193
+ // Utility: decide whether to auto-show toast
194
+ // ---------------------------------------------------------------------------
195
+ const MUTABLE_METHODS = new Set(['post', 'put', 'patch', 'delete']);
196
+ const EXCLUDED_URL_PATTERNS = [
197
+ '/list',
198
+ '/search',
199
+ '/query',
200
+ '/page',
201
+ '/paginated',
202
+ '/health',
203
+ '/status',
204
+ '/ping',
205
+ ];
206
+ function shouldShowSuccessToast(req) {
207
+ const method = req.method.toLowerCase();
208
+ const url = req.url.toLowerCase();
209
+ if (!MUTABLE_METHODS.has(method))
210
+ return false;
211
+ if (EXCLUDED_URL_PATTERNS.some((p) => url.includes(p)))
212
+ return false;
213
+ return true;
241
214
  }
242
215
 
243
216
  class LoggingService {
@@ -309,10 +282,10 @@ class LoggingService {
309
282
  // e.g., Sentry, LogRocket, etc.
310
283
  // This is a placeholder for production logging implementation
311
284
  }
312
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: LoggingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
313
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: LoggingService, providedIn: 'root' });
285
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LoggingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
286
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LoggingService, providedIn: 'root' });
314
287
  }
315
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: LoggingService, decorators: [{
288
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LoggingService, decorators: [{
316
289
  type: Injectable,
317
290
  args: [{
318
291
  providedIn: 'root',
@@ -358,10 +331,10 @@ class TenantInfo {
358
331
  localStorage.removeItem('tenantId');
359
332
  sessionStorage.removeItem('tenantId');
360
333
  }
361
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: TenantInfo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
362
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: TenantInfo, providedIn: 'root' });
334
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantInfo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
335
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantInfo, providedIn: 'root' });
363
336
  }
364
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: TenantInfo, decorators: [{
337
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantInfo, decorators: [{
365
338
  type: Injectable,
366
339
  args: [{
367
340
  providedIn: 'root',
@@ -393,21 +366,25 @@ class CorrelationInfo {
393
366
  getId() {
394
367
  return this.getOrCreateCorrelationId();
395
368
  }
396
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CorrelationInfo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
397
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CorrelationInfo, providedIn: 'root' });
369
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CorrelationInfo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
370
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CorrelationInfo, providedIn: 'root' });
398
371
  }
399
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CorrelationInfo, decorators: [{
372
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CorrelationInfo, decorators: [{
400
373
  type: Injectable,
401
374
  args: [{
402
375
  providedIn: 'root',
403
376
  }]
404
377
  }] });
405
378
 
406
- // HTTP Context tokens
379
+ // ---------------------------------------------------------------------------
380
+ // HTTP Context Tokens & helpers
381
+ // ---------------------------------------------------------------------------
382
+ /** Mark a request as using an absolute URL (skip base-URL injection). */
407
383
  const CUSTOM_URL = new HttpContextToken(() => false);
384
+ /** Skip all context headers (correlation, tenant, etc.) for this request. */
408
385
  const SKIP_CONTEXT_HEADERS = new HttpContextToken(() => false);
386
+ /** Merge additional headers into the request at call-site. */
409
387
  const CUSTOM_HEADERS = new HttpContextToken(() => ({}));
410
- // Helper functions for HTTP context
411
388
  function customUrl() {
412
389
  return new HttpContext().set(CUSTOM_URL, true);
413
390
  }
@@ -417,10 +394,9 @@ function skipContextHeaders() {
417
394
  function withCustomHeaders(headers) {
418
395
  return new HttpContext().set(CUSTOM_HEADERS, headers);
419
396
  }
420
- // Default configuration
421
397
  const DEFAULT_CONFIG = {
422
398
  enableCorrelationTracking: true,
423
- enableRequestLogging: false, // This will be overridden by environment.isProduction
399
+ enableRequestLogging: false,
424
400
  enableErrorLogging: true,
425
401
  customHeaders: {},
426
402
  clientVersion: '1.0.0',
@@ -429,100 +405,94 @@ const DEFAULT_CONFIG = {
429
405
  requestIdHeader: 'Request-Id',
430
406
  timestampHeader: 'Timestamp',
431
407
  excludeUrls: [],
408
+ skipAuthUrls: ['/login', '/register', '/refresh'],
432
409
  includeAuthToken: true,
433
410
  baseUrlInjection: true,
434
411
  refreshTokenCallback: undefined,
435
412
  logoutCallback: undefined,
436
413
  };
437
- // Injection token for configuration - FIXED
438
414
  const HTTP_CONTEXT_CONFIG = new InjectionToken('HTTP_CONTEXT_CONFIG', {
439
- factory: () => DEFAULT_CONFIG, // Provide a default factory in case it's not provided
415
+ factory: () => DEFAULT_CONFIG,
440
416
  });
417
+ // ---------------------------------------------------------------------------
418
+ // Interceptor
419
+ // ---------------------------------------------------------------------------
441
420
  const httpContextInterceptor = (req, next) => {
442
421
  const tokenProvider = inject(AUTH_TOKEN, { optional: true });
443
- console.log(tokenProvider);
444
422
  const router = inject(Router);
445
423
  const tenantService = inject(TenantInfo);
446
424
  const correlationService = inject(CorrelationInfo);
447
425
  const loggingService = inject(LoggingService);
448
426
  const environment = inject(ENVIRONMENT);
449
- // Get configuration (with fallback to default) - FIXED
450
427
  const config = {
451
428
  ...DEFAULT_CONFIG,
452
- // The default enableRequestLogging needs to be set based on environment here,
453
- // as it's dynamic, and DEFAULT_CONFIG is static.
454
429
  enableRequestLogging: !environment.isProduction,
455
430
  ...inject(HTTP_CONTEXT_CONFIG, { optional: true }),
456
431
  };
457
- // Check HTTP context tokens
432
+ // ── Context token flags ──────────────────────────────────────────────────
458
433
  const isCustomUrl = req.context.get(CUSTOM_URL);
459
434
  const skipHeaders = req.context.get(SKIP_CONTEXT_HEADERS);
460
435
  const contextHeaders = req.context.get(CUSTOM_HEADERS);
461
- // Skip processing if headers are disabled for this request
462
- if (skipHeaders) {
436
+ if (skipHeaders)
463
437
  return next(req);
464
- }
465
- // Check if URL should be excluded
466
- const shouldExclude = config.excludeUrls.some((url) => req.url.includes(url) || new RegExp(url).test(req.url));
467
- if (shouldExclude) {
438
+ // ── URL exclusion (with ReDoS protection) ────────────────────────────────
439
+ const shouldExclude = config.excludeUrls.some((pattern) => {
440
+ try {
441
+ return req.url.includes(pattern) || new RegExp(pattern).test(req.url);
442
+ }
443
+ catch {
444
+ return false; // malformed regex — skip safely
445
+ }
446
+ });
447
+ if (shouldExclude)
468
448
  return next(req);
469
- }
470
- // Skip auth for login, register, and refresh endpoints
471
- const skipAuth = req.url.includes('/login') || req.url.includes('/register') || req.url.includes('/refresh');
472
- // Handle URL transformation
449
+ // ── Auth skip check ──────────────────────────────────────────────────────
450
+ const skipAuth = config.skipAuthUrls.some((u) => req.url.includes(u));
451
+ // ── URL resolution ───────────────────────────────────────────────────────
473
452
  const baseUrl = environment.apiBaseUrl;
474
453
  const finalUrl = isCustomUrl || !config.baseUrlInjection ? req.url : `${baseUrl}${req.url}`;
475
- // Generate or get correlation context
454
+ // ── Header construction ──────────────────────────────────────────────────
476
455
  const correlationId = config.enableCorrelationTracking
477
456
  ? correlationService.getOrCreateCorrelationId()
478
457
  : crypto.randomUUID();
479
458
  const tenantId = tenantService.getTenantId();
480
459
  const requestId = crypto.randomUUID();
481
- // Build dynamic headers
482
- const headers = {};
483
- // Core context headers
484
- headers[config.requestIdHeader] = requestId;
460
+ const headers = {
461
+ [config.requestIdHeader]: requestId,
462
+ [config.timestampHeader]: new Date().toISOString(),
463
+ };
485
464
  if (config.enableCorrelationTracking) {
486
465
  headers[config.correlationIdHeader] = correlationId;
487
466
  }
488
467
  if (tenantId) {
489
468
  headers[config.tenantIdHeader] = tenantId;
490
469
  }
491
- headers[config.timestampHeader] = new Date().toISOString();
492
- // Client information
493
470
  if (config.clientVersion) {
494
471
  headers['Client-Version'] = config.clientVersion;
495
472
  }
496
- // Environment client ID
497
473
  if (environment.clientId) {
498
474
  headers['Client-Id'] = environment.clientId;
499
475
  }
500
- // Add authorization header if configured and available
501
476
  if (config.includeAuthToken && !skipAuth) {
502
477
  const authToken = tokenProvider?.getToken();
503
- if (authToken) {
478
+ if (authToken)
504
479
  headers['Authorization'] = `Bearer ${authToken}`;
505
- }
506
480
  }
507
- // Process custom headers from configuration (can be static values or dynamic functions)
481
+ // Static and dynamic custom headers from config
508
482
  Object.entries(config.customHeaders).forEach(([key, value]) => {
509
483
  headers[key] = typeof value === 'function' ? value() : value;
510
484
  });
511
- // Add headers from HTTP context
485
+ // Per-request headers from HttpContext
512
486
  Object.entries(contextHeaders).forEach(([key, value]) => {
513
487
  headers[key] = value;
514
488
  });
515
- // Add content-type for data requests if not already set
516
- if (['POST', 'PUT', 'PATCH'].includes(req.method.toUpperCase()) &&
517
- !req.headers.has('Content-Type')) {
489
+ // Content-Type for mutation requests (do not override if already set)
490
+ if (['POST', 'PUT', 'PATCH'].includes(req.method) && !req.headers.has('Content-Type')) {
518
491
  headers['Content-Type'] = 'application/json';
519
492
  }
520
- // Clone request with enhanced headers and URL
521
- const enhancedReq = req.clone({
522
- url: finalUrl,
523
- setHeaders: headers,
524
- });
525
- // Log outgoing request if enabled
493
+ // ── Clone request ────────────────────────────────────────────────────────
494
+ const enhancedReq = req.clone({ url: finalUrl, setHeaders: headers });
495
+ // ── Request logging ──────────────────────────────────────────────────────
526
496
  if (config.enableRequestLogging) {
527
497
  loggingService.logHttpRequest({
528
498
  method: req.method,
@@ -536,113 +506,107 @@ const httpContextInterceptor = (req, next) => {
536
506
  isCustomUrl,
537
507
  });
538
508
  }
539
- // If request has auth header, handle with refresh logic
540
- if (headers['Authorization'] && config.refreshTokenCallback) {
541
- return next(enhancedReq).pipe(catchError$1((error) => {
542
- // If 401 Unauthorized, try to refresh token
543
- if (error.status === 401) {
544
- const refreshToken = tokenProvider?.getRefreshToken?.();
545
- if (refreshToken && refreshToken.trim().length > 0) {
546
- return config.refreshTokenCallback().pipe(switchMap((newTokens) => {
547
- // Retry the original request with new token
548
- const retryReq = req.clone({
549
- url: finalUrl,
550
- setHeaders: { ...headers, Authorization: `Bearer ${newTokens.token}` },
551
- });
552
- return next(retryReq);
553
- }), catchError$1((refreshError) => {
554
- // Refresh failed, logout user
555
- config.logoutCallback?.();
556
- return throwError(() => refreshError);
557
- }));
558
- }
559
- }
560
- // Enhanced error logging with context
561
- if (config.enableErrorLogging) {
562
- loggingService.logHttpError({
563
- method: req.method,
564
- url: finalUrl,
565
- originalUrl: req.url,
566
- requestId,
567
- correlationId,
568
- tenantId,
569
- status: error.status,
570
- statusText: error.statusText,
571
- message: error.message,
572
- timestamp: new Date().toISOString(),
573
- errorDetails: error.error,
574
- environment: environment.clientId,
575
- isCustomUrl,
576
- headers: [], // Headers are not included in HttpErrorResponse by default, adjust if needed
577
- });
578
- }
579
- // Handle other error scenarios
580
- switch (error.status) {
581
- case 403:
582
- tenantService.handleForbidden();
583
- break;
584
- case 0:
585
- loggingService.logNetworkError(correlationId);
586
- break;
587
- case 429:
588
- loggingService.logRateLimitError(correlationId, finalUrl);
589
- break;
590
- }
509
+ // ── Execute request with unified error handling ──────────────────────────
510
+ const hasAuth = !!headers['Authorization'];
511
+ // Shared error context for both handlers below
512
+ const errorCtx = {
513
+ req,
514
+ finalUrl,
515
+ requestId,
516
+ correlationId,
517
+ tenantId,
518
+ isCustomUrl,
519
+ hasAuth,
520
+ config,
521
+ router,
522
+ loggingService,
523
+ tenantService,
524
+ tokenProvider,
525
+ environment,
526
+ };
527
+ // Base pipeline — always present
528
+ const base$ = next(enhancedReq).pipe(catchError((error) => handleContextError(error, errorCtx)));
529
+ // When auth + refresh callback are available, prepend a 401-refresh layer
530
+ if (!hasAuth || !config.refreshTokenCallback) {
531
+ return base$;
532
+ }
533
+ return next(enhancedReq).pipe(
534
+ // Intercept 401 first — attempt token refresh before general error handling
535
+ catchError((error) => {
536
+ if (error.status !== 401)
591
537
  return throwError(() => error);
592
- }));
593
- }
594
- else {
595
- // No auth header, use standard error handling
596
- return next(enhancedReq).pipe(catchError$1((error) => {
597
- // Enhanced error logging with context
598
- if (config.enableErrorLogging) {
599
- loggingService.logHttpError({
600
- method: req.method,
601
- url: finalUrl,
602
- originalUrl: req.url,
603
- requestId,
604
- correlationId,
605
- tenantId,
606
- status: error.status,
607
- statusText: error.statusText,
608
- message: error.message,
609
- timestamp: new Date().toISOString(),
610
- errorDetails: error.error,
611
- environment: environment.clientId,
612
- isCustomUrl,
613
- headers: [], // Headers are not included in HttpErrorResponse by default, adjust if needed
614
- });
615
- }
616
- // Handle specific error scenarios
617
- switch (error.status) {
618
- case 401: {
619
- loggingService.error('Unauthorized access - token expired or invalid');
620
- // Note: Token clearing should be handled by the auth service, not infrastructure
621
- router.navigate([`/${environment.loginRoute}`]);
622
- break;
623
- }
624
- case 403:
625
- tenantService.handleForbidden();
626
- break;
627
- case 0:
628
- loggingService.logNetworkError(correlationId);
629
- break;
630
- case 429:
631
- loggingService.logRateLimitError(correlationId, finalUrl);
632
- break;
633
- }
538
+ const refreshToken = tokenProvider?.getRefreshToken?.();
539
+ if (!refreshToken?.trim())
634
540
  return throwError(() => error);
541
+ return config.refreshTokenCallback().pipe(switchMap(({ token }) => next(req.clone({
542
+ url: finalUrl,
543
+ setHeaders: { ...headers, Authorization: `Bearer ${token}` },
544
+ }))), catchError((refreshError) => {
545
+ config.logoutCallback?.();
546
+ return throwError(() => refreshError);
635
547
  }));
636
- }
548
+ }),
549
+ // General error handling (logs, redirects, tenant, rate-limit, etc.)
550
+ catchError((error) => handleContextError(error, errorCtx)));
637
551
  };
638
- // Helper function to create configuration
552
+ function handleContextError(error, ctx) {
553
+ const { req, finalUrl, requestId, correlationId, tenantId, isCustomUrl, config, router, loggingService, tenantService, environment, } = ctx;
554
+ if (config.enableErrorLogging) {
555
+ loggingService.logHttpError({
556
+ method: req.method,
557
+ url: finalUrl,
558
+ originalUrl: req.url,
559
+ requestId,
560
+ correlationId,
561
+ tenantId,
562
+ status: error.status,
563
+ statusText: error.statusText,
564
+ message: error.message,
565
+ timestamp: new Date().toISOString(),
566
+ errorDetails: error.error,
567
+ environment: environment.clientId,
568
+ isCustomUrl,
569
+ headers: [],
570
+ });
571
+ }
572
+ switch (error.status) {
573
+ case 401:
574
+ loggingService.error('Unauthorized — redirecting to login');
575
+ // Let auth service clear tokens before navigation
576
+ config.logoutCallback?.();
577
+ router.navigate([`/${environment.loginRoute}`]);
578
+ break;
579
+ case 403:
580
+ tenantService.handleForbidden();
581
+ break;
582
+ case 0:
583
+ loggingService.logNetworkError(correlationId);
584
+ break;
585
+ case 429:
586
+ loggingService.logRateLimitError(correlationId, finalUrl);
587
+ break;
588
+ }
589
+ return throwError(() => error);
590
+ }
591
+ // ---------------------------------------------------------------------------
592
+ // Public helpers for library consumers
593
+ // ---------------------------------------------------------------------------
639
594
  function createHttpContextConfig(overrides = {}) {
640
- return {
641
- ...DEFAULT_CONFIG,
642
- ...overrides,
643
- };
595
+ return { ...DEFAULT_CONFIG, ...overrides };
644
596
  }
645
- // Provider function for easy setup
597
+ /**
598
+ * Register the interceptor configuration in your app providers.
599
+ *
600
+ * @example
601
+ * // app.config.ts
602
+ * provideHttpClient(
603
+ * withInterceptors([
604
+ * httpContextInterceptor, // 1st — adds headers, handles 401 refresh
605
+ * apiInterceptor, // 2nd — normalises response, shows toasts
606
+ * ])
607
+ * ),
608
+ * ...provideHttpContext({ clientVersion: '2.0.0' }),
609
+ */
646
610
  function provideHttpContext(config = {}) {
647
611
  return [
648
612
  {
@@ -652,65 +616,50 @@ function provideHttpContext(config = {}) {
652
616
  ];
653
617
  }
654
618
 
655
- /**
656
- * Token to determine if a request should show spinner
657
- * Default is true (show spinner for all requests)
658
- */
659
619
  const SHOW_SPINNER = new HttpContextToken(() => true);
660
- const requests = [];
661
- /**
662
- * Helper function to disable spinner for specific requests
663
- * @returns HttpContext with spinner disabled
664
- */
665
620
  function withoutSpinner() {
666
621
  return new HttpContext().set(SHOW_SPINNER, false);
667
622
  }
668
- /**
669
- * Service to track active HTTP requests
670
- */
671
623
  class ActiveRequestsTracker {
624
+ requests = [];
672
625
  get count() {
673
- return requests.length;
626
+ return this.requests.length;
674
627
  }
628
+ /** Retorna true si se pasa de 0 → 1 (primera request activa). */
675
629
  add(request) {
676
- requests.push(request);
630
+ this.requests.push(request);
631
+ return this.requests.length === 1;
677
632
  }
633
+ /** Retorna true si se pasa de 1 → 0 (última request completada). */
678
634
  remove(request) {
679
- const index = requests.indexOf(request);
635
+ const index = this.requests.indexOf(request);
680
636
  if (index >= 0) {
681
- requests.splice(index, 1);
637
+ this.requests.splice(index, 1);
682
638
  }
639
+ return this.requests.length === 0;
683
640
  }
684
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ActiveRequestsTracker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
685
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ActiveRequestsTracker, providedIn: 'root' });
641
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ActiveRequestsTracker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
642
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ActiveRequestsTracker, providedIn: 'root' });
686
643
  }
687
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ActiveRequestsTracker, decorators: [{
644
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ActiveRequestsTracker, decorators: [{
688
645
  type: Injectable,
689
- args: [{
690
- providedIn: 'root',
691
- }]
646
+ args: [{ providedIn: 'root' }]
692
647
  }] });
693
- /**
694
- * Interceptor that shows/hides a loading spinner based on active HTTP requests
695
- */
696
648
  const spinnerInterceptor = (req, next) => {
697
- // Track active requests requiring spinner
698
649
  const activeRequests = inject(ActiveRequestsTracker);
699
650
  const overlayService = inject(OverlayService);
700
- // Skip spinner if disabled for this request
701
651
  if (!req.context.get(SHOW_SPINNER)) {
702
652
  return next(req);
703
653
  }
704
- // Add request to tracking
705
- activeRequests.add(req);
706
- // Show spinner if this is the first active request
707
- if (activeRequests.count === 1) {
654
+ // showSpinner solo cuando pasamos de 0 → 1 requests
655
+ const isFirst = activeRequests.add(req);
656
+ if (isFirst) {
708
657
  overlayService.showSpinner();
709
658
  }
710
659
  return next(req).pipe(finalize(() => {
711
- // Remove request and hide spinner if no more active requests
712
- activeRequests.remove(req);
713
- if (activeRequests.count === 0) {
660
+ // hideSpinner solo cuando pasamos de 1 0 requests
661
+ const isEmpty = activeRequests.remove(req);
662
+ if (isEmpty) {
714
663
  overlayService.hideSpinner();
715
664
  }
716
665
  }));
@@ -739,10 +688,10 @@ class BaseHttpRepository {
739
688
  delete(path = '') {
740
689
  return this.http.delete(this.buildUrl(path));
741
690
  }
742
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: BaseHttpRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
743
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: BaseHttpRepository });
691
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BaseHttpRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
692
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BaseHttpRepository });
744
693
  }
745
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: BaseHttpRepository, decorators: [{
694
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BaseHttpRepository, decorators: [{
746
695
  type: Injectable
747
696
  }] });
748
697
 
@@ -786,10 +735,10 @@ class GenericRepository extends BaseHttpRepository {
786
735
  }
787
736
  return params;
788
737
  }
789
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: GenericRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
790
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: GenericRepository });
738
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GenericRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
739
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GenericRepository });
791
740
  }
792
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: GenericRepository, decorators: [{
741
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GenericRepository, decorators: [{
793
742
  type: Injectable
794
743
  }], ctorParameters: () => [] });
795
744
  class SearchableGenericRepository extends GenericRepository {
@@ -798,10 +747,10 @@ class SearchableGenericRepository extends GenericRepository {
798
747
  const params = this.buildParams(pagination, searchFilters);
799
748
  return this.get('search', params);
800
749
  }
801
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: SearchableGenericRepository, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
802
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: SearchableGenericRepository });
750
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SearchableGenericRepository, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
751
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SearchableGenericRepository });
803
752
  }
804
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: SearchableGenericRepository, decorators: [{
753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SearchableGenericRepository, decorators: [{
805
754
  type: Injectable
806
755
  }] });
807
756
 
@@ -844,10 +793,10 @@ class RepositoryFactory {
844
793
  remove: (id) => this.http.delete(buildUrl(idToString(id))),
845
794
  };
846
795
  }
847
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: RepositoryFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
848
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: RepositoryFactory, providedIn: 'root' });
796
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RepositoryFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
797
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RepositoryFactory, providedIn: 'root' });
849
798
  }
850
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: RepositoryFactory, decorators: [{
799
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RepositoryFactory, decorators: [{
851
800
  type: Injectable,
852
801
  args: [{ providedIn: 'root' }]
853
802
  }] });
@@ -979,10 +928,10 @@ class CoreConfigService {
979
928
  resetConfig() {
980
929
  this.config = this.initializeConfig();
981
930
  }
982
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CoreConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
983
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CoreConfigService, providedIn: 'root' });
931
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoreConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
932
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoreConfigService, providedIn: 'root' });
984
933
  }
985
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: CoreConfigService, decorators: [{
934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoreConfigService, decorators: [{
986
935
  type: Injectable,
987
936
  args: [{
988
937
  providedIn: 'root',
@@ -1025,7 +974,7 @@ class Command extends BaseUseCase {
1025
974
  timestamp: new Date().toISOString(),
1026
975
  }));
1027
976
  }
1028
- return this.executeInternal(request).pipe(catchError((error) => {
977
+ return this.executeInternal(request).pipe(catchError$1((error) => {
1029
978
  // Log the error for debugging
1030
979
  const logger = inject(LoggingService);
1031
980
  logger.error('An error occurred during command execution:', error);
@@ -1037,7 +986,7 @@ class Command extends BaseUseCase {
1037
986
 
1038
987
  class Query extends BaseUseCase {
1039
988
  execute(request) {
1040
- return this.executeInternal(request).pipe(catchError((error) => {
989
+ return this.executeInternal(request).pipe(catchError$1((error) => {
1041
990
  const logger = inject(LoggingService);
1042
991
  logger.error('An error occurred during query execution:', error);
1043
992
  return throwError(() => error);
@@ -1049,5 +998,5 @@ class Query extends BaseUseCase {
1049
998
  * Generated bundle index. Do not edit.
1050
999
  */
1051
1000
 
1052
- export { ActiveRequestsTracker, AngularHttpAdapter, BaseHttpRepository, BaseUseCase, Command, CoreConfigService, CorrelationInfo, GenericRepository, HTTP_CONTEXT_CONFIG, LoggingService, Query, REPOSITORY_CONFIG, RepositoryFactory, SHOW_NOTIFICATIONS, SKIP_NOTIFICATION, SearchableGenericRepository, apiInterceptor, createCoreConfig, createHttpContextConfig, customUrl, httpContextInterceptor, provideCoreConfig, provideHttpContext, skipContextHeaders, spinnerInterceptor, withCustomHeaders, withoutSpinner };
1001
+ export { ActiveRequestsTracker, AngularHttpAdapter, BaseHttpRepository, BaseUseCase, Command, CoreConfigService, CorrelationInfo, GenericRepository, HTTP_CONTEXT_CONFIG, LoggingService, Query, REPOSITORY_CONFIG, RepositoryFactory, SHOW_NOTIFICATIONS, SHOW_SPINNER, SKIP_NOTIFICATION, SearchableGenericRepository, apiInterceptor, createCoreConfig, createHttpContextConfig, customUrl, httpContextInterceptor, provideCoreConfig, provideHttpContext, skipContextHeaders, spinnerInterceptor, withCustomHeaders, withoutSpinner };
1053
1002
  //# sourceMappingURL=acontplus-ng-infrastructure.mjs.map