valtech-components 4.0.172 → 4.0.173

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.
@@ -2,5 +2,5 @@
2
2
  * Current version of valtech-components.
3
3
  * This is automatically updated during the publish process.
4
4
  */
5
- export const VERSION = '4.0.172';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDdXJyZW50IHZlcnNpb24gb2YgdmFsdGVjaC1jb21wb25lbnRzLlxuICogVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHVwZGF0ZWQgZHVyaW5nIHRoZSBwdWJsaXNoIHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OID0gJzQuMC4xNzInO1xuIl19
5
+ export const VERSION = '4.0.173';
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDdXJyZW50IHZlcnNpb24gb2YgdmFsdGVjaC1jb21wb25lbnRzLlxuICogVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHVwZGF0ZWQgZHVyaW5nIHRoZSBwdWJsaXNoIHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OID0gJzQuMC4xNzMnO1xuIl19
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, InjectionToken, makeEnvironmentProviders, APP_INITIALIZER, ErrorHandler, Injectable, DestroyRef, Inject, signal, computed, PLATFORM_ID, isDevMode, Optional, runInInjectionContext, effect, Pipe, TemplateRef, ViewContainerRef, input, Directive, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, HostBinding, HostListener, SecurityContext, NgZone, ViewChild, ElementRef, ChangeDetectorRef, output, ContentChild, model, ViewEncapsulation, untracked, viewChild, Injector, isSignal } from '@angular/core';
3
- import { BehaviorSubject, throwError, Subject, map, distinctUntilChanged, filter as filter$1, take as take$1, firstValueFrom, of, from, EMPTY, Observable, interval, debounceTime, switchMap as switchMap$1, catchError as catchError$1, takeUntil, isObservable, forkJoin, race, timer, shareReplay } from 'rxjs';
4
- import { catchError, switchMap, finalize, filter, take, timeout, map as map$1, tap, distinctUntilChanged as distinctUntilChanged$1, retry, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
3
+ import { BehaviorSubject, throwError, Subject, map, distinctUntilChanged, filter as filter$1, take as take$1, firstValueFrom, of, from, EMPTY, Observable, interval, debounceTime, switchMap as switchMap$1, catchError as catchError$1, takeUntil, isObservable, forkJoin, race, timer, shareReplay as shareReplay$1 } from 'rxjs';
4
+ import { catchError, switchMap, finalize, filter, take, timeout, map as map$1, tap, shareReplay, distinctUntilChanged as distinctUntilChanged$1, retry, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
5
5
  import * as i1$3 from '@angular/common/http';
6
6
  import { provideHttpClient, withInterceptors, HttpClient, HttpErrorResponse } from '@angular/common/http';
7
7
  import * as i1$1 from '@angular/fire/firestore';
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
56
56
  * Current version of valtech-components.
57
57
  * This is automatically updated during the publish process.
58
58
  */
59
- const VERSION = '4.0.172';
59
+ const VERSION = '4.0.173';
60
60
 
61
61
  // Control de estado de refresco (singleton a nivel de módulo)
62
62
  let isRefreshing = false;
@@ -8349,6 +8349,13 @@ class AuthService {
8349
8349
  this.refreshTimerId = null;
8350
8350
  this.syncSubscription = null;
8351
8351
  this.firestoreRBACUnsubscribe = null;
8352
+ // Single-flight refresh: un solo POST /refresh en vuelo a la vez. Todos los
8353
+ // callers (bootstrap, reestablecer Firebase, interceptor 401, timer proactivo)
8354
+ // comparten el MISMO observable. Sin esto, refreshes concurrentes chocan contra
8355
+ // la rotación de refresh token del backend: el primero rota el token y el
8356
+ // segundo llega con el token ya invalidado → 401 → logout espurio (el usuario
8357
+ // cae a la landing al actualizar). Ver project_pwa-login-freeze.
8358
+ this.inFlightRefresh$ = null;
8352
8359
  // =============================================
8353
8360
  // ESTADO PÚBLICO (Signals readonly)
8354
8361
  // =============================================
@@ -8759,13 +8766,20 @@ class AuthService {
8759
8766
  * que el cliente debe guardar para el próximo refresh.
8760
8767
  */
8761
8768
  refreshAccessToken() {
8769
+ // Single-flight: si ya hay un /refresh en vuelo, devolvemos el MISMO
8770
+ // observable (multicast vía shareReplay). Evita la carrera contra la rotación
8771
+ // de refresh token del backend, que producía 401 espurios → logout al
8772
+ // actualizar. Ver inFlightRefresh$ y project_pwa-login-freeze.
8773
+ if (this.inFlightRefresh$) {
8774
+ return this.inFlightRefresh$;
8775
+ }
8762
8776
  // H-05 same-site-aware: en same-site (prod) el refresh va SOLO por la cookie
8763
8777
  // HttpOnly (body vacío), no se persiste en localStorage → un XSS no lo roba.
8764
8778
  // En cross-site (dev) la cookie de terceros se bloquea (Safari ITP / Chrome
8765
8779
  // 3rd-party phase-out) → se manda en el body + se persiste (fallback).
8766
8780
  // withCredentials adjunta la cookie cuando el browser la permite.
8767
8781
  const body = this.storageService.cookieOnly ? {} : { refreshToken: this.state().refreshToken };
8768
- return this.http
8782
+ this.inFlightRefresh$ = this.http
8769
8783
  .post(`${this.baseUrl}/refresh`, body, { withCredentials: true })
8770
8784
  .pipe(
8771
8785
  // Nunca dejar el refresh colgado: si la red/Lambda no responde a tiempo,
@@ -8795,7 +8809,17 @@ class AuthService {
8795
8809
  // initialize() only clears state (no redirect). Auto-navigating to
8796
8810
  // /login from this method redirects even users on public routes.
8797
8811
  return throwError(() => error);
8798
- }));
8812
+ }),
8813
+ // Liberar el slot al terminar (éxito, error o timeout) para que el
8814
+ // próximo refresh real arranque uno nuevo.
8815
+ finalize(() => {
8816
+ this.inFlightRefresh$ = null;
8817
+ }),
8818
+ // Multicast: los side-effects del tap corren UNA vez y todos los callers
8819
+ // concurrentes reciben la misma respuesta/error. refCount:false → que un
8820
+ // caller se desuscribe temprano NO cancela la rotación de token en vuelo.
8821
+ shareReplay({ bufferSize: 1, refCount: false }));
8822
+ return this.inFlightRefresh$;
8799
8823
  }
8800
8824
  /**
8801
8825
  * Maneja autenticación exitosa desde fuentes externas (OAuth, etc).
@@ -61908,7 +61932,7 @@ class LegalContentService {
61908
61932
  const stream = fallback && fallback !== locale
61909
61933
  ? primary.pipe(catchError$1(() => this.loadOne(slug, fallback, options.basePath)))
61910
61934
  : primary;
61911
- const shared = stream.pipe(shareReplay({ bufferSize: 1, refCount: false }));
61935
+ const shared = stream.pipe(shareReplay$1({ bufferSize: 1, refCount: false }));
61912
61936
  this.cache.set(key, shared);
61913
61937
  return shared;
61914
61938
  }