@arsedizioni/ars-utils 22.0.53 → 22.0.55

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,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, inject, Service, DestroyRef, Injector, Injectable } from '@angular/core';
3
- import { HttpClient, HttpHeaders, HttpRequest, HttpErrorResponse } from '@angular/common/http';
2
+ import { signal, computed, inject, Service, DestroyRef, Injector } from '@angular/core';
3
+ import { HttpErrorResponse, HttpClient, HttpHeaders, HttpRequest } from '@angular/common/http';
4
4
  import { BroadcastService, SystemUtils, SplashService } from '@arsedizioni/ars-utils/core';
5
- import { throwError, of, EMPTY, catchError as catchError$1 } from 'rxjs';
6
- import { catchError, map, finalize } from 'rxjs/operators';
5
+ import { catchError, throwError, of, EMPTY } from 'rxjs';
6
+ import { catchError as catchError$1, map, finalize } from 'rxjs/operators';
7
7
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
8
 
9
9
  const ClipperMessages = {
@@ -2191,6 +2191,93 @@ const NotesColors = [{ name: "Verde", value: "#2E8B74" },
2191
2191
  { name: "Indaco", value: "#5B6EBF" }
2192
2192
  ];
2193
2193
 
2194
+ /** Minimum milliseconds between consecutive error broadcasts (debounce guard). */
2195
+ const ERROR_DEBOUNCE_MS = 5000;
2196
+ /**
2197
+ * Builds the Clipper auth HTTP interceptor.
2198
+ *
2199
+ * Takes its two inputs — the Clipper base URI and the service flags — directly
2200
+ * as arguments (captured in the closure), so it never injects ClipperService:
2201
+ * registering it does not pull the full Clipper service into the eager bundle.
2202
+ *
2203
+ * Register it with `withInterceptors`:
2204
+ * @example
2205
+ * provideHttpClient(withInterceptors([
2206
+ * clipperAuthInterceptor(environment.serviceUri, ClipperServiceFlags.NotifySystemErrors),
2207
+ * ]))
2208
+ *
2209
+ * @param serviceUri - Base URI of the Clipper service; only requests starting with it are handled.
2210
+ * @param flags - Service flags (e.g. NotifySystemErrors). Defaults to None.
2211
+ * @returns An `HttpInterceptorFn` ready to register.
2212
+ */
2213
+ function clipperAuthInterceptor(serviceUri, flags = ClipperServiceFlags.None) {
2214
+ // Debounce state, persisted across requests via the factory closure.
2215
+ let lastErrorTime = -1;
2216
+ /**
2217
+ * Broadcasts a user-friendly message for a Clipper HTTP error, debounced.
2218
+ * @param error - The raw error value thrown by the HTTP layer.
2219
+ * @param broadcastService - The broadcast service for the current request.
2220
+ * @returns void
2221
+ */
2222
+ function handleError(error, broadcastService) {
2223
+ if (!(error instanceof HttpErrorResponse))
2224
+ return;
2225
+ // Reject only errors that explicitly belong to a different service; a missing
2226
+ // url (network failure on a Clipper request) is allowed through.
2227
+ if (error.url && !error.url.startsWith(serviceUri))
2228
+ return;
2229
+ const errorStatus = error.status;
2230
+ const shouldNotify = errorStatus === 0 ||
2231
+ (errorStatus > 0 && errorStatus < 500) ||
2232
+ (flags & ClipperServiceFlags.NotifySystemErrors) > 0;
2233
+ if (!shouldNotify)
2234
+ return;
2235
+ const now = Date.now();
2236
+ if (now - lastErrorTime <= ERROR_DEBOUNCE_MS)
2237
+ return;
2238
+ lastErrorTime = now;
2239
+ let message;
2240
+ switch (errorStatus) {
2241
+ case 0:
2242
+ message = "In questo momento Clipper non è disponibile. Riprova tra qualche minuto.";
2243
+ break;
2244
+ case 403:
2245
+ message = "Non hai i permessi necessari per eseguire l'operazione richiesta.";
2246
+ break;
2247
+ default:
2248
+ message = (error.error?.['message'] ??
2249
+ error.message ??
2250
+ "Impossibile eseguire l'operazione richiesta.").replaceAll('\r\n', '</p><p>');
2251
+ break;
2252
+ }
2253
+ broadcastService.sendMessage(ClipperMessages.ERROR, {
2254
+ invalidateSession: errorStatus === 405 || errorStatus === 410,
2255
+ message,
2256
+ title: "Errore in Clipper",
2257
+ errorStatus,
2258
+ service: serviceUri
2259
+ });
2260
+ }
2261
+ // The interceptor itself. Runs in an injection context, so `inject()` works.
2262
+ return (request, next) => {
2263
+ if (!request.url.startsWith(serviceUri)) {
2264
+ return next(request);
2265
+ }
2266
+ const broadcastService = inject(BroadcastService);
2267
+ const authenticatedRequest = request.clone({
2268
+ withCredentials: true,
2269
+ setHeaders: {
2270
+ 'ngsw-bypass': 'ngsw-bypass',
2271
+ 'X-Client-Id': sessionStorage.getItem('clipper_client_id') ?? ''
2272
+ }
2273
+ });
2274
+ return next(authenticatedRequest).pipe(catchError((error) => {
2275
+ handleError(error, broadcastService);
2276
+ return throwError(() => error);
2277
+ }));
2278
+ };
2279
+ }
2280
+
2194
2281
  /**
2195
2282
  * Document search, references, export and metadata, plus the dashboard counters,
2196
2283
  * taxonomy/topics/tags lookups, the working-documents "bag" and saved searches.
@@ -2625,7 +2712,7 @@ class ClipperLoginService {
2625
2712
  : new HttpHeaders()
2626
2713
  .set("Authorization", 'Bearer ' + oauthAccessToken)
2627
2714
  })
2628
- .pipe(catchError(err => {
2715
+ .pipe(catchError$1(err => {
2629
2716
  return throwError(() => err);
2630
2717
  }), map((r) => {
2631
2718
  if (r.success) {
@@ -2667,7 +2754,7 @@ class ClipperLoginService {
2667
2754
  confirmIdentity(code) {
2668
2755
  return this.httpClient
2669
2756
  .post(this.core.serviceUri + '/login/confirm/' + code, {})
2670
- .pipe(catchError((err) => {
2757
+ .pipe(catchError$1((err) => {
2671
2758
  return throwError(() => err);
2672
2759
  }), map((r) => {
2673
2760
  if (r.success) {
@@ -2687,7 +2774,7 @@ class ClipperLoginService {
2687
2774
  this.core.clear(true);
2688
2775
  // Clean up
2689
2776
  localStorage.removeItem('clipper_context');
2690
- }), catchError((_e) => {
2777
+ }), catchError$1((_e) => {
2691
2778
  return of([]);
2692
2779
  }));
2693
2780
  }
@@ -2993,7 +3080,7 @@ class ClipperCoreService {
2993
3080
  */
2994
3081
  ping() {
2995
3082
  this.httpClient.get(this._serviceUri + '/ping?nocache=' + SystemUtils.generateUUID())
2996
- .pipe(catchError(() => { return EMPTY; }))
3083
+ .pipe(catchError$1(() => { return EMPTY; }))
2997
3084
  .subscribe();
2998
3085
  }
2999
3086
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ClipperCoreService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
@@ -3687,99 +3774,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
3687
3774
  type: Service
3688
3775
  }] });
