@arsedizioni/ars-utils 22.5.51 → 22.5.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs +15 -15
  2. package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs +43 -43
  3. package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs.map +1 -1
  4. package/fesm2022/arsedizioni-ars-utils-core.date.mjs +9 -9
  5. package/fesm2022/arsedizioni-ars-utils-core.markdown.mjs +3 -3
  6. package/fesm2022/arsedizioni-ars-utils-core.mjs +48 -48
  7. package/fesm2022/arsedizioni-ars-utils-core.validators.mjs +42 -42
  8. package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs +94 -24
  9. package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs.map +1 -1
  10. package/fesm2022/arsedizioni-ars-utils-support.common.mjs +18 -9
  11. package/fesm2022/arsedizioni-ars-utils-support.common.mjs.map +1 -1
  12. package/fesm2022/arsedizioni-ars-utils-ui.controls.date.mjs +6 -6
  13. package/fesm2022/arsedizioni-ars-utils-ui.controls.mjs +9 -9
  14. package/fesm2022/arsedizioni-ars-utils-ui.controls.tree.mjs +3 -3
  15. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.auth.mjs +18 -18
  16. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.mjs +12 -12
  17. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.prompt.mjs +9 -9
  18. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.select.mjs +15 -15
  19. package/fesm2022/arsedizioni-ars-utils-ui.files.mjs +12 -12
  20. package/fesm2022/arsedizioni-ars-utils-ui.filters.mjs +3 -3
  21. package/fesm2022/arsedizioni-ars-utils-ui.help.mjs +9 -9
  22. package/fesm2022/arsedizioni-ars-utils-ui.mjs +61 -61
  23. package/fesm2022/arsedizioni-ars-utils-ui.navigation.mjs +3 -3
  24. package/fesm2022/arsedizioni-ars-utils-ui.notifications.mjs +6 -6
  25. package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs +6 -6
  26. package/fesm2022/arsedizioni-ars-utils-ui.paginator.mjs +3 -3
  27. package/fesm2022/arsedizioni-ars-utils-ui.shell.mjs +9 -9
  28. package/fesm2022/arsedizioni-ars-utils-ui.tinymce.mjs +9 -9
  29. package/package.json +1 -1
  30. package/types/arsedizioni-ars-utils-clipper.common.d.ts +5 -5
  31. package/types/arsedizioni-ars-utils-clipper.ui.d.ts +1 -1
  32. package/types/arsedizioni-ars-utils-core.d.ts +5 -5
  33. package/types/arsedizioni-ars-utils-core.date.d.ts +3 -3
  34. package/types/arsedizioni-ars-utils-evolution.common.d.ts +23 -7
  35. package/types/arsedizioni-ars-utils-support.common.d.ts +11 -2
  36. package/types/arsedizioni-ars-utils-ui.d.ts +2 -2
  37. package/types/arsedizioni-ars-utils-ui.dialogs.d.ts +1 -1
  38. package/types/arsedizioni-ars-utils-ui.dialogs.select.d.ts +1 -1
  39. package/types/arsedizioni-ars-utils-ui.help.d.ts +2 -2
  40. package/types/arsedizioni-ars-utils-ui.paginator.d.ts +1 -1
  41. package/types/arsedizioni-ars-utils-ui.shell.d.ts +1 -1
  42. package/types/arsedizioni-ars-utils-ui.tinymce.d.ts +1 -1
@@ -2,13 +2,23 @@ import { HttpResponse, HttpClient } from '@angular/common/http';
2
2
  import * as i0 from '@angular/core';
3
3
  import { inject, signal, Service, DestroyRef } from '@angular/core';
4
4
  import { BroadcastService, SystemUtils } from '@arsedizioni/ars-utils/core';
5
- import { tap, catchError, throwError, EMPTY, of } from 'rxjs';
5
+ import { retry, timer, throwError, tap, catchError, EMPTY, of } from 'rxjs';
6
6
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
  import { catchError as catchError$1, map, finalize } from 'rxjs/operators';
