valtech-components 2.0.826 → 2.0.828
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.
- package/esm2022/lib/services/auth/auth.service.mjs +48 -1
- package/esm2022/lib/services/firebase/firebase.service.mjs +12 -2
- package/esm2022/lib/services/firebase/notifications.service.mjs +10 -3
- package/esm2022/lib/services/refreshable-stream/refreshable-stream.mjs +16 -4
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +82 -6
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/services/auth/auth.service.d.ts +18 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -53,7 +53,7 @@ import 'prismjs/components/prism-json';
|
|
|
53
53
|
* Current version of valtech-components.
|
|
54
54
|
* This is automatically updated during the publish process.
|
|
55
55
|
*/
|
|
56
|
-
const VERSION = '2.0.
|
|
56
|
+
const VERSION = '2.0.828';
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Servicio para gestionar presets de componentes.
|
|
@@ -21509,6 +21509,7 @@ class FirebaseService {
|
|
|
21509
21509
|
// true al haber User, false en signout. Permite que los gates
|
|
21510
21510
|
// re-evalúen tras un logout/login dentro de la misma sesión de página.
|
|
21511
21511
|
this._firebaseAuthReady.set(!!user);
|
|
21512
|
+
console.log(`[FBAuth] authState emit user=${user?.uid ?? 'null'} firebaseAuthReady=${!!user}`);
|
|
21512
21513
|
this.sessionState.next({
|
|
21513
21514
|
user: user ? this.mapUser(user) : null,
|
|
21514
21515
|
isAuthenticated: !!user,
|
|
@@ -21518,6 +21519,7 @@ class FirebaseService {
|
|
|
21518
21519
|
},
|
|
21519
21520
|
error: error => {
|
|
21520
21521
|
this._firebaseAuthReady.set(false);
|
|
21522
|
+
console.error(`[FBAuth] authState ERROR: ${error instanceof Error ? error.message : String(error)}`);
|
|
21521
21523
|
this.sessionState.next({
|
|
21522
21524
|
user: null,
|
|
21523
21525
|
isAuthenticated: false,
|
|
@@ -21536,9 +21538,14 @@ class FirebaseService {
|
|
|
21536
21538
|
*/
|
|
21537
21539
|
whenFirebaseAuthReady() {
|
|
21538
21540
|
if (this._firebaseAuthReady()) {
|
|
21541
|
+
console.log('[FBAuth] whenFirebaseAuthReady resolved immediately (already ready)');
|
|
21539
21542
|
return Promise.resolve(true);
|
|
21540
21543
|
}
|
|
21541
|
-
|
|
21544
|
+
console.log('[FBAuth] whenFirebaseAuthReady waiting for Firebase Auth session...');
|
|
21545
|
+
return firstValueFrom(this.firebaseAuthReady$).then(v => {
|
|
21546
|
+
console.log('[FBAuth] whenFirebaseAuthReady resolved after wait');
|
|
21547
|
+
return v;
|
|
21548
|
+
});
|
|
21542
21549
|
}
|
|
21543
21550
|
// ===========================================================================
|
|
21544
21551
|
// AUTENTICACIÓN
|
|
@@ -21558,6 +21565,7 @@ class FirebaseService {
|
|
|
21558
21565
|
* ```
|
|
21559
21566
|
*/
|
|
21560
21567
|
async signInWithCustomToken(token) {
|
|
21568
|
+
console.log(`[FBAuth] signInWithCustomToken called tokenLen=${token?.length ?? 0}`);
|
|
21561
21569
|
try {
|
|
21562
21570
|
const credential = await signInWithCustomToken(this.auth, token);
|
|
21563
21571
|
// Forzar refresh del token para asegurar que los claims estén actualizados
|
|
@@ -21565,10 +21573,12 @@ class FirebaseService {
|
|
|
21565
21573
|
if (credential.user) {
|
|
21566
21574
|
await credential.user.getIdToken(true);
|
|
21567
21575
|
}
|
|
21576
|
+
console.log(`[FBAuth] signInWithCustomToken OK uid=${credential.user?.uid ?? 'null'}`);
|
|
21568
21577
|
return credential;
|
|
21569
21578
|
}
|
|
21570
21579
|
catch (error) {
|
|
21571
21580
|
const message = this.getErrorMessage(error);
|
|
21581
|
+
console.error(`[FBAuth] signInWithCustomToken FAILED: ${message}`);
|
|
21572
21582
|
throw new Error(message);
|
|
21573
21583
|
}
|
|
21574
21584
|
}
|
|
@@ -23766,8 +23776,11 @@ class NotificationsService {
|
|
|
23766
23776
|
return this.collectionReady$.asObservable();
|
|
23767
23777
|
}
|
|
23768
23778
|
return this.collectionReady$.pipe(switchMap(collection => {
|
|
23769
|
-
if (!collection)
|
|
23779
|
+
if (!collection) {
|
|
23780
|
+
console.log('[Notifs] collectionWhenAuthReady$ — collection is null (not initialized)');
|
|
23770
23781
|
return of(null);
|
|
23782
|
+
}
|
|
23783
|
+
console.log('[Notifs] collectionWhenAuthReady$ — collection ready, awaiting Firebase Auth');
|
|
23771
23784
|
return from(fb.whenFirebaseAuthReady()).pipe(map$1(() => collection));
|
|
23772
23785
|
}));
|
|
23773
23786
|
}
|
|
@@ -23810,6 +23823,7 @@ class NotificationsService {
|
|
|
23810
23823
|
* un userId diferente al del usuario autenticado.
|
|
23811
23824
|
*/
|
|
23812
23825
|
initialize(userId) {
|
|
23826
|
+
console.log(`[Notifs] initialize uid=${userId}`);
|
|
23813
23827
|
if (!this.collectionFactory) {
|
|
23814
23828
|
console.warn('[Notifications] FirestoreCollectionFactory not available. Ensure provideValtechFirebase() is configured.');
|
|
23815
23829
|
return;
|
|
@@ -23853,8 +23867,11 @@ class NotificationsService {
|
|
|
23853
23867
|
*/
|
|
23854
23868
|
getAll(limit = DEFAULT_LIMIT) {
|
|
23855
23869
|
return this.collectionWhenAuthReady$().pipe(switchMap(collection => {
|
|
23856
|
-
if (!collection)
|
|
23870
|
+
if (!collection) {
|
|
23871
|
+
console.warn('[Notifs] getAll — collection null → returning EMPTY (no emission)');
|
|
23857
23872
|
return EMPTY;
|
|
23873
|
+
}
|
|
23874
|
+
console.log('[Notifs] getAll — attaching Firestore watchAll listener');
|
|
23858
23875
|
return collection.watchAll({
|
|
23859
23876
|
orderBy: [{ field: 'createdAt', direction: 'desc' }],
|
|
23860
23877
|
limit,
|
|
@@ -24609,6 +24626,14 @@ class AuthService {
|
|
|
24609
24626
|
}
|
|
24610
24627
|
// 3. Iniciar timer de refresco proactivo
|
|
24611
24628
|
this.startRefreshTimer();
|
|
24629
|
+
// 4. Re-establecer la sesión de Firebase en bootstrap.
|
|
24630
|
+
// El access token sigue válido, así que NO pasamos por la rama de
|
|
24631
|
+
// refresh — pero la persistencia IndexedDB propia de Firebase Auth no
|
|
24632
|
+
// sobrevive el contenedor de storage de una PWA standalone en iOS.
|
|
24633
|
+
// Sin esto, `firebaseAuthReady` se queda en `false` para siempre y
|
|
24634
|
+
// cualquier listener Firestore (ej. el inbox de notificaciones) cuelga.
|
|
24635
|
+
// Fire-and-forget: NO debe demorar el resolve de initialize().
|
|
24636
|
+
void this.ensureFirebaseSessionOnBootstrap();
|
|
24612
24637
|
}
|
|
24613
24638
|
else if (storedState.refreshToken) {
|
|
24614
24639
|
// 4. Token expirado pero hay refresh token - intentar refrescar
|
|
@@ -25355,6 +25380,8 @@ class AuthService {
|
|
|
25355
25380
|
// FIREBASE INTEGRATION
|
|
25356
25381
|
// =============================================
|
|
25357
25382
|
async signInWithFirebase(firebaseToken) {
|
|
25383
|
+
console.log(`[FBAuth] auth flow → signInWithFirebase attempt tokenLen=${firebaseToken?.length ?? 0} ` +
|
|
25384
|
+
`firebaseServicePresent=${!!this.firebaseService}`);
|
|
25358
25385
|
try {
|
|
25359
25386
|
if (this.firebaseService) {
|
|
25360
25387
|
await this.firebaseService.signInWithCustomToken(firebaseToken);
|
|
@@ -25383,6 +25410,43 @@ class AuthService {
|
|
|
25383
25410
|
console.warn('[ValtechAuth] Firebase signout failed:', error);
|
|
25384
25411
|
}
|
|
25385
25412
|
}
|
|
25413
|
+
/**
|
|
25414
|
+
* Re-establece la sesión de Firebase Auth en el bootstrap de la app cuando
|
|
25415
|
+
* el access token de la app sigue válido (la rama de initialize() que NO
|
|
25416
|
+
* pasa por refresh).
|
|
25417
|
+
*
|
|
25418
|
+
* Firebase Auth mantiene su PROPIA sesión con persistencia IndexedDB
|
|
25419
|
+
* independiente del storage de tokens de la app. En un navegador normal esa
|
|
25420
|
+
* persistencia se auto-restaura sola; en una PWA standalone en iOS el
|
|
25421
|
+
* contenedor de storage no sobrevive, y `firebaseAuthReady` se queda en
|
|
25422
|
+
* `false` indefinidamente → los listeners Firestore cuelgan (~40s timeout).
|
|
25423
|
+
*
|
|
25424
|
+
* Estrategia: dar una ventana corta a la auto-restauración de Firebase y, si
|
|
25425
|
+
* no ocurre, forzar un `refreshAccessToken()` — que ya hace `signInWithFirebase`
|
|
25426
|
+
* con el `firebaseToken` que devuelve `/refresh`.
|
|
25427
|
+
*
|
|
25428
|
+
* Fire-and-forget: se invoca con `void` para no demorar `initialize()`.
|
|
25429
|
+
*/
|
|
25430
|
+
async ensureFirebaseSessionOnBootstrap() {
|
|
25431
|
+
if (!this.config.enableFirebaseIntegration || !this.firebaseService)
|
|
25432
|
+
return;
|
|
25433
|
+
// Dar a la persistencia IndexedDB propia de Firebase Auth una oportunidad
|
|
25434
|
+
// de auto-restaurarse (navegador normal). En una PWA standalone de iOS
|
|
25435
|
+
// normalmente NO lo hará.
|
|
25436
|
+
await new Promise(r => setTimeout(r, 2500));
|
|
25437
|
+
if (this.firebaseService.firebaseAuthReady()) {
|
|
25438
|
+
console.log('[FBAuth] bootstrap — Firebase session restored on its own');
|
|
25439
|
+
return;
|
|
25440
|
+
}
|
|
25441
|
+
console.log('[FBAuth] bootstrap — session valid but Firebase NOT ready → refreshing to re-establish');
|
|
25442
|
+
try {
|
|
25443
|
+
// refreshAccessToken() ya llama signInWithFirebase() en éxito.
|
|
25444
|
+
await firstValueFrom(this.refreshAccessToken());
|
|
25445
|
+
}
|
|
25446
|
+
catch (e) {
|
|
25447
|
+
console.warn('[FBAuth] bootstrap — refresh to re-establish Firebase failed:', e);
|
|
25448
|
+
}
|
|
25449
|
+
}
|
|
25386
25450
|
// =============================================
|
|
25387
25451
|
// DEVICE REGISTRATION (Push Notifications)
|
|
25388
25452
|
// =============================================
|
|
@@ -38521,8 +38585,18 @@ function createRefreshableStream(factory, options) {
|
|
|
38521
38585
|
// sesión NUNCA se establece (token inválido, signInWithFirebase falló), el
|
|
38522
38586
|
// gate igual se libera tras `authGateTimeoutMs` y la factory procede: si no
|
|
38523
38587
|
// hay permiso terminará en estado `error`, jamás en skeleton infinito.
|
|
38588
|
+
// Tag los dos inputs del `race` para distinguir en logs cuál ganó: la sesión
|
|
38589
|
+
// de Firebase Auth (`auth-ready`) o el timeout de seguridad (`timeout`).
|
|
38524
38590
|
const authGate$ = firebase
|
|
38525
|
-
? race(firebase.firebaseAuthReady
|
|
38591
|
+
? race(firebase.firebaseAuthReady$.pipe(map$1(() => 'auth-ready')), timer(authGateTimeoutMs).pipe(map$1(() => 'timeout'))).pipe(take(1), tap(winner => {
|
|
38592
|
+
if (winner === 'timeout') {
|
|
38593
|
+
console.warn(`[RefreshStream] auth gate resolved by TIMEOUT after ${authGateTimeoutMs}ms ` +
|
|
38594
|
+
'(Firebase Auth session never confirmed)');
|
|
38595
|
+
}
|
|
38596
|
+
else {
|
|
38597
|
+
console.log('[RefreshStream] auth gate resolved by firebaseAuthReady$ (session ready)');
|
|
38598
|
+
}
|
|
38599
|
+
}))
|
|
38526
38600
|
: of(true);
|
|
38527
38601
|
const data = toSignal(toObservable(_reload, { injector }).pipe(tap(() => {
|
|
38528
38602
|
_loading.set(true);
|
|
@@ -38534,6 +38608,7 @@ function createRefreshableStream(factory, options) {
|
|
|
38534
38608
|
switchMap(() => authGate$.pipe(switchMap(() => factory().pipe(tap(() => {
|
|
38535
38609
|
_loading.set(false);
|
|
38536
38610
|
_error.set(false);
|
|
38611
|
+
console.log('[RefreshStream] factory emitted first value — loading=false');
|
|
38537
38612
|
}),
|
|
38538
38613
|
// Backoff corto exponencial acotado — solo red de seguridad para
|
|
38539
38614
|
// reconexiones transitorias, NO para cubrir cold start.
|
|
@@ -38546,9 +38621,10 @@ function createRefreshableStream(factory, options) {
|
|
|
38546
38621
|
// silencio), `timeout` dispara un error → `catchError` → estado
|
|
38547
38622
|
// `error`. Nunca skeleton perpetuo. Va DESPUÉS de `retry` para
|
|
38548
38623
|
// que cuente el tiempo total, no por intento.
|
|
38549
|
-
timeout({ first: firstEmitTimeoutMs }), catchError(
|
|
38624
|
+
timeout({ first: firstEmitTimeoutMs }), catchError(err => {
|
|
38550
38625
|
_loading.set(false);
|
|
38551
38626
|
_error.set(true);
|
|
38627
|
+
console.error(`[RefreshStream] factory failed/timed out → error state: ${err instanceof Error ? err.message : String(err)}`);
|
|
38552
38628
|
return of(fallback);
|
|
38553
38629
|
})))))), { initialValue: null, injector });
|
|
38554
38630
|
return {
|