spiderly 19.12.2 → 19.12.3

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.
@@ -34,11 +34,6 @@
34
34
  "name": "spiderly-upgrade",
35
35
  "surface": "skill",
36
36
  "description": "Upgrade a Spiderly consumer app's package version (NuGet + npm) to a newer Spiderly release. Use when the user asks to upgrade Spiderly, bump the Spiderly version, jump to a newer Spiderly release, or migrate to a newer Spiderly package version. Do NOT use for EF Core schema migrations (see ef-migrations skill)."
37
- },
38
- {
39
- "name": "verify-ui",
40
- "surface": "skill",
41
- "description": "Log past Spiderly's email-code auth wall to visually verify the running Angular admin panel — screenshot a page, click through a flow, or dogfood a UI change without writing a test. Use when asked to \"verify the admin UI\", \"check this page renders\", \"screenshot the admin panel\", \"does this screen look right\", or to eyeball a change in the live app. For authoring Playwright test suites or debugging CI traces, use the e2e-testing skill instead."
42
37
  }
43
38
  ]
44
39
  }
@@ -17,7 +17,7 @@ import { TooltipModule } from 'primeng/tooltip';
17
17
  import * as FileSaver from 'file-saver';
18
18
  import mime from 'mime';
19
19
  import 'reflect-metadata';
20
- import { map, Subject, throttleTime, BehaviorSubject, of, filter as filter$1, firstValueFrom, take, finalize as finalize$1, tap as tap$1, throwError } from 'rxjs';
20
+ import { map, Subject, throttleTime, BehaviorSubject, of, filter as filter$1, firstValueFrom, take, finalize as finalize$1, tap as tap$1, throwError, from } from 'rxjs';
21
21
  import * as i4$2 from 'primeng/checkbox';
22
22
  import { CheckboxModule } from 'primeng/checkbox';
23
23
  import * as i4$3 from 'primeng/colorpicker';
@@ -49,7 +49,7 @@ import * as i3$2 from '@angular/router';
49
49
  import { NavigationEnd, RouterModule } from '@angular/router';
50
50
  import * as i1$1 from 'primeng/api';
51
51
  import { ConfirmationService } from 'primeng/api';
52
- import { filter, map as map$1, finalize, catchError, delay, tap, takeUntil } from 'rxjs/operators';
52
+ import { filter, map as map$1, finalize, catchError, delay, tap, takeUntil, switchMap } from 'rxjs/operators';
53
53
  import * as i1$2 from '@angular/common/http';
54
54
  import { HttpParams, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http';
55
55
  import { InputOtp } from 'primeng/inputotp';
@@ -7964,7 +7964,7 @@ const unauthorizedInterceptor = (req, next) => {
7964
7964
  const messageService = inject(SpiderlyMessageService);
7965
7965
  const translocoService = inject(TranslocoService);
7966
7966
  const authService = inject(AuthServiceBase);
7967
- const reactToError = (err, request) => {
7967
+ const reactToError = (err, request, errorResponse) => {
7968
7968
  // Unconditional, production included, and the only log for a failed request (the global
7969
7969
  // ErrorHandler skips HTTP errors): the toast the user gets is deliberately vague, so this is
7970
7970
  // where a developer reads which request failed and how. See SpiderlyErrorHandler for why
@@ -7972,17 +7972,9 @@ const unauthorizedInterceptor = (req, next) => {
7972
7972
  // HttpErrorResponse on its own stringifies to "[object HttpErrorResponse]" in an error
7973
7973
  // tracker's console breadcrumb; the object still follows, to expand in devtools.
7974
7974
  console.error(`HTTP ${err.status} ${request.method} ${request.url}`, err);
7975
- // TODO: type this as an ApiError interface (TS mirror of ApiErrorDTO, next to errors/api-error-codes.ts)
7976
- // so message/errorCode/traceId reads of the cross-language contract are compile-checked, not conventional.
7977
- let errorResponse = err.error;
7978
- if (request.responseType !== 'json' && typeof err.error === 'string') {
7979
- try {
7980
- errorResponse = JSON.parse(err.error);
7981
- }
7982
- catch {
7983
- errorResponse = null;
7984
- }
7985
- }
7975
+ // TODO: type errorResponse as an ApiError interface (TS mirror of ApiErrorDTO, next to
7976
+ // errors/api-error-codes.ts) so message/errorCode/traceId reads of the cross-language contract
7977
+ // are compile-checked, not conventional.
7986
7978
  // ApiErrorDTO.traceId is present only on reportable errors, so this is a no-op everywhere else —
7987
7979
  // the server decides which responses carry a support reference, never this status-code chain.
7988
7980
  const withReference = (detail) => errorResponse?.traceId
@@ -8015,10 +8007,36 @@ const unauthorizedInterceptor = (req, next) => {
8015
8007
  messageService.errorMessage(withReference(translocoService.translate('UnexpectedErrorDetails')), translocoService.translate('UnexpectedErrorTitle'));
8016
8008
  }
8017
8009
  };
8018
- return next(req).pipe(catchError((err) => {
8019
- reactToError(err, req);
8010
+ return next(req).pipe(catchError((err) => readErrorBody(err, req).pipe(switchMap((errorResponse) => {
8011
+ reactToError(err, req, errorResponse);
8020
8012
  return throwError(() => err);
8021
- }));
8013
+ }))));
8014
+ };
8015
+ /**
8016
+ * The server's ApiErrorDTO, whatever body type the request asked for. Angular hands the ERROR body
8017
+ * back in the requested `responseType` too: a `'text'` request gets the JSON as a string, and a
8018
+ * `'blob'` request (every PDF / Excel download) gets it as a Blob, whose text is only readable
8019
+ * asynchronously — which is why this is an Observable and the toast waits for it. Before the Blob
8020
+ * branch existed, a BusinessException on a download endpoint reached the operator as the generic
8021
+ * "bad request" toast: the sentence the server had spelled out was sitting unread in the Blob.
8022
+ * A body that is not JSON resolves to null, so the status-code fallbacks below still apply.
8023
+ */
8024
+ const readErrorBody = (err, request) => {
8025
+ if (err.error instanceof Blob) {
8026
+ return from(err.error.text()).pipe(map$1(parseJsonOrNull), catchError(() => of(null)));
8027
+ }
8028
+ if (request.responseType !== 'json' && typeof err.error === 'string') {
8029
+ return of(parseJsonOrNull(err.error));
8030
+ }
8031
+ return of(err.error);
8032
+ };
8033
+ const parseJsonOrNull = (text) => {
8034
+ try {
8035
+ return JSON.parse(text);
8036
+ }
8037
+ catch {
8038
+ return null;
8039
+ }
8022
8040
  };
8023
8041
 
8024
8042
  function authInitializer(authService, platformId) {