8
8
 
9
9
  const EvolutionMessages = {
10
10
  // Error
11
11
  ERROR: '§evo-error',
12
+ /**
13
+ * The API is not answering: the interceptor has already sent the request three times over ten
14
+ * seconds and none of them got an answer carrying an `ApiResult` envelope. Emitted once per
15
+ * outage, however many requests were in flight, and re-armed on the first good response.
16
+ *
17
+ * Deliberately not an `ERROR`: a server being replaced is neither a failed operation to report
18
+ * in a dialog nor an expired session to sign out of, and the host application usually answers it
19
+ * with a banner that clears itself.
20
+ */
21
+ ERROR_UNAVAILABLE: '§evo-error-unavailable',
12
22
  SUCCESS: '§evo-success',
13
23
  SUCCESS_TOAST: '§evo-success-toast',
14
24
  // Login
@@ -494,6 +504,33 @@ class EvolutionComplianceContextInfo {
494
504
 
495
505
  /** Minimum milliseconds between consecutive generic error broadcasts (debounce guard). */
496
506
  const ERROR_DEBOUNCE_MS = 5000;
507
+ /** How many more times a request is sent when the API is not there. */
508
+ const UNAVAILABLE_RETRIES = 2;
509
+ /** How long to wait before each of those retries, in milliseconds. */
510
+ const UNAVAILABLE_RETRY_DELAY = 5000;
511
+ /** The statuses a server that is being replaced answers with, plus the one it cannot answer at all. */
512
+ const UNAVAILABLE_STATUSES = [0, 500, 502, 503, 504];
513
+ /**
514
+ * Returns whether the failure means the API is not there, rather than the request being wrong.
515
+ *
516
+ * The discriminator is the body, not the status. An error raised INSIDE the application arrives
517
+ * wrapped in the usual `ApiResult` envelope, so it has been seen, parsed and acted upon: replaying
518
+ * it would run whatever it already ran a second time. A server that is being replaced answers with
519
+ * IIS's own HTML, or with nothing at all — no envelope — and that request never reached any
520
+ * handler. Only the second kind is retried, which is what makes it safe to retry a POST.
521
+ * @param error - The raw HTTP error object.
522
+ * @returns True when the request is worth sending again.
523
+ */
524
+ function isServiceUnavailable(error) {
525
+ if (!UNAVAILABLE_STATUSES.includes(parseInt(error?.status ?? '0')))
526
+ return false;
527
+ const body = error?.error;
528
+ const isApiEnvelope = !!body
529
+ && typeof body === 'object'
530
+ && !(body instanceof Blob)
531
+ && ('success' in body || 'problem' in body);
532
+ return !isApiEnvelope;
533
+ }
497
534
  /**
498
535
  * Returns whether the URL is an authentication endpoint, where a 401 is a normal outcome -- wrong
499
536
  * credentials, a rejected code, a refused passkey -- rather than an expired or invalid session.
@@ -531,6 +568,10 @@ function isStatusProbe(url) {
531
568
  }
532
569
  /**
533
570
  * Resolves the user-facing message for a failed request.
571
+ *
572
+ * There is no branch for status 0 any more, nor for the 5xx family: a request that got no answer,
573
+ * or an answer with no `ApiResult` envelope, never reaches here — it is retried and then reported
574
+ * as `ERROR_UNAVAILABLE`. What is left is the failures the API itself described.
534
575
  * @param error - The raw HTTP error object.
535
576
  * @param errorStatus - The numeric HTTP status code.
536
577
  * @returns A localised message, ready to be rendered as HTML.
@@ -538,8 +579,6 @@ function isStatusProbe(url) {
538
579
  function resolveErrorMessage(error, errorStatus) {
539
580
  const message = error?.error?.message ?? error?.message;
540
581
  switch (errorStatus) {
541
- case 0:
542
- return "In questo momento Evolution non è disponibile. Riprova tra qualche minuto.";
543
582
  case 401:
544
583
  // A 401 that reaches here comes from a sign-in attempt; anything else was classified as a
545
584
  // dead session before this function was called.
@@ -574,9 +613,15 @@ function resolveErrorMessage(error, errorStatus) {
574
613
  * broke, and those are three different things that used to be one. `invalidateSession` is
575
614
  * therefore now, and only, a 401 outside the authentication endpoints.
576
615
  *
577
- * `EvolutionServiceFlags.NotifySystemErrors` still governs one narrow case, and only that one: a
578
- * 5xx, i.e. a failure of the server rather than of the request. Without the flag it is swallowed,
579
- * as it always was in this library.
616
+ * Between them sits a third case, which is neither: the API is being replaced. A deployment answers
617
+ * with a 5xx that has no `ApiResult` envelope, or does not answer at all, for the few seconds the
618
+ * process takes to come back. That is not worth a dialog and not worth a redirect either, so the
619
+ * request is simply sent again — twice, five seconds apart — and only if the third attempt fails
620
+ * too does it become `EvolutionMessages.ERROR_UNAVAILABLE`, once per outage.
621
+ *
622
+ * `EvolutionServiceFlags.NotifySystemErrors` therefore now governs one narrower case: a 5xx that
623
+ * DOES carry an envelope, i.e. a server error the API itself described. Without the flag it is
624
+ * swallowed, as it always was in this library.
580
625
  *
581
626
  * Errors are re-thrown rather than swallowed: call sites rely on their `error` callback firing, and
582
627
  * on `finalize` clearing a busy dialog.
@@ -600,6 +645,11 @@ function evolutionAuthInterceptor(serviceUri, flags = EvolutionServiceFlags.None
600
645
  * several in-flight requests fail at once; re-armed on the next successful response.
601
646
  */
602
647
  let sessionExpiredNotified = false;
648
+ /**
649
+ * Whether the unavailable-API notification has already been sent. A deployment fails every
650
+ * request in flight, and one notification is enough. Re-armed on the next successful response.
651
+ */
652
+ let unavailableNotified = false;
603
653
  /**
604
654
  * Broadcasts a generic, user-facing error for an Evolution HTTP failure, debounced.
605
655
  * @param error - The raw error value thrown by the HTTP layer.
@@ -608,7 +658,7 @@ function evolutionAuthInterceptor(serviceUri, flags = EvolutionServiceFlags.None
608
658
  * @returns void
609
659
  */
610
660
  function notifyError(error, errorStatus, broadcastService) {
611
- // A broken server is the one case the flag still governs.
661
+ // A 5xx the API described itself is the one case the flag still governs.
612
662
  if (errorStatus >= 500 && (flags & EvolutionServiceFlags.NotifySystemErrors) === 0)
613
663
  return;
614
664
  const now = Date.now();
@@ -643,10 +693,20 @@ function evolutionAuthInterceptor(serviceUri, flags = EvolutionServiceFlags.None
643
693
  'X-Evo-Account': sessionStorage.getItem(EVOLUTION_ACTIVE_ACCOUNT_KEY) ?? ''
644
694
  }
645
695
  });
646
- return next(authenticatedRequest).pipe(tap(event => {
647
- // A response means the session is valid again: re-arm the expiry notifier.
696
+ return next(authenticatedRequest).pipe(
697
+ // An API that is being replaced is not an error to show, it is a wait: two more attempts,
698
+ // five seconds apart. Anything else is rethrown on the spot, so a request that genuinely
699
+ // failed still fails immediately.
700
+ retry({
701
+ count: UNAVAILABLE_RETRIES,
702
+ delay: error => isServiceUnavailable(error)
703
+ ? timer(UNAVAILABLE_RETRY_DELAY)
704
+ : throwError(() => error)
705
+ }), tap(event => {
706
+ // A response means the session is valid again, and the API is back: re-arm both notifiers.
648
707
  if (event instanceof HttpResponse) {
649
708
  sessionExpiredNotified = false;
709
+ unavailableNotified = false;
650
710
  }
651
711
  }), catchError((error) => {
652
712
  // Which request this was is read from the request, not from `error.url`: a failure with no
@@ -658,6 +718,16 @@ function evolutionAuthInterceptor(serviceUri, flags = EvolutionServiceFlags.None
658
718
  // The silent probe stays silent: its caller reads the outcome from the rethrown error,
659
719
  // nobody else needs to hear about it.
660
720
  }
721
+ else if (isServiceUnavailable(error)) {
722
+ // Three attempts over ten seconds and the API is still not answering. There is nothing
723
+ // left to retry, so the host application is told once, however many requests were in
724
+ // flight. Reported whatever the flags say: `NotifySystemErrors` governs a 5xx the API
725
+ // described itself, and a server that never answered described nothing.
726
+ if (!unavailableNotified) {
727
+ unavailableNotified = true;
728
+ broadcastService.sendMessage(EvolutionMessages.ERROR_UNAVAILABLE);
729
+ }
730
+ }
661
731
  else if (errorStatus === 401 && !isAuthEndpoint(authenticatedRequest.url)) {
662
732
  // Expired or invalidated session on an authenticated request. Notified once: several
663
733
  // in-flight requests failing together is how a single expiry becomes a wall of dialogs.
@@ -851,10 +921,10 @@ class CoreService {
851
921
  sessionStorage.removeItem('evolution_client_id');
852
922
  this.reset();
853
923
  }
854
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: CoreService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
855
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: CoreService }); }
924
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: CoreService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
925
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: CoreService }); }
856
926
  }
857
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: CoreService, decorators: [{
927
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: CoreService, decorators: [{
858
928
  type: Service
859
929
  }] });
860
930
 
@@ -1219,10 +1289,10 @@ class LoginService {
1219
1289
  this.broadcastService.sendMessage(EvolutionMessages.LOGIN_COMPLETED);
1220
1290
  }
1221
1291
  }
