@acontplus/ng-infrastructure 2.0.8 → 2.0.11

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