3689
3776
 
3690
- /** Minimum milliseconds between consecutive error broadcasts (debounce guard). */
3691
- const ERROR_DEBOUNCE_MS = 5000;
3692
- /**
3693
- * HTTP interceptor that attaches Clipper authentication headers to every request
3694
- * targeting the Clipper service, and broadcasts user-friendly error messages
3695
- * when the server returns an error response.
3696
- */
3697
- class ClipperAuthInterceptor {
3698
- constructor() {
3699
- this.clipperService = inject(ClipperService);
3700
- this.broadcastService = inject(BroadcastService);
3701
- this.lastErrorTime = -1;
3702
- }
3703
- /**
3704
- * Intercepts every outgoing HTTP request.
3705
- * When the request targets the Clipper service URI, attaches credentials and
3706
- * a client-ID header, then pipes the response through an error handler.
3707
- * @param request - The outgoing HTTP request.
3708
- * @param next - The next handler in the interceptor chain.
3709
- * @returns An observable of the HTTP event stream.
3710
- */
3711
- intercept(request, next) {
3712
- if (!request.url.startsWith(this.clipperService.serviceUri)) {
3713
- return next.handle(request);
3714
- }
3715
- const authenticatedRequest = request.clone({
3716
- withCredentials: true,
3717
- setHeaders: {
3718
- 'ngsw-bypass': 'ngsw-bypass',
3719
- 'X-Client-Id': sessionStorage.getItem('clipper_client_id') ?? ''
3720
- }
3721
- });
3722
- return next.handle(authenticatedRequest).pipe(catchError$1((error) => {
3723
- this.handleError(error);
3724
- return throwError(() => error);
3725
- }));
3726
- }
3727
- /**
3728
- * Processes an HTTP error, broadcasting a user-friendly message when appropriate.
3729
- * Errors are debounced: only one message is sent per `ERROR_DEBOUNCE_MS` window.
3730
- *
3731
- * This handler only ever runs for requests already targeting the Clipper service
3732
- * (see `intercept`). A transport-level failure produces an `HttpErrorResponse`
3733
- * with status `0` and a `null` url, so the url is only validated when present —
3734
- * that way genuine network failures still surface the "service unavailable" message.
3735
- * @param error - The raw error value thrown by the HTTP layer.
3736
- */
3737
- handleError(error) {
3738
- if (!(error instanceof HttpErrorResponse))
3739
- return;
3740
- // Reject only errors that explicitly belong to a different service; a missing
3741
- // url (network failure on a Clipper request) is allowed through.
3742
- if (error.url && !error.url.startsWith(this.clipperService.serviceUri))
3743
- return;
3744
- const errorStatus = error.status;
3745
- const shouldNotify = errorStatus === 0 ||
3746
- (errorStatus > 0 && errorStatus < 500) ||
3747
- (this.clipperService.flags & ClipperServiceFlags.NotifySystemErrors) > 0;
3748
- if (!shouldNotify)
3749
- return;
3750
- const now = Date.now();
3751
- if (now - this.lastErrorTime <= ERROR_DEBOUNCE_MS)
3752
- return;
3753
- this.lastErrorTime = now;
3754
- let message;
3755
- switch (errorStatus) {
3756
- case 0:
3757
- message = "In questo momento Clipper non è disponibile. Riprova tra qualche minuto.";
3758
- break;
3759
- case 403:
3760
- message = "Non hai i permessi necessari per eseguire l'operazione richiesta.";
3761
- break;
3762
- default:
3763
- message = (error.error?.['message'] ??
3764
- error.message ??
3765
- "Impossibile eseguire l'operazione richiesta.").replaceAll('\r\n', '</p><p>');
3766
- break;
3767
- }
3768
- this.broadcastService.sendMessage(ClipperMessages.ERROR, {
3769
- invalidateSession: errorStatus === 405 || errorStatus === 410,
3770
- message,
3771
- title: "Errore in Clipper",
3772
- errorStatus,
3773
- service: this.clipperService.serviceUri
3774
- });
3775
- }
3776
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ClipperAuthInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3777
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ClipperAuthInterceptor }); }
3778
- }
3779
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ClipperAuthInterceptor, decorators: [{
3780
- type: Injectable
3781
- }] });
3782
-
3783
3777
  // Public API surface for the Clipper services.