1222
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LoginService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1223
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LoginService }); }
1292
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: LoginService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1293
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: LoginService }); }
1224
1294
  }
1225
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LoginService, decorators: [{
1295
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: LoginService, decorators: [{
1226
1296
  type: Service
1227
1297
  }] });
1228
1298
 
@@ -1310,10 +1380,10 @@ class ComplianceService {
1310
1380
  addActivities(params) {
1311
1381
  return this.httpClient.post(this.core.serviceUri + '/compliance/activities/add', params);
1312
1382
  }
1313
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ComplianceService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1314
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: ComplianceService }); }
1383
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ComplianceService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1384
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: ComplianceService }); }
1315
1385
  }
1316
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ComplianceService, decorators: [{
1386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ComplianceService, decorators: [{
1317
1387
  type: Service
1318
1388
  }] });
1319
1389
 
@@ -1383,10 +1453,10 @@ class AccountService {
1383
1453
  refreshLicence() {
1384
1454
  return this.httpClient.get(this.core.serviceUri + '/account/licenses/refresh/' + (this.core.user?.userId ?? -1));
1385
1455
  }
1386
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AccountService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1387
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: AccountService }); }
1456
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AccountService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1457
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: AccountService }); }
1388
1458
  }
1389
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AccountService, decorators: [{
1459
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AccountService, decorators: [{
1390
1460
  type: Service
1391
1461
  }] });
1392
1462
 
@@ -1460,10 +1530,10 @@ class EvolutionService {
1460
1530
  * @returns void
1461
1531
  */
1462
1532
  ping() { this.session.ping(); }
1463
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EvolutionService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1464
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: EvolutionService }); }
1533
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: EvolutionService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
1534
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: EvolutionService }); }
1465
1535
  }
1466
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EvolutionService, decorators: [{
1536
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: EvolutionService, decorators: [{
1467
1537
  type: Service
1468
1538
  }] });
1469
1539