@ama-mfe/ng-utils 14.5.0-prerelease.9 → 14.5.0-rc.2
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/README.md +42 -0
- package/fesm2022/ama-mfe-ng-utils.mjs +690 -127
- package/fesm2022/ama-mfe-ng-utils.mjs.map +1 -1
- package/migration.json +10 -0
- package/package.json +13 -10
- package/schematics/ng-update/v14-5/index.d.ts +8 -0
- package/schematics/ng-update/v14-5/index.d.ts.map +1 -0
- package/schematics/ng-update/v14-5/index.js +64 -0
- package/schematics/ng-update/v14-5/index.js.map +1 -0
- package/types/ama-mfe-ng-utils.d.ts +425 -47
- package/types/ama-mfe-ng-utils.d.ts.map +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { MessagePeerService, MESSAGE_PEER_CONFIG, MESSAGE_PEER_CONNECT_OPTIONS } from '@amadeus-it-group/microfrontends-angular';
|
|
2
2
|
export { MessagePeerService as ConnectionService } from '@amadeus-it-group/microfrontends-angular';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { input, inject, ElementRef, computed, SecurityContext, effect, HostBinding, Directive, Injectable, signal, DestroyRef, provideAppInitializer, Pipe, makeEnvironmentProviders, Renderer2, afterNextRender, ChangeDetectionStrategy, ViewEncapsulation, Component, untracked } from '@angular/core';
|
|
4
|
+
import { input, inject, ElementRef, computed, SecurityContext, effect, HostBinding, Directive, Injectable, signal, DestroyRef, provideAppInitializer, Pipe, makeEnvironmentProviders, Renderer2, afterNextRender, ChangeDetectionStrategy, ViewEncapsulation, Component, untracked, InjectionToken } from '@angular/core';
|
|
5
5
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
6
6
|
import { LoggerService } from '@o3r/logger';
|
|
7
|
-
import { HISTORY_MESSAGE_TYPE, USER_ACTIVITY_MESSAGE_TYPE, RESIZE_MESSAGE_TYPE, THEME_MESSAGE_TYPE, NAVIGATION_MESSAGE_TYPE } from '@ama-mfe/messages';
|
|
7
|
+
import { HISTORY_MESSAGE_TYPE, USER_ACTIVITY_MESSAGE_TYPE, RESIZE_MESSAGE_TYPE, THEME_MESSAGE_TYPE, NAVIGATION_MESSAGE_TYPE, NAVIGATION_BLOCK_STATE_MESSAGE_TYPE, NAVIGATION_REQUEST_MESSAGE_TYPE, NAVIGATION_DECISION_MESSAGE_TYPE } from '@ama-mfe/messages';
|
|
8
8
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
9
9
|
import { fromEvent, throttleTime, Subject, filter, map } from 'rxjs';
|
|
10
10
|
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
|
|
@@ -58,10 +58,10 @@ class ConnectDirective {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
62
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
61
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
62
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: ConnectDirective, isStandalone: true, selector: "iframe[connect]", inputs: { connect: { classPropertyName: "connect", publicName: "connect", isSignal: true, isRequired: true, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "src": "this.srcAttr" } }, ngImport: i0 }); }
|
|
63
63
|
}
|
|
64
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConnectDirective, decorators: [{
|
|
65
65
|
type: Directive,
|
|
66
66
|
args: [{
|
|
67
67
|
selector: 'iframe[connect]',
|
|
@@ -143,10 +143,10 @@ class ProducerManagerService {
|
|
|
143
143
|
}
|
|
144
144
|
return handlersPresent;
|
|
145
145
|
}
|
|
146
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
147
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
146
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ProducerManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
147
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ProducerManagerService, providedIn: 'root' }); }
|
|
148
148
|
}
|
|
149
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
149
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ProducerManagerService, decorators: [{
|
|
150
150
|
type: Injectable,
|
|
151
151
|
args: [{
|
|
152
152
|
providedIn: 'root'
|
|
@@ -267,16 +267,47 @@ class ConsumerManagerService {
|
|
|
267
267
|
return consumers.filter((c) => c !== consumer);
|
|
268
268
|
});
|
|
269
269
|
}
|
|
270
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
271
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
270
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConsumerManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
271
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConsumerManagerService, providedIn: 'root' }); }
|
|
272
272
|
}
|
|
273
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
273
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConsumerManagerService, decorators: [{
|
|
274
274
|
type: Injectable,
|
|
275
275
|
args: [{
|
|
276
276
|
providedIn: 'root'
|
|
277
277
|
}]
|
|
278
278
|
}], ctorParameters: () => [] });
|
|
279
279
|
|
|
280
|
+
/**
|
|
281
|
+
* An abstract base class for message consumers that provides common functionality for registering and unregistering with the ConsumerManagerService.
|
|
282
|
+
*
|
|
283
|
+
* This class implements the MessageConsumer interface and provides a default implementation for the start and stop methods.
|
|
284
|
+
* It also handles the automatic registration and unregistration of the consumer with the ConsumerManagerService when the service is created and destroyed.
|
|
285
|
+
*
|
|
286
|
+
* To use this class, simply extend it and implement the required properties (type and supportedVersions) and any additional logic needed to consume messages.
|
|
287
|
+
*/
|
|
288
|
+
class AbstractMessageConsumer {
|
|
289
|
+
constructor() {
|
|
290
|
+
/** The service used to register the consumer */
|
|
291
|
+
this.consumerManagerService = inject(ConsumerManagerService);
|
|
292
|
+
/** The reference used to handle the destruction of the consumer */
|
|
293
|
+
this.destroyRef = inject(DestroyRef);
|
|
294
|
+
/** Logger shared with subclasses. */
|
|
295
|
+
this.logger = inject(LoggerService);
|
|
296
|
+
}
|
|
297
|
+
/** @inheritdoc */
|
|
298
|
+
start() {
|
|
299
|
+
this.consumerManagerService.register(this);
|
|
300
|
+
this.unregisterCallback?.();
|
|
301
|
+
this.unregisterCallback = this.destroyRef.onDestroy(() => this.stop());
|
|
302
|
+
}
|
|
303
|
+
/** @inheritdoc */
|
|
304
|
+
stop() {
|
|
305
|
+
this.consumerManagerService.unregister(this);
|
|
306
|
+
this.unregisterCallback?.();
|
|
307
|
+
this.unregisterCallback = undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
280
311
|
/**
|
|
281
312
|
* Method to call in the constructor of a producer
|
|
282
313
|
* @note should be used in injection context
|
|
@@ -307,8 +338,9 @@ const registerConsumer = (consumer) => {
|
|
|
307
338
|
*
|
|
308
339
|
* This service listens for history messages and navigates accordingly.
|
|
309
340
|
*/
|
|
310
|
-
class HistoryConsumerService {
|
|
341
|
+
class HistoryConsumerService extends AbstractMessageConsumer {
|
|
311
342
|
constructor() {
|
|
343
|
+
super();
|
|
312
344
|
/**
|
|
313
345
|
* The type of messages this service handles.
|
|
314
346
|
*/
|
|
@@ -325,26 +357,16 @@ class HistoryConsumerService {
|
|
|
325
357
|
history.go(message.payload.delta);
|
|
326
358
|
}
|
|
327
359
|
};
|
|
328
|
-
|
|
360
|
+
/**
|
|
361
|
+
* Auto-starts the consumer on creation.
|
|
362
|
+
* @deprecated The constructor auto-starts the consumer for backwards compatibility. It will be removed in v15;
|
|
363
|
+
*/
|
|
329
364
|
this.start();
|
|
330
|
-
inject(DestroyRef).onDestroy(() => this.stop());
|
|
331
365
|
}
|
|
332
|
-
/**
|
|
333
|
-
|
|
334
|
-
*/
|
|
335
|
-
start() {
|
|
336
|
-
this.consumerManagerService.register(this);
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* @inheritdoc
|
|
340
|
-
*/
|
|
341
|
-
stop() {
|
|
342
|
-
this.consumerManagerService.unregister(this);
|
|
343
|
-
}
|
|
344
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HistoryConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
345
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HistoryConsumerService, providedIn: 'root' }); }
|
|
366
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HistoryConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
367
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HistoryConsumerService, providedIn: 'root' }); }
|
|
346
368
|
}
|
|
347
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
369
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HistoryConsumerService, decorators: [{
|
|
348
370
|
type: Injectable,
|
|
349
371
|
args: [{
|
|
350
372
|
providedIn: 'root'
|
|
@@ -460,10 +482,10 @@ class HostInfoPipe {
|
|
|
460
482
|
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
461
483
|
}
|
|
462
484
|
}
|
|
463
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
464
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.
|
|
485
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HostInfoPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
486
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: HostInfoPipe, isStandalone: true, name: "hostInfo" }); }
|
|
465
487
|
}
|
|
466
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
488
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HostInfoPipe, decorators: [{
|
|
467
489
|
type: Pipe,
|
|
468
490
|
args: [{
|
|
469
491
|
name: 'hostInfo'
|
|
@@ -514,6 +536,17 @@ function getDefaultClientEndpointStartOptions() {
|
|
|
514
536
|
function isEmbedded(windowParam = window) {
|
|
515
537
|
return windowParam.top !== windowParam.self;
|
|
516
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Generate a string id suitable for correlating asynchronous request/reply messages across the micro-frontend bus.
|
|
541
|
+
* Uses `crypto.randomUUID` when the runtime supports it, otherwise falls back to a timestamp + random suffix.
|
|
542
|
+
* @param prefix Optional prefix used by the fallback path to make ids easier to identify in logs (ignored when `crypto.randomUUID` is available).
|
|
543
|
+
*/
|
|
544
|
+
function generateCorrelationId(prefix = 'corr') {
|
|
545
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
546
|
+
return crypto.randomUUID();
|
|
547
|
+
}
|
|
548
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
549
|
+
}
|
|
517
550
|
|
|
518
551
|
/**
|
|
519
552
|
* Provide the communication protocol connection configuration
|
|
@@ -549,8 +582,9 @@ function provideConnection(connectionConfigOptions) {
|
|
|
549
582
|
/**
|
|
550
583
|
* This service listens for resize messages and updates the height of elements based on the received messages.
|
|
551
584
|
*/
|
|
552
|
-
class ResizeConsumerService {
|
|
585
|
+
class ResizeConsumerService extends AbstractMessageConsumer {
|
|
553
586
|
constructor() {
|
|
587
|
+
super();
|
|
554
588
|
this.newHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "newHeight" }] : /* istanbul ignore next */ []));
|
|
555
589
|
/**
|
|
556
590
|
* A readonly signal that provides the new height information from the channel.
|
|
@@ -570,26 +604,16 @@ class ResizeConsumerService {
|
|
|
570
604
|
*/
|
|
571
605
|
'1.0': (message) => this.newHeight.set({ height: message.payload.height, channelId: message.from })
|
|
572
606
|
};
|
|
573
|
-
|
|
607
|
+
/**
|
|
608
|
+
* Auto-starts the consumer on creation.
|
|
609
|
+
* @deprecated The constructor auto-starts the consumer for backwards compatibility. It will be removed in v15;
|
|
610
|
+
*/
|
|
574
611
|
this.start();
|
|
575
|
-
inject(DestroyRef).onDestroy(() => this.stop());
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Starts the resize handler service by registering it into the consumer manager service.
|
|
579
|
-
*/
|
|
580
|
-
start() {
|
|
581
|
-
this.consumerManagerService.register(this);
|
|
582
612
|
}
|
|
583
|
-
/**
|
|
584
|
-
|
|
585
|
-
*/
|
|
586
|
-
stop() {
|
|
587
|
-
this.consumerManagerService.unregister(this);
|
|
588
|
-
}
|
|
589
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
590
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
|
|
613
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
614
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
|
|
591
615
|
}
|
|
592
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
616
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResizeConsumerService, decorators: [{
|
|
593
617
|
type: Injectable,
|
|
594
618
|
args: [{
|
|
595
619
|
providedIn: 'root'
|
|
@@ -723,10 +747,10 @@ class ScalableDirective {
|
|
|
723
747
|
this.renderer.setStyle(this.elem.nativeElement, 'min-height', `${height}px`);
|
|
724
748
|
}
|
|
725
749
|
}
|
|
726
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
727
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
750
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ScalableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
751
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: ScalableDirective, isStandalone: true, selector: "[scalable]", inputs: { connect: { classPropertyName: "connect", publicName: "connect", isSignal: true, isRequired: false, transformFunction: null }, scalable: { classPropertyName: "scalable", publicName: "scalable", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
728
752
|
}
|
|
729
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
753
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ScalableDirective, decorators: [{
|
|
730
754
|
type: Directive,
|
|
731
755
|
args: [{
|
|
732
756
|
selector: '[scalable]',
|
|
@@ -884,10 +908,10 @@ class ThemeProducerService {
|
|
|
884
908
|
this.logger.error('Error in theme service message', message);
|
|
885
909
|
this.revertToPreviousTheme();
|
|
886
910
|
}
|
|
887
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
888
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
911
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ThemeProducerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
912
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ThemeProducerService, providedIn: 'root' }); }
|
|
889
913
|
}
|
|
890
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
914
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ThemeProducerService, decorators: [{
|
|
891
915
|
type: Injectable,
|
|
892
916
|
args: [{
|
|
893
917
|
providedIn: 'root'
|
|
@@ -920,10 +944,10 @@ class ApplyTheme {
|
|
|
920
944
|
}
|
|
921
945
|
return undefined;
|
|
922
946
|
}
|
|
923
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
924
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.
|
|
947
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ApplyTheme, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
948
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: ApplyTheme, isStandalone: true, name: "applyTheme" }); }
|
|
925
949
|
}
|
|
926
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
950
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ApplyTheme, decorators: [{
|
|
927
951
|
type: Pipe,
|
|
928
952
|
args: [{
|
|
929
953
|
name: 'applyTheme'
|
|
@@ -952,10 +976,10 @@ class SandboxDirective {
|
|
|
952
976
|
ngOnInit() {
|
|
953
977
|
this.renderer.setAttribute(this.elementRef.nativeElement, 'sandbox', this.mfeSandbox());
|
|
954
978
|
}
|
|
955
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
956
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
979
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SandboxDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
980
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: SandboxDirective, isStandalone: true, selector: "iframe[mfeSandbox]", inputs: { mfeSandbox: { classPropertyName: "mfeSandbox", publicName: "mfeSandbox", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
957
981
|
}
|
|
958
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
982
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SandboxDirective, decorators: [{
|
|
959
983
|
type: Directive,
|
|
960
984
|
args: [{
|
|
961
985
|
selector: 'iframe[mfeSandbox]'
|
|
@@ -988,10 +1012,10 @@ class IframeEmbedComponent {
|
|
|
988
1012
|
*/
|
|
989
1013
|
this.sandbox = input('allow-scripts allow-same-origin', ...(ngDevMode ? [{ debugName: "sandbox" }] : /* istanbul ignore next */ []));
|
|
990
1014
|
}
|
|
991
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
992
|
-
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
1015
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: IframeEmbedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1016
|
+
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: IframeEmbedComponent, isStandalone: true, selector: "mfe-iframe-embed", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, moduleId: { classPropertyName: "moduleId", publicName: "moduleId", isSignal: true, isRequired: true, transformFunction: null }, hostApplicationId: { classPropertyName: "hostApplicationId", publicName: "hostApplicationId", isSignal: true, isRequired: true, transformFunction: null }, sandbox: { classPropertyName: "sandbox", publicName: "sandbox", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let hostInfoOptions = {hostId: hostApplicationId(), moduleId: moduleId()};\n<iframe\n [connect]=\"hostInfoOptions.moduleId\"\n scalable\n [src]=\"safeSrc() | hostInfo: hostInfoOptions | applyTheme\"\n [mfeSandbox]=\"sandbox()\">\n</iframe>\n", styles: ["mfe-iframe-embed iframe{height:100%;width:100%;display:block}\n"], dependencies: [{ kind: "directive", type: ConnectDirective, selector: "iframe[connect]", inputs: ["connect", "src"] }, { kind: "directive", type: ScalableDirective, selector: "[scalable]", inputs: ["connect", "scalable"] }, { kind: "directive", type: SandboxDirective, selector: "iframe[mfeSandbox]", inputs: ["mfeSandbox"] }, { kind: "pipe", type: ApplyTheme, name: "applyTheme" }, { kind: "pipe", type: HostInfoPipe, name: "hostInfo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
993
1017
|
}
|
|
994
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1018
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: IframeEmbedComponent, decorators: [{
|
|
995
1019
|
type: Component,
|
|
996
1020
|
args: [{ selector: 'mfe-iframe-embed', imports: [
|
|
997
1021
|
ConnectDirective,
|
|
@@ -1007,8 +1031,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
1007
1031
|
*
|
|
1008
1032
|
* This service listens for navigation messages and updates the router state accordingly.
|
|
1009
1033
|
*/
|
|
1010
|
-
class NavigationConsumerService {
|
|
1034
|
+
class NavigationConsumerService extends AbstractMessageConsumer {
|
|
1011
1035
|
constructor() {
|
|
1036
|
+
super();
|
|
1012
1037
|
this.router = inject(Router);
|
|
1013
1038
|
this.activeRoute = inject(ActivatedRoute);
|
|
1014
1039
|
this.requestedUrl = new Subject();
|
|
@@ -1046,9 +1071,11 @@ class NavigationConsumerService {
|
|
|
1046
1071
|
this.navigate(message.payload.url, message.payload.extras);
|
|
1047
1072
|
}
|
|
1048
1073
|
};
|
|
1049
|
-
|
|
1074
|
+
/**
|
|
1075
|
+
* Auto-starts the consumer on creation.
|
|
1076
|
+
* @deprecated The constructor auto-starts the consumer for backwards compatibility. It will be removed in v15;
|
|
1077
|
+
*/
|
|
1050
1078
|
this.start();
|
|
1051
|
-
inject(DestroyRef).onDestroy(() => this.stop());
|
|
1052
1079
|
}
|
|
1053
1080
|
/**
|
|
1054
1081
|
* Parses a URL and returns an object containing the paths and query parameters.
|
|
@@ -1076,22 +1103,10 @@ class NavigationConsumerService {
|
|
|
1076
1103
|
replaceUrl: extras?.replaceUrl
|
|
1077
1104
|
});
|
|
1078
1105
|
}
|
|
1079
|
-
/**
|
|
1080
|
-
|
|
1081
|
-
*/
|
|
1082
|
-
start() {
|
|
1083
|
-
this.consumerManagerService.register(this);
|
|
1084
|
-
}
|
|
1085
|
-
/**
|
|
1086
|
-
* @inheritdoc
|
|
1087
|
-
*/
|
|
1088
|
-
stop() {
|
|
1089
|
-
this.consumerManagerService.unregister(this);
|
|
1090
|
-
}
|
|
1091
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NavigationConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1092
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NavigationConsumerService, providedIn: 'root' }); }
|
|
1106
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1107
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationConsumerService, providedIn: 'root' }); }
|
|
1093
1108
|
}
|
|
1094
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1109
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationConsumerService, decorators: [{
|
|
1095
1110
|
type: Injectable,
|
|
1096
1111
|
args: [{
|
|
1097
1112
|
providedIn: 'root'
|
|
@@ -1134,10 +1149,10 @@ class RouteMemorizeService {
|
|
|
1134
1149
|
getRoute(channelId) {
|
|
1135
1150
|
return this.routeStack[channelId];
|
|
1136
1151
|
}
|
|
1137
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1138
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1152
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RouteMemorizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1153
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RouteMemorizeService, providedIn: 'root' }); }
|
|
1139
1154
|
}
|
|
1140
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1155
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RouteMemorizeService, decorators: [{
|
|
1141
1156
|
type: Injectable,
|
|
1142
1157
|
args: [{
|
|
1143
1158
|
providedIn: 'root'
|
|
@@ -1185,10 +1200,10 @@ class RestoreRoute {
|
|
|
1185
1200
|
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
1186
1201
|
}
|
|
1187
1202
|
}
|
|
1188
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1189
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.
|
|
1203
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RestoreRoute, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1204
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: RestoreRoute, isStandalone: true, name: "restoreRoute" }); }
|
|
1190
1205
|
}
|
|
1191
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1206
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RestoreRoute, decorators: [{
|
|
1192
1207
|
type: Pipe,
|
|
1193
1208
|
args: [{
|
|
1194
1209
|
name: 'restoreRoute'
|
|
@@ -1242,10 +1257,10 @@ class RouteMemorizeDirective {
|
|
|
1242
1257
|
}
|
|
1243
1258
|
});
|
|
1244
1259
|
}
|
|
1245
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1246
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
1260
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RouteMemorizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1261
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: RouteMemorizeDirective, isStandalone: true, selector: "iframe[memorizeRoute]", inputs: { memorizeRoute: { classPropertyName: "memorizeRoute", publicName: "memorizeRoute", isSignal: true, isRequired: false, transformFunction: null }, memorizeRouteId: { classPropertyName: "memorizeRouteId", publicName: "memorizeRouteId", isSignal: true, isRequired: false, transformFunction: null }, memorizeMaxAge: { classPropertyName: "memorizeMaxAge", publicName: "memorizeMaxAge", isSignal: true, isRequired: false, transformFunction: null }, memorizeRouteMaxAge: { classPropertyName: "memorizeRouteMaxAge", publicName: "memorizeRouteMaxAge", isSignal: true, isRequired: false, transformFunction: null }, connect: { classPropertyName: "connect", publicName: "connect", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
1247
1262
|
}
|
|
1248
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1263
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RouteMemorizeDirective, decorators: [{
|
|
1249
1264
|
type: Directive,
|
|
1250
1265
|
args: [{
|
|
1251
1266
|
selector: 'iframe[memorizeRoute]',
|
|
@@ -1359,16 +1374,574 @@ class RoutingService {
|
|
|
1359
1374
|
}
|
|
1360
1375
|
});
|
|
1361
1376
|
}
|
|
1362
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1363
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1377
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RoutingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1378
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RoutingService, providedIn: 'root' }); }
|
|
1379
|
+
}
|
|
1380
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RoutingService, decorators: [{
|
|
1381
|
+
type: Injectable,
|
|
1382
|
+
args: [{
|
|
1383
|
+
providedIn: 'root'
|
|
1384
|
+
}]
|
|
1385
|
+
}], ctorParameters: () => [] });
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Module-side writable source of truth for the navigation block state.
|
|
1389
|
+
*
|
|
1390
|
+
* Feature code (forms, editors, etc.) calls {@link block} when unsaved changes appear and {@link unblock} once they are saved or discarded.
|
|
1391
|
+
* The {@link NavigationBlockStateProducerService} watches this signal and mirrors the state to the shell;
|
|
1392
|
+
* the {@link navigationBlockModuleGuard} reads it to decide whether to intercept in-module navigation.
|
|
1393
|
+
*/
|
|
1394
|
+
class NavigationBlockService {
|
|
1395
|
+
constructor() {
|
|
1396
|
+
this.stateSignal = signal({ blocked: false }, ...(ngDevMode ? [{ debugName: "stateSignal" }] : /* istanbul ignore next */ []));
|
|
1397
|
+
/** Readonly view of the current navigation block state. */
|
|
1398
|
+
this.state = this.stateSignal.asReadonly();
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Mark the module as blocked (unsaved changes present).
|
|
1402
|
+
* @param reason Optional human-readable reason shown in the confirmation modal.
|
|
1403
|
+
*/
|
|
1404
|
+
block(reason) {
|
|
1405
|
+
this.stateSignal.set({ blocked: true, reason });
|
|
1406
|
+
}
|
|
1407
|
+
/** Mark the module as unblocked (no unsaved changes). */
|
|
1408
|
+
unblock() {
|
|
1409
|
+
this.stateSignal.set({ blocked: false });
|
|
1410
|
+
}
|
|
1411
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1412
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockService, providedIn: 'root' }); }
|
|
1413
|
+
}
|
|
1414
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockService, decorators: [{
|
|
1415
|
+
type: Injectable,
|
|
1416
|
+
args: [{
|
|
1417
|
+
providedIn: 'root'
|
|
1418
|
+
}]
|
|
1419
|
+
}] });
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Module-side producer that mirrors {@link NavigationBlockService} to the shell via `navigation-block-state` messages.
|
|
1423
|
+
* Every state change triggers a broadcast so the shell guard can decide without a round-trip.
|
|
1424
|
+
*
|
|
1425
|
+
* Runs module-side only (it watches the module's writable state). The shell never instantiates it.
|
|
1426
|
+
*/
|
|
1427
|
+
class NavigationBlockStateProducerService {
|
|
1428
|
+
constructor() {
|
|
1429
|
+
/**
|
|
1430
|
+
* @inheritdoc
|
|
1431
|
+
*/
|
|
1432
|
+
this.types = NAVIGATION_BLOCK_STATE_MESSAGE_TYPE;
|
|
1433
|
+
this.connectionService = inject(MessagePeerService);
|
|
1434
|
+
this.producerManagerService = inject(ProducerManagerService);
|
|
1435
|
+
this.logger = inject(LoggerService);
|
|
1436
|
+
this.store = inject(NavigationBlockService);
|
|
1437
|
+
this.producerManagerService.register(this);
|
|
1438
|
+
inject(DestroyRef).onDestroy(() => this.producerManagerService.unregister(this));
|
|
1439
|
+
effect(() => {
|
|
1440
|
+
const state = this.store.state();
|
|
1441
|
+
const message = {
|
|
1442
|
+
type: NAVIGATION_BLOCK_STATE_MESSAGE_TYPE,
|
|
1443
|
+
version: '1.0',
|
|
1444
|
+
blocked: state.blocked,
|
|
1445
|
+
reason: state.reason
|
|
1446
|
+
};
|
|
1447
|
+
this.connectionService.send(message);
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* @inheritdoc
|
|
1452
|
+
*/
|
|
1453
|
+
handleError(message) {
|
|
1454
|
+
this.logger.error('navigation-block state message could not be consumed by the shell', message);
|
|
1455
|
+
}
|
|
1456
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockStateProducerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1457
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockStateProducerService, providedIn: 'root' }); }
|
|
1458
|
+
}
|
|
1459
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockStateProducerService, decorators: [{
|
|
1460
|
+
type: Injectable,
|
|
1461
|
+
args: [{
|
|
1462
|
+
providedIn: 'root'
|
|
1463
|
+
}]
|
|
1464
|
+
}], ctorParameters: () => [] });
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* Shell-side consumer for `navigation-block-state` messages sent by modules.
|
|
1468
|
+
*
|
|
1469
|
+
* Keeps the last received state in a signal that the shell guard reads to decide whether to intercept shell-initiated navigation.
|
|
1470
|
+
* The state stays `undefined` until any module reports a value (interpreted as "no block known" which means navigation is allowed).
|
|
1471
|
+
*/
|
|
1472
|
+
class NavigationBlockStateConsumerService extends AbstractMessageConsumer {
|
|
1473
|
+
constructor() {
|
|
1474
|
+
super(...arguments);
|
|
1475
|
+
/**
|
|
1476
|
+
* @inheritdoc
|
|
1477
|
+
*/
|
|
1478
|
+
this.type = NAVIGATION_BLOCK_STATE_MESSAGE_TYPE;
|
|
1479
|
+
/**
|
|
1480
|
+
* @inheritdoc
|
|
1481
|
+
*/
|
|
1482
|
+
this.supportedVersions = {
|
|
1483
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention -- expected naming for versions
|
|
1484
|
+
'1.0': (message) => {
|
|
1485
|
+
this.blockStateSignal.set({
|
|
1486
|
+
blocked: message.payload.blocked,
|
|
1487
|
+
reason: message.payload.reason,
|
|
1488
|
+
channelId: message.from
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
this.blockStateSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "blockStateSignal" }] : /* istanbul ignore next */ []));
|
|
1493
|
+
/** Last navigation block state reported by any module. */
|
|
1494
|
+
this.blockState = this.blockStateSignal.asReadonly();
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Mark the state as unblocked after the shell has resolved a confirmation (e.g. the user confirmed a sidebar-initiated navigation).
|
|
1498
|
+
*/
|
|
1499
|
+
clear() {
|
|
1500
|
+
this.blockStateSignal.set({ blocked: false });
|
|
1501
|
+
}
|
|
1502
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockStateConsumerService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1503
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockStateConsumerService, providedIn: 'root' }); }
|
|
1504
|
+
}
|
|
1505
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockStateConsumerService, decorators: [{
|
|
1506
|
+
type: Injectable,
|
|
1507
|
+
args: [{
|
|
1508
|
+
providedIn: 'root'
|
|
1509
|
+
}]
|
|
1510
|
+
}] });
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Manages navigation request/decision round-trip communication for **both** shell and module.
|
|
1514
|
+
* Sends `navigation-request` messages, tracks pending requests, and sends `navigation-decision` replies.
|
|
1515
|
+
* Correlates responses with their originating requests using correlation IDs.
|
|
1516
|
+
*
|
|
1517
|
+
* - Module-initiated navigation → guard calls `requestNavigation()` with no target, broadcasting to the shell;
|
|
1518
|
+
* the shell's handler opens the modal and replies.
|
|
1519
|
+
* - Shell-initiated navigation → guard calls `requestNavigation(channelId)` after the user already confirmed locally,
|
|
1520
|
+
* so the module can run its unblock / draft-persistence logic and reply.
|
|
1521
|
+
*
|
|
1522
|
+
* If the peer never replies the returned promise stays pending and the navigation is held — callers rely on Angular
|
|
1523
|
+
* tearing their context down (the producer's `ngOnDestroy` resolves pending promises to `false`).
|
|
1524
|
+
*/
|
|
1525
|
+
class NavigationRequestManagerService {
|
|
1526
|
+
constructor() {
|
|
1527
|
+
/**
|
|
1528
|
+
* @inheritdoc
|
|
1529
|
+
*/
|
|
1530
|
+
this.types = [NAVIGATION_REQUEST_MESSAGE_TYPE, NAVIGATION_DECISION_MESSAGE_TYPE];
|
|
1531
|
+
this.connectionService = inject(MessagePeerService);
|
|
1532
|
+
this.producerManagerService = inject(ProducerManagerService);
|
|
1533
|
+
this.logger = inject(LoggerService);
|
|
1534
|
+
this.producerManagerService.register(this);
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Ask the peer to handle the navigation. Resolves when the peer replies with a `navigation-decision`.
|
|
1538
|
+
* If the peer never replies the promise stays pending.
|
|
1539
|
+
*
|
|
1540
|
+
* Re-entrant calls while a request is pending (e.g. the same navigation firing both `canActivate` and `canActivateChild`)
|
|
1541
|
+
* return the same promise, so only one round-trip is sent.
|
|
1542
|
+
* @param target Optional target peer id. Omit to broadcast (module → shell).
|
|
1543
|
+
* @param reason Optional human-readable reason surfaced in the shell modal.
|
|
1544
|
+
*/
|
|
1545
|
+
requestNavigation(target, reason) {
|
|
1546
|
+
if (this.pendingRequest) {
|
|
1547
|
+
return this.pendingRequest.promise;
|
|
1548
|
+
}
|
|
1549
|
+
const correlationId = generateCorrelationId('nav-req');
|
|
1550
|
+
let resolveFn;
|
|
1551
|
+
const promise = new Promise((resolve) => {
|
|
1552
|
+
resolveFn = resolve;
|
|
1553
|
+
});
|
|
1554
|
+
this.pendingRequest = { correlationId, promise, resolve: resolveFn };
|
|
1555
|
+
const message = {
|
|
1556
|
+
type: NAVIGATION_REQUEST_MESSAGE_TYPE,
|
|
1557
|
+
version: '1.0',
|
|
1558
|
+
correlationId,
|
|
1559
|
+
reason
|
|
1560
|
+
};
|
|
1561
|
+
this.connectionService.send(message, target ? { to: target } : undefined);
|
|
1562
|
+
return promise;
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Send a `navigation-decision` reply with the given `correlationId`.
|
|
1566
|
+
* Called by {@link NavigationNegotiationConsumerService} after the side-specific handler has finished.
|
|
1567
|
+
* @param correlationId id echoed from the request
|
|
1568
|
+
* @param target peer id of the original sender
|
|
1569
|
+
* @param proceed whether to allow navigation (default true)
|
|
1570
|
+
*/
|
|
1571
|
+
sendDecision(correlationId, target, proceed = true) {
|
|
1572
|
+
const message = {
|
|
1573
|
+
type: NAVIGATION_DECISION_MESSAGE_TYPE,
|
|
1574
|
+
version: '1.0',
|
|
1575
|
+
correlationId,
|
|
1576
|
+
proceed
|
|
1577
|
+
};
|
|
1578
|
+
this.connectionService.send(message, { to: target });
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Called by {@link NavigationDecisionConsumerService} when a decision arrives.
|
|
1582
|
+
* Resolves the pending promise if the `correlationId` matches.
|
|
1583
|
+
* @param correlationId
|
|
1584
|
+
* @param proceed
|
|
1585
|
+
*/
|
|
1586
|
+
resolvePendingRequest(correlationId, proceed) {
|
|
1587
|
+
const pending = this.pendingRequest;
|
|
1588
|
+
if (!pending || pending.correlationId !== correlationId) {
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
this.pendingRequest = undefined;
|
|
1592
|
+
pending.resolve(proceed);
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* @inheritdoc
|
|
1596
|
+
*/
|
|
1597
|
+
handleError(message) {
|
|
1598
|
+
this.logger.error('navigation negotiation message could not be consumed by a peer', message);
|
|
1599
|
+
}
|
|
1600
|
+
ngOnDestroy() {
|
|
1601
|
+
if (this.pendingRequest) {
|
|
1602
|
+
this.pendingRequest.resolve(false);
|
|
1603
|
+
this.pendingRequest = undefined;
|
|
1604
|
+
}
|
|
1605
|
+
this.producerManagerService.unregister(this);
|
|
1606
|
+
}
|
|
1607
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationRequestManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1608
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationRequestManagerService, providedIn: 'root' }); }
|
|
1364
1609
|
}
|
|
1365
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1610
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationRequestManagerService, decorators: [{
|
|
1366
1611
|
type: Injectable,
|
|
1367
1612
|
args: [{
|
|
1368
1613
|
providedIn: 'root'
|
|
1369
1614
|
}]
|
|
1370
1615
|
}], ctorParameters: () => [] });
|
|
1371
1616
|
|
|
1617
|
+
/**
|
|
1618
|
+
* Default module-side `navigation-request` handler. Runs the module's local cleanup — at minimum clearing the block state — and resolves.
|
|
1619
|
+
* Modules cannot refuse a navigation (only the shell modal can), so this always resolves and a `navigation-decision` is always sent back.
|
|
1620
|
+
*
|
|
1621
|
+
* Used as the default factory of {@link NAVIGATION_REQUEST_HANDLER}, so a module gets this behavior without any explicit provider.
|
|
1622
|
+
* Apps that need to persist drafts before unblocking can override the token with their own handler.
|
|
1623
|
+
*/
|
|
1624
|
+
const createNavigationRequestHandler = () => {
|
|
1625
|
+
const store = inject(NavigationBlockService);
|
|
1626
|
+
return {
|
|
1627
|
+
handle: () => {
|
|
1628
|
+
store.unblock();
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
};
|
|
1632
|
+
/**
|
|
1633
|
+
* Injection token for the per-side request handler.
|
|
1634
|
+
*
|
|
1635
|
+
* **Default behavior (modules):** Clears the block state (unblock) — see {@link createNavigationRequestHandler}.
|
|
1636
|
+
* **Shell override:** Opens confirmation modal and awaits user decision.
|
|
1637
|
+
*
|
|
1638
|
+
* The module handler is provided by default at root level. Only the shell application needs to override it
|
|
1639
|
+
* with `provideNavigationRequestShellHandler()`.
|
|
1640
|
+
*/
|
|
1641
|
+
const NAVIGATION_REQUEST_HANDLER = new InjectionToken('NAVIGATION_REQUEST_HANDLER', {
|
|
1642
|
+
providedIn: 'root',
|
|
1643
|
+
factory: createNavigationRequestHandler
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
/**
|
|
1647
|
+
* Agnostic consumer for `navigation-request` messages.
|
|
1648
|
+
*
|
|
1649
|
+
* On each incoming request, invokes the side-specific handler registered under {@link NAVIGATION_REQUEST_HANDLER}:
|
|
1650
|
+
* - **Cockpit side** — handler opens the confirmation modal and awaits the user's answer. If the user cancels the handler
|
|
1651
|
+
* throws; this consumer catches it and replies with a decision carrying `proceed: false`, so the module's pending promise
|
|
1652
|
+
* resolves to `false` and the in-iframe navigation is held until a fresh request is made.
|
|
1653
|
+
* - **Module side** — handler runs the unblock / future draft-persistence logic and resolves. Modules cannot refuse;
|
|
1654
|
+
* a decision is always sent.
|
|
1655
|
+
*/
|
|
1656
|
+
class NavigationRequestConsumerService extends AbstractMessageConsumer {
|
|
1657
|
+
constructor() {
|
|
1658
|
+
super(...arguments);
|
|
1659
|
+
/**
|
|
1660
|
+
* @inheritdoc
|
|
1661
|
+
*/
|
|
1662
|
+
this.type = NAVIGATION_REQUEST_MESSAGE_TYPE;
|
|
1663
|
+
/**
|
|
1664
|
+
* @inheritdoc
|
|
1665
|
+
*/
|
|
1666
|
+
this.supportedVersions = {
|
|
1667
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention -- expected naming for versions
|
|
1668
|
+
'1.0': (message) => this.handleRequest(message)
|
|
1669
|
+
};
|
|
1670
|
+
this.producerService = inject(NavigationRequestManagerService);
|
|
1671
|
+
this.handler = inject(NAVIGATION_REQUEST_HANDLER);
|
|
1672
|
+
}
|
|
1673
|
+
async handleRequest(message) {
|
|
1674
|
+
try {
|
|
1675
|
+
await this.handler.handle({ from: message.from, reason: message.payload.reason });
|
|
1676
|
+
}
|
|
1677
|
+
catch (error) {
|
|
1678
|
+
// Handler rejected — usually the user cancelled, but any unexpected
|
|
1679
|
+
// exception ends up here too. Log it so the failure is observable, then
|
|
1680
|
+
// send a decision with proceed=false so the requester's pending promise
|
|
1681
|
+
// resolves and subsequent navigation attempts can create a fresh request.
|
|
1682
|
+
this.logger.error('navigation-request handler rejected', error, message);
|
|
1683
|
+
this.producerService.sendDecision(message.payload.correlationId, message.from, false);
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
this.producerService.sendDecision(message.payload.correlationId, message.from);
|
|
1687
|
+
}
|
|
1688
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationRequestConsumerService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1689
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationRequestConsumerService, providedIn: 'root' }); }
|
|
1690
|
+
}
|
|
1691
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationRequestConsumerService, decorators: [{
|
|
1692
|
+
type: Injectable,
|
|
1693
|
+
args: [{
|
|
1694
|
+
providedIn: 'root'
|
|
1695
|
+
}]
|
|
1696
|
+
}] });
|
|
1697
|
+
|
|
1698
|
+
/**
|
|
1699
|
+
* Provider wiring {@link NAVIGATION_REQUEST_HANDLER} to the module handler.
|
|
1700
|
+
* Not normally needed — the token already defaults to this handler — but
|
|
1701
|
+
* available for apps that want to register it explicitly.
|
|
1702
|
+
*/
|
|
1703
|
+
const provideNavigationRequestModuleHandler = () => makeEnvironmentProviders([
|
|
1704
|
+
{
|
|
1705
|
+
provide: NAVIGATION_REQUEST_HANDLER,
|
|
1706
|
+
useFactory: createNavigationRequestHandler
|
|
1707
|
+
}
|
|
1708
|
+
]);
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Default confirmation strategy using browser's native `window.confirm()`.
|
|
1712
|
+
* Simple, no dependencies, works in any environment. Suitable for:
|
|
1713
|
+
* - Development/testing
|
|
1714
|
+
* - Apps without custom modal libraries
|
|
1715
|
+
* - `@ama-mfe` packages that need basic confirmation
|
|
1716
|
+
*
|
|
1717
|
+
* Production apps typically override this with a modal-based strategy.
|
|
1718
|
+
*/
|
|
1719
|
+
class BrowserConfirmationStrategy {
|
|
1720
|
+
/**
|
|
1721
|
+
* Show native browser confirmation dialog.
|
|
1722
|
+
* @param reason Optional reason displayed in the message
|
|
1723
|
+
* @returns Promise<true> if user clicks OK, Promise<false> if user clicks Cancel
|
|
1724
|
+
*/
|
|
1725
|
+
confirm(reason) {
|
|
1726
|
+
const message = `${reason ? reason + '\n' : ''}Do you want to leave this page?`;
|
|
1727
|
+
// eslint-disable-next-line no-alert -- intentional use of window.confirm for basic confirmation strategy
|
|
1728
|
+
return Promise.resolve(window.confirm(message));
|
|
1729
|
+
}
|
|
1730
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: BrowserConfirmationStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1731
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: BrowserConfirmationStrategy, providedIn: 'root' }); }
|
|
1732
|
+
}
|
|
1733
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: BrowserConfirmationStrategy, decorators: [{
|
|
1734
|
+
type: Injectable,
|
|
1735
|
+
args: [{
|
|
1736
|
+
providedIn: 'root'
|
|
1737
|
+
}]
|
|
1738
|
+
}] });
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* Injection token for the navigation block confirmation strategy.
|
|
1742
|
+
* Defaults to {@link BrowserConfirmationStrategy} (uses `window.confirm()`).
|
|
1743
|
+
* Apps with a styled modal override the default by passing their own class via `provideNavigationConfig({ confirmationStrategy })`
|
|
1744
|
+
* or by binding this token directly.
|
|
1745
|
+
*/
|
|
1746
|
+
const NAVIGATION_BLOCK_CONFIRMATION = new InjectionToken('NAVIGATION_BLOCK_CONFIRMATION', {
|
|
1747
|
+
providedIn: 'root',
|
|
1748
|
+
factory: () => inject(BrowserConfirmationStrategy)
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Orchestrates navigation block confirmation by delegating to a pluggable strategy.
|
|
1753
|
+
* Handles concurrent call deduplication to prevent duplicate confirmations when guards run multiple times.
|
|
1754
|
+
*
|
|
1755
|
+
* The confirmation UI is read from the `NAVIGATION_BLOCK_CONFIRMATION` token, which defaults to {@link BrowserConfirmationStrategy}
|
|
1756
|
+
* (no setup required). Apps with a styled modal override the token via `provideNavigationConfig({ confirmationStrategy })`
|
|
1757
|
+
* or by binding the token directly.
|
|
1758
|
+
*/
|
|
1759
|
+
class NavigationBlockConfirmationService {
|
|
1760
|
+
constructor() {
|
|
1761
|
+
this.strategy = inject(NAVIGATION_BLOCK_CONFIRMATION);
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* Show confirmation and await user decision. Concurrent calls return the same promise to prevent duplicate confirmations.
|
|
1765
|
+
* @param reason Optional reason displayed in the confirmation UI
|
|
1766
|
+
* @returns `true` when the user confirms, `false` when they cancel
|
|
1767
|
+
*/
|
|
1768
|
+
confirm(reason) {
|
|
1769
|
+
// Deduplication: if confirmation already in progress, return same promise
|
|
1770
|
+
if (this.pendingConfirmation) {
|
|
1771
|
+
return this.pendingConfirmation;
|
|
1772
|
+
}
|
|
1773
|
+
// Delegate to injected strategy. Clear the slot only once the promise
|
|
1774
|
+
// settles, so concurrent callers in the meantime get the same promise.
|
|
1775
|
+
const pending = this.strategy.confirm(reason).finally(() => {
|
|
1776
|
+
this.pendingConfirmation = undefined;
|
|
1777
|
+
});
|
|
1778
|
+
this.pendingConfirmation = pending;
|
|
1779
|
+
return pending;
|
|
1780
|
+
}
|
|
1781
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockConfirmationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1782
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockConfirmationService, providedIn: 'root' }); }
|
|
1783
|
+
}
|
|
1784
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationBlockConfirmationService, decorators: [{
|
|
1785
|
+
type: Injectable,
|
|
1786
|
+
args: [{
|
|
1787
|
+
providedIn: 'root'
|
|
1788
|
+
}]
|
|
1789
|
+
}] });
|
|
1790
|
+
|
|
1791
|
+
/**
|
|
1792
|
+
* Shell application `navigation-request` handler used when a module initiates the navigation (in-iframe link click).
|
|
1793
|
+
* Opens the shared confirmation modal, awaits the user's answer, and — on confirm — clears the shell's block mirror.
|
|
1794
|
+
* On cancel the handler throws; the consumer catches it and replies with a `navigation-decision` carrying `proceed: false`,
|
|
1795
|
+
* so the module's pending promise resolves to `false` and the iframe navigation is held until a fresh request is made.
|
|
1796
|
+
*/
|
|
1797
|
+
const createNavigationRequestShellHandler = () => {
|
|
1798
|
+
const confirmation = inject(NavigationBlockConfirmationService);
|
|
1799
|
+
const blockConsumer = inject(NavigationBlockStateConsumerService);
|
|
1800
|
+
return {
|
|
1801
|
+
handle: async ({ reason }) => {
|
|
1802
|
+
const confirmed = await confirmation.confirm(reason);
|
|
1803
|
+
if (!confirmed) {
|
|
1804
|
+
throw new Error('navigation-request cancelled by user');
|
|
1805
|
+
}
|
|
1806
|
+
blockConsumer.clear();
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
};
|
|
1810
|
+
/**
|
|
1811
|
+
* Provider wiring {@link NAVIGATION_REQUEST_HANDLER} to the shell application
|
|
1812
|
+
* handler. Register once in the shell app providers to override the default
|
|
1813
|
+
* module handler.
|
|
1814
|
+
*/
|
|
1815
|
+
const provideNavigationRequestShellHandler = () => makeEnvironmentProviders([
|
|
1816
|
+
{
|
|
1817
|
+
provide: NAVIGATION_REQUEST_HANDLER,
|
|
1818
|
+
useFactory: createNavigationRequestShellHandler
|
|
1819
|
+
}
|
|
1820
|
+
]);
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* Agnostic consumer for `navigation-decision` messages — the reply to a
|
|
1824
|
+
* previously sent `navigation-request`. Forwards the decision to
|
|
1825
|
+
* {@link NavigationRequestManagerService.resolvePendingRequest} so
|
|
1826
|
+
* the awaiting caller's promise resolves.
|
|
1827
|
+
*/
|
|
1828
|
+
class NavigationDecisionConsumerService extends AbstractMessageConsumer {
|
|
1829
|
+
constructor() {
|
|
1830
|
+
super(...arguments);
|
|
1831
|
+
/**
|
|
1832
|
+
* @inheritdoc
|
|
1833
|
+
*/
|
|
1834
|
+
this.type = NAVIGATION_DECISION_MESSAGE_TYPE;
|
|
1835
|
+
/**
|
|
1836
|
+
* @inheritdoc
|
|
1837
|
+
*/
|
|
1838
|
+
this.supportedVersions = {
|
|
1839
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention -- expected naming for versions
|
|
1840
|
+
'1.0': (message) => {
|
|
1841
|
+
this.producerService.resolvePendingRequest(message.payload.correlationId, message.payload.proceed);
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
this.producerService = inject(NavigationRequestManagerService);
|
|
1845
|
+
}
|
|
1846
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationDecisionConsumerService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1847
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationDecisionConsumerService, providedIn: 'root' }); }
|
|
1848
|
+
}
|
|
1849
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: NavigationDecisionConsumerService, decorators: [{
|
|
1850
|
+
type: Injectable,
|
|
1851
|
+
args: [{
|
|
1852
|
+
providedIn: 'root'
|
|
1853
|
+
}]
|
|
1854
|
+
}] });
|
|
1855
|
+
|
|
1856
|
+
/**
|
|
1857
|
+
* Shell application guard intercepting shell-initiated navigation (sidebar clicks, URL bar, programmatic `router.navigate`)
|
|
1858
|
+
* when the last state received from a module is `blocked`.
|
|
1859
|
+
*
|
|
1860
|
+
* Can be used as both `canActivate` and `canActivateChild` to ensure the guard runs when entering a route directly
|
|
1861
|
+
* or navigating to its children.
|
|
1862
|
+
*
|
|
1863
|
+
* Flow:
|
|
1864
|
+
* - No state received, or `blocked === false` → navigation proceeds.
|
|
1865
|
+
* - `blocked === true` → open the confirmation modal.
|
|
1866
|
+
* - On cancel/dismiss → return `false`, state is untouched, no message sent to the module.
|
|
1867
|
+
* - On confirm → clear the shell mirror, send a `navigation-request` to the originating module (so it can run its unblock /
|
|
1868
|
+
* future draft-persistence logic), await its `navigation-decision`, then return `true`.
|
|
1869
|
+
*/
|
|
1870
|
+
const navigationBlockShellGuard = async () => {
|
|
1871
|
+
const consumer = inject(NavigationBlockStateConsumerService);
|
|
1872
|
+
const confirmation = inject(NavigationBlockConfirmationService);
|
|
1873
|
+
const negotiation = inject(NavigationRequestManagerService);
|
|
1874
|
+
const state = consumer.blockState();
|
|
1875
|
+
if (!state?.blocked) {
|
|
1876
|
+
return true;
|
|
1877
|
+
}
|
|
1878
|
+
const confirmed = await confirmation.confirm(state.reason);
|
|
1879
|
+
if (!confirmed) {
|
|
1880
|
+
return false;
|
|
1881
|
+
}
|
|
1882
|
+
consumer.clear();
|
|
1883
|
+
if (!state.channelId) {
|
|
1884
|
+
return true;
|
|
1885
|
+
}
|
|
1886
|
+
return negotiation.requestNavigation(state.channelId, state.reason);
|
|
1887
|
+
};
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* Module-side guard intercepting navigation away from a page that holds unsaved changes.
|
|
1891
|
+
*
|
|
1892
|
+
* - `blocked === false` → navigation proceeds.
|
|
1893
|
+
* - `blocked === true` → broadcast a `navigation-request` to the shell and await the reply. The shell opens the confirmation modal
|
|
1894
|
+
* and replies in both cases: `proceed === true` on confirm, `proceed === false` on cancel/dismiss. On proceed, clear the local
|
|
1895
|
+
* state and return `true`; on cancel, return `false` and leave the block state intact so the navigation is held and a later
|
|
1896
|
+
* attempt can negotiate again.
|
|
1897
|
+
*
|
|
1898
|
+
* Wire it as `canDeactivate` only on the routes that can actually hold unsaved work — modules opt in per page.
|
|
1899
|
+
*/
|
|
1900
|
+
const navigationBlockModuleGuard = async () => {
|
|
1901
|
+
const store = inject(NavigationBlockService);
|
|
1902
|
+
const negotiation = inject(NavigationRequestManagerService);
|
|
1903
|
+
const { blocked, reason } = store.state();
|
|
1904
|
+
if (!blocked) {
|
|
1905
|
+
return true;
|
|
1906
|
+
}
|
|
1907
|
+
const proceed = await negotiation.requestNavigation(undefined, reason);
|
|
1908
|
+
if (proceed) {
|
|
1909
|
+
store.unblock();
|
|
1910
|
+
}
|
|
1911
|
+
return proceed;
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
/**
|
|
1915
|
+
* Convenience helper that explicitly binds the {@link NAVIGATION_BLOCK_CONFIRMATION} token to {@link BrowserConfirmationStrategy}.
|
|
1916
|
+
*
|
|
1917
|
+
* **Usually unnecessary** — the token already defaults to `BrowserConfirmationStrategy` via its tree-shakable factory,
|
|
1918
|
+
* so the helper only re-binds the same value. Use it when you want the binding to be visible at the call site
|
|
1919
|
+
* (e.g. for documentation in `app.config.ts`) or when you've previously overridden the token and want to switch back
|
|
1920
|
+
* to the default in a child injector.
|
|
1921
|
+
*
|
|
1922
|
+
* Production apps with a styled modal pass their own strategy via `provideNavigationConfig({ confirmationStrategy })` instead.
|
|
1923
|
+
* @example
|
|
1924
|
+
* ```typescript
|
|
1925
|
+
* // app.config.ts
|
|
1926
|
+
* export const appConfig: ApplicationConfig = {
|
|
1927
|
+
* providers: [
|
|
1928
|
+
* provideDefaultNavigationBlockConfirmation(),
|
|
1929
|
+
* // ... other providers
|
|
1930
|
+
* ]
|
|
1931
|
+
* };
|
|
1932
|
+
* ```
|
|
1933
|
+
*/
|
|
1934
|
+
function provideDefaultNavigationBlockConfirmation() {
|
|
1935
|
+
return makeEnvironmentProviders([
|
|
1936
|
+
{
|
|
1937
|
+
provide: NAVIGATION_BLOCK_CONFIRMATION,
|
|
1938
|
+
useClass: BrowserConfirmationStrategy
|
|
1939
|
+
}
|
|
1940
|
+
]);
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// Block state (module state, its producer, and the shell-side mirror consumer)
|
|
1944
|
+
|
|
1372
1945
|
/**
|
|
1373
1946
|
* This service observe changes in the document's body height.
|
|
1374
1947
|
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
@@ -1409,10 +1982,10 @@ class ResizeService {
|
|
|
1409
1982
|
});
|
|
1410
1983
|
afterNextRender(() => this.resizeObserver?.observe(document.body));
|
|
1411
1984
|
}
|
|
1412
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1413
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1985
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1986
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResizeService, providedIn: 'root' }); }
|
|
1414
1987
|
}
|
|
1415
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1988
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResizeService, decorators: [{
|
|
1416
1989
|
type: Injectable,
|
|
1417
1990
|
args: [{
|
|
1418
1991
|
providedIn: 'root'
|
|
@@ -1422,11 +1995,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
1422
1995
|
/**
|
|
1423
1996
|
* A service that handles theme messages and applies the received theme.
|
|
1424
1997
|
*/
|
|
1425
|
-
class ThemeConsumerService {
|
|
1998
|
+
class ThemeConsumerService extends AbstractMessageConsumer {
|
|
1426
1999
|
constructor() {
|
|
2000
|
+
super();
|
|
1427
2001
|
this.domSanitizer = inject(DomSanitizer);
|
|
1428
|
-
this.consumerManagerService = inject(ConsumerManagerService);
|
|
1429
|
-
this.logger = inject(LoggerService);
|
|
1430
2002
|
/**
|
|
1431
2003
|
* The type of messages this service handles ('theme').
|
|
1432
2004
|
*/
|
|
@@ -1453,25 +2025,16 @@ class ThemeConsumerService {
|
|
|
1453
2025
|
}
|
|
1454
2026
|
}
|
|
1455
2027
|
};
|
|
2028
|
+
/**
|
|
2029
|
+
* Auto-starts the consumer on creation.
|
|
2030
|
+
* @deprecated The constructor auto-starts the consumer for backwards compatibility. It will be removed in v15;
|
|
2031
|
+
*/
|
|
1456
2032
|
this.start();
|
|
1457
|
-
inject(DestroyRef).onDestroy(() => this.stop());
|
|
1458
|
-
}
|
|
1459
|
-
/**
|
|
1460
|
-
* Starts the theme handler service by registering it into the consumer manager service.
|
|
1461
|
-
*/
|
|
1462
|
-
start() {
|
|
1463
|
-
this.consumerManagerService.register(this);
|
|
1464
|
-
}
|
|
1465
|
-
/**
|
|
1466
|
-
* Stops the theme handler service by unregistering it from the consumer manager service.
|
|
1467
|
-
*/
|
|
1468
|
-
stop() {
|
|
1469
|
-
this.consumerManagerService.unregister(this);
|
|
1470
2033
|
}
|
|
1471
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1472
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
2034
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ThemeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2035
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ThemeConsumerService, providedIn: 'root' }); }
|
|
1473
2036
|
}
|
|
1474
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2037
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ThemeConsumerService, decorators: [{
|
|
1475
2038
|
type: Injectable,
|
|
1476
2039
|
args: [{
|
|
1477
2040
|
providedIn: 'root'
|
|
@@ -1644,10 +2207,10 @@ class IframeActivityTrackerService {
|
|
|
1644
2207
|
this.stopActivityInterval();
|
|
1645
2208
|
this.config = undefined;
|
|
1646
2209
|
}
|
|
1647
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1648
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
2210
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: IframeActivityTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2211
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: IframeActivityTrackerService, providedIn: 'root' }); }
|
|
1649
2212
|
}
|
|
1650
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2213
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: IframeActivityTrackerService, decorators: [{
|
|
1651
2214
|
type: Injectable,
|
|
1652
2215
|
args: [{
|
|
1653
2216
|
providedIn: 'root'
|
|
@@ -1824,10 +2387,10 @@ class ActivityProducerService {
|
|
|
1824
2387
|
this.lastEmitTimestamps.clear();
|
|
1825
2388
|
this.iframeActivityTracker.stop();
|
|
1826
2389
|
}
|
|
1827
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1828
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
2390
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActivityProducerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2391
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActivityProducerService, providedIn: 'root' }); }
|
|
1829
2392
|
}
|
|
1830
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2393
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActivityProducerService, decorators: [{
|
|
1831
2394
|
type: Injectable,
|
|
1832
2395
|
args: [{
|
|
1833
2396
|
providedIn: 'root'
|
|
@@ -1880,10 +2443,10 @@ class ActivityConsumerService {
|
|
|
1880
2443
|
stop() {
|
|
1881
2444
|
this.consumerManagerService.unregister(this);
|
|
1882
2445
|
}
|
|
1883
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1884
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
2446
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActivityConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2447
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActivityConsumerService, providedIn: 'root' }); }
|
|
1885
2448
|
}
|
|
1886
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2449
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActivityConsumerService, decorators: [{
|
|
1887
2450
|
type: Injectable,
|
|
1888
2451
|
args: [{
|
|
1889
2452
|
providedIn: 'root'
|
|
@@ -1894,5 +2457,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
1894
2457
|
* Generated bundle index. Do not edit.
|
|
1895
2458
|
*/
|
|
1896
2459
|
|
|
1897
|
-
export { ACTIVITY_EVENTS, ActivityConsumerService, ActivityProducerService, ApplyTheme, ConnectDirective, ConsumerManagerService, DEFAULT_ACTIVITY_PRODUCER_CONFIG, ERROR_MESSAGE_TYPE, HIGH_FREQUENCY_EVENTS, HistoryConsumerService, HostInfoPipe, IFRAME_INTERACTION_EVENT, IframeActivityTrackerService, IframeEmbedComponent, KNOWN_MESSAGES, MFE_HOST_APPLICATION_ID_PARAM, MFE_HOST_URL_PARAM, MFE_MODULE_APPLICATION_ID_PARAM, NavigationConsumerService, ProducerManagerService, ResizeConsumerService, ResizeService, RestoreRoute, RouteMemorizeDirective, RouteMemorizeService, RoutingService, ScalableDirective, THEME_QUERY_PARAM_NAME, THEME_URL_SUFFIX, ThemeConsumerService, ThemeProducerService, VISIBILITY_CHANGE_EVENT, applyInitialTheme, applyTheme, downloadApplicationThemeCss, getAvailableConsumers, getDefaultClientEndpointStartOptions, getHostInfo, getStyle, hostQueryParams, isEmbedded, isErrorMessage, isUserActivityMessage, persistHostInfo, provideConnection, provideHistoryOverrides, registerConsumer, registerProducer, sendError };
|
|
2460
|
+
export { ACTIVITY_EVENTS, AbstractMessageConsumer, ActivityConsumerService, ActivityProducerService, ApplyTheme, BrowserConfirmationStrategy, ConnectDirective, ConsumerManagerService, DEFAULT_ACTIVITY_PRODUCER_CONFIG, ERROR_MESSAGE_TYPE, HIGH_FREQUENCY_EVENTS, HistoryConsumerService, HostInfoPipe, IFRAME_INTERACTION_EVENT, IframeActivityTrackerService, IframeEmbedComponent, KNOWN_MESSAGES, MFE_HOST_APPLICATION_ID_PARAM, MFE_HOST_URL_PARAM, MFE_MODULE_APPLICATION_ID_PARAM, NAVIGATION_BLOCK_CONFIRMATION, NAVIGATION_REQUEST_HANDLER, NavigationBlockConfirmationService, NavigationBlockService, NavigationBlockStateConsumerService, NavigationBlockStateProducerService, NavigationConsumerService, NavigationDecisionConsumerService, NavigationRequestConsumerService, NavigationRequestManagerService, ProducerManagerService, ResizeConsumerService, ResizeService, RestoreRoute, RouteMemorizeDirective, RouteMemorizeService, RoutingService, ScalableDirective, THEME_QUERY_PARAM_NAME, THEME_URL_SUFFIX, ThemeConsumerService, ThemeProducerService, VISIBILITY_CHANGE_EVENT, applyInitialTheme, applyTheme, createNavigationRequestHandler, createNavigationRequestShellHandler, downloadApplicationThemeCss, generateCorrelationId, getAvailableConsumers, getDefaultClientEndpointStartOptions, getHostInfo, getStyle, hostQueryParams, isEmbedded, isErrorMessage, isUserActivityMessage, navigationBlockModuleGuard, navigationBlockShellGuard, persistHostInfo, provideConnection, provideDefaultNavigationBlockConfirmation, provideHistoryOverrides, provideNavigationRequestModuleHandler, provideNavigationRequestShellHandler, registerConsumer, registerProducer, sendError };
|
|
1898
2461
|
//# sourceMappingURL=ama-mfe-ng-utils.mjs.map
|