3784
3778
  //
3785
3779
  // `ClipperService` is the facade/barrel: inject it and reach a feature via its
@@ -3795,5 +3789,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
3795
3789
  * Generated bundle index. Do not edit.
3796
3790
  */
3797
3791
 
3798
- export { ClipperAccountService, ClipperArchiveCopyMode, ClipperArchiveFileStorageType, ClipperArchiveFileStorageTypes, ClipperArchiveFileType, ClipperArchiveFileTypes, ClipperArchiveFilesSearchParams, ClipperArchiveFoldersSearchParams, ClipperArchiveService, ClipperAuthInterceptor, ClipperAuthors, ClipperCalendarCopyMode, ClipperCalendarSearchParams, ClipperCalendarService, ClipperCalendarState, ClipperCalendarStates, ClipperChannel, ClipperChannelSettings, ClipperChannels, ClipperCollaborationService, ClipperCoreService, ClipperDashboard, ClipperDocumentChangeReasons, ClipperDocumentContainer, ClipperDocumentsService, ClipperExportDocumentsFormat, ClipperFacet, ClipperLoginService, ClipperMessages, ClipperModel, ClipperModels, ClipperModule, ClipperModuleGroup, ClipperModuleGroups, ClipperModules, ClipperQueryDocumentFlags, ClipperQueryReferencesMode, ClipperRecurrenceType, ClipperRecurrenceTypes, ClipperRegions, ClipperSearchCalendarSnapshotResult, ClipperSearchFacetsSnapshot, ClipperSearchParams, ClipperSearchResult, ClipperSearchUtils, ClipperSectorTypes, ClipperSectors, ClipperSelectionMode, ClipperService, ClipperServiceFlags, ClipperSort, ClipperSources, ClipperTeamInfo, ClipperTeamProduct, ClipperTeamProductPermission, ClipperUpdateChannelsStateParams, ClipperUtils, NotesColors };
3792
+ export { ClipperAccountService, ClipperArchiveCopyMode, ClipperArchiveFileStorageType, ClipperArchiveFileStorageTypes, ClipperArchiveFileType, ClipperArchiveFileTypes, ClipperArchiveFilesSearchParams, ClipperArchiveFoldersSearchParams, ClipperArchiveService, ClipperAuthors, ClipperCalendarCopyMode, ClipperCalendarSearchParams, ClipperCalendarService, ClipperCalendarState, ClipperCalendarStates, ClipperChannel, ClipperChannelSettings, ClipperChannels, ClipperCollaborationService, ClipperCoreService, ClipperDashboard, ClipperDocumentChangeReasons, ClipperDocumentContainer, ClipperDocumentsService, ClipperExportDocumentsFormat, ClipperFacet, ClipperLoginService, ClipperMessages, ClipperModel, ClipperModels, ClipperModule, ClipperModuleGroup, ClipperModuleGroups, ClipperModules, ClipperQueryDocumentFlags, ClipperQueryReferencesMode, ClipperRecurrenceType, ClipperRecurrenceTypes, ClipperRegions, ClipperSearchCalendarSnapshotResult, ClipperSearchFacetsSnapshot, ClipperSearchParams, ClipperSearchResult, ClipperSearchUtils, ClipperSectorTypes, ClipperSectors, ClipperSelectionMode, ClipperService, ClipperServiceFlags, ClipperSort, ClipperSources, ClipperTeamInfo, ClipperTeamProduct, ClipperTeamProductPermission, ClipperUpdateChannelsStateParams, ClipperUtils, NotesColors, clipperAuthInterceptor };
3799
3793
  //# sourceMappingURL=arsedizioni-ars-utils-clipper.common.mjs.map