@ama-mfe/ng-utils 14.3.0-prerelease.9 → 14.3.0-rc.4
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
CHANGED
|
@@ -180,6 +180,21 @@ export interface CustomMessageV1_0 extends Message {
|
|
|
180
180
|
export type CustomMessageVersions = CustomMessageV1_0;
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
+
#### Message version compatibility
|
|
184
|
+
The `ConsumerManagerService` dispatches incoming messages with a semver-compatible fallback, applied uniformly to every
|
|
185
|
+
message type in this package (navigation, theme, resize, user-activity, history, and custom messages).
|
|
186
|
+
|
|
187
|
+
Within a major version, a consumer's highest declared minor ≤ the incoming minor, is used. This lets producers add
|
|
188
|
+
optional fields in a new minor version (e.g. `v1.1`) without breaking consumers that only implement `v1.0` — the older
|
|
189
|
+
handler runs and simply ignores the unknown fields.
|
|
190
|
+
|
|
191
|
+
Majors are isolated — no cross-major fallback in either direction:
|
|
192
|
+
- An incoming v2.x will never fall back to a v1.x handler (breaking changes are expected across majors).
|
|
193
|
+
- A consumer declaring only v2.x will not match an incoming v1.x (the consumer is ahead of the producer).
|
|
194
|
+
|
|
195
|
+
Both cross-major cases produce a `version_mismatch` error. Introducing a new major version therefore requires coordinated
|
|
196
|
+
updates on both the producer and consumer sides.
|
|
197
|
+
|
|
183
198
|
#### Consumer
|
|
184
199
|
A consumer should implement the `MessageConsumer` interface and inject the `ConsumeManagerService` which handles the
|
|
185
200
|
registration to the communication protocol.
|
|
@@ -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.10", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
62
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", 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.10", ngImport: i0, type: ConnectDirective, decorators: [{
|
|
65
65
|
type: Directive,
|
|
66
66
|
args: [{
|
|
67
67
|
selector: 'iframe[connect]',
|
|
@@ -143,16 +143,29 @@ 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.10", ngImport: i0, type: ProducerManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
147
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", 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.10", ngImport: i0, type: ProducerManagerService, decorators: [{
|
|
150
150
|
type: Injectable,
|
|
151
151
|
args: [{
|
|
152
152
|
providedIn: 'root'
|
|
153
153
|
}]
|
|
154
154
|
}] });
|
|
155
155
|
|
|
156
|
+
const VERSION_MATCH = /^(\d+)\.(\d+)$/;
|
|
157
|
+
/**
|
|
158
|
+
* Parses a "major.minor" version string. Returns undefined if the string is malformed.
|
|
159
|
+
* @param version
|
|
160
|
+
*/
|
|
161
|
+
const parseVersion = (version) => {
|
|
162
|
+
const match = VERSION_MATCH.exec(version);
|
|
163
|
+
if (!match) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
const [major, minor] = match.slice(1).map(Number);
|
|
167
|
+
return { major, minor, raw: version };
|
|
168
|
+
};
|
|
156
169
|
class ConsumerManagerService {
|
|
157
170
|
constructor() {
|
|
158
171
|
this.messageService = inject(MessagePeerService);
|
|
@@ -162,7 +175,7 @@ class ConsumerManagerService {
|
|
|
162
175
|
/** The list of registered consumers */
|
|
163
176
|
this.consumers = this.registeredConsumers.asReadonly();
|
|
164
177
|
this.messageService.messages$.pipe(takeUntilDestroyed()).subscribe((message) => this.consumeMessage(message));
|
|
165
|
-
// Each time a consumer is registered/unregistered update the list of registered messages
|
|
178
|
+
// Each time a consumer is registered/unregistered update the list of registered messages.
|
|
166
179
|
effect(() => {
|
|
167
180
|
const declareMessages = getAvailableConsumers(this.consumers());
|
|
168
181
|
// registering consumed messages locally for validation
|
|
@@ -202,17 +215,33 @@ class ConsumerManagerService {
|
|
|
202
215
|
this.logger.warn(`No consumer found for message type: ${message.payload.type}`);
|
|
203
216
|
return sendError(this.messageService, { reason: 'unknown_type', source: message.payload });
|
|
204
217
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
// Semver-compatible dispatch: for an incoming v{major}.{minor}, pick each consumer's highest
|
|
219
|
+
// declared v{major}.{n} where n <= minor.
|
|
220
|
+
// Major versions are isolated: no cross-major fallback in either direction. An incoming v2.x
|
|
221
|
+
// will never fall back to a v1.x handler (breaking changes), and a consumer that declares
|
|
222
|
+
// only v2.x will not match an incoming v1.x (the consumer is ahead of the producer). Both
|
|
223
|
+
// result in a version_mismatch error.
|
|
224
|
+
const messageVersion = parseVersion(message.payload.version);
|
|
225
|
+
const resolvedConsumers = messageVersion
|
|
226
|
+
? typeMatchingConsumers
|
|
227
|
+
.map((consumer) => {
|
|
228
|
+
const fallbackVersion = Object.keys(consumer.supportedVersions)
|
|
229
|
+
.map((version) => parseVersion(version))
|
|
230
|
+
.filter((v) => !!v && v.major === messageVersion.major && v.minor <= messageVersion.minor)
|
|
231
|
+
.toSorted((a, b) => b.minor - a.minor)
|
|
232
|
+
.at(0);
|
|
233
|
+
return fallbackVersion && { consumer, version: fallbackVersion.raw };
|
|
234
|
+
})
|
|
235
|
+
.filter((entry) => !!entry)
|
|
236
|
+
: [];
|
|
237
|
+
if (resolvedConsumers.length === 0) {
|
|
209
238
|
this.logger.warn(`No consumer found for message version: ${message.payload.version}`);
|
|
210
239
|
return sendError(this.messageService, { reason: 'version_mismatch', source: message.payload });
|
|
211
240
|
}
|
|
212
|
-
await Promise.all(
|
|
213
|
-
.map(async (consumer) => {
|
|
241
|
+
await Promise.all(resolvedConsumers
|
|
242
|
+
.map(async ({ consumer, version }) => {
|
|
214
243
|
try {
|
|
215
|
-
await consumer.supportedVersions[
|
|
244
|
+
await consumer.supportedVersions[version](message);
|
|
216
245
|
}
|
|
217
246
|
catch (error) {
|
|
218
247
|
this.logger.error('Error while consuming message', error);
|
|
@@ -238,10 +267,10 @@ class ConsumerManagerService {
|
|
|
238
267
|
return consumers.filter((c) => c !== consumer);
|
|
239
268
|
});
|
|
240
269
|
}
|
|
241
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
242
|
-
/** @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.10", ngImport: i0, type: ConsumerManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
271
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConsumerManagerService, providedIn: 'root' }); }
|
|
243
272
|
}
|
|
244
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
273
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConsumerManagerService, decorators: [{
|
|
245
274
|
type: Injectable,
|
|
246
275
|
args: [{
|
|
247
276
|
providedIn: 'root'
|
|
@@ -312,10 +341,10 @@ class HistoryConsumerService {
|
|
|
312
341
|
stop() {
|
|
313
342
|
this.consumerManagerService.unregister(this);
|
|
314
343
|
}
|
|
315
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
316
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
344
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HistoryConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
345
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HistoryConsumerService, providedIn: 'root' }); }
|
|
317
346
|
}
|
|
318
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
347
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HistoryConsumerService, decorators: [{
|
|
319
348
|
type: Injectable,
|
|
320
349
|
args: [{
|
|
321
350
|
providedIn: 'root'
|
|
@@ -431,10 +460,10 @@ class HostInfoPipe {
|
|
|
431
460
|
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
432
461
|
}
|
|
433
462
|
}
|
|
434
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
435
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.
|
|
463
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HostInfoPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
464
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.10", ngImport: i0, type: HostInfoPipe, isStandalone: true, name: "hostInfo" }); }
|
|
436
465
|
}
|
|
437
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HostInfoPipe, decorators: [{
|
|
438
467
|
type: Pipe,
|
|
439
468
|
args: [{
|
|
440
469
|
name: 'hostInfo'
|
|
@@ -557,10 +586,10 @@ class ResizeConsumerService {
|
|
|
557
586
|
stop() {
|
|
558
587
|
this.consumerManagerService.unregister(this);
|
|
559
588
|
}
|
|
560
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
561
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
589
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
590
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
|
|
562
591
|
}
|
|
563
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
592
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResizeConsumerService, decorators: [{
|
|
564
593
|
type: Injectable,
|
|
565
594
|
args: [{
|
|
566
595
|
providedIn: 'root'
|
|
@@ -694,10 +723,10 @@ class ScalableDirective {
|
|
|
694
723
|
this.renderer.setStyle(this.elem.nativeElement, 'min-height', `${height}px`);
|
|
695
724
|
}
|
|
696
725
|
}
|
|
697
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
698
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
726
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ScalableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
727
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", 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 }); }
|
|
699
728
|
}
|
|
700
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
729
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ScalableDirective, decorators: [{
|
|
701
730
|
type: Directive,
|
|
702
731
|
args: [{
|
|
703
732
|
selector: '[scalable]',
|
|
@@ -855,10 +884,10 @@ class ThemeProducerService {
|
|
|
855
884
|
this.logger.error('Error in theme service message', message);
|
|
856
885
|
this.revertToPreviousTheme();
|
|
857
886
|
}
|
|
858
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
859
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
887
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ThemeProducerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
888
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ThemeProducerService, providedIn: 'root' }); }
|
|
860
889
|
}
|
|
861
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
890
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ThemeProducerService, decorators: [{
|
|
862
891
|
type: Injectable,
|
|
863
892
|
args: [{
|
|
864
893
|
providedIn: 'root'
|
|
@@ -891,10 +920,10 @@ class ApplyTheme {
|
|
|
891
920
|
}
|
|
892
921
|
return undefined;
|
|
893
922
|
}
|
|
894
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
895
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.
|
|
923
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ApplyTheme, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
924
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.10", ngImport: i0, type: ApplyTheme, isStandalone: true, name: "applyTheme" }); }
|
|
896
925
|
}
|
|
897
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
926
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ApplyTheme, decorators: [{
|
|
898
927
|
type: Pipe,
|
|
899
928
|
args: [{
|
|
900
929
|
name: 'applyTheme'
|
|
@@ -923,10 +952,10 @@ class SandboxDirective {
|
|
|
923
952
|
ngOnInit() {
|
|
924
953
|
this.renderer.setAttribute(this.elementRef.nativeElement, 'sandbox', this.mfeSandbox());
|
|
925
954
|
}
|
|
926
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
927
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
955
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SandboxDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
956
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: SandboxDirective, isStandalone: true, selector: "iframe[mfeSandbox]", inputs: { mfeSandbox: { classPropertyName: "mfeSandbox", publicName: "mfeSandbox", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
928
957
|
}
|
|
929
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
958
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SandboxDirective, decorators: [{
|
|
930
959
|
type: Directive,
|
|
931
960
|
args: [{
|
|
932
961
|
selector: 'iframe[mfeSandbox]'
|
|
@@ -959,10 +988,10 @@ class IframeEmbedComponent {
|
|
|
959
988
|
*/
|
|
960
989
|
this.sandbox = input('allow-scripts allow-same-origin', ...(ngDevMode ? [{ debugName: "sandbox" }] : /* istanbul ignore next */ []));
|
|
961
990
|
}
|
|
962
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
963
|
-
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
991
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IframeEmbedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
992
|
+
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.10", 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 }); }
|
|
964
993
|
}
|
|
965
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
994
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IframeEmbedComponent, decorators: [{
|
|
966
995
|
type: Component,
|
|
967
996
|
args: [{ selector: 'mfe-iframe-embed', imports: [
|
|
968
997
|
ConnectDirective,
|
|
@@ -1004,6 +1033,17 @@ class NavigationConsumerService {
|
|
|
1004
1033
|
const channelId = message.from || undefined;
|
|
1005
1034
|
this.requestedUrl.next({ url: message.payload.url, channelId });
|
|
1006
1035
|
this.navigate(message.payload.url);
|
|
1036
|
+
},
|
|
1037
|
+
/**
|
|
1038
|
+
* Same as 1.0 but applies the navigation extras (e.g. replaceUrl) forwarded by the producer
|
|
1039
|
+
* so the host router reproduces the original history semantics.
|
|
1040
|
+
* @param message message to consume
|
|
1041
|
+
*/
|
|
1042
|
+
// eslint-disable-next-line @stylistic/quote-props -- keep quotes for consistency with '1.0'
|
|
1043
|
+
'1.1': (message) => {
|
|
1044
|
+
const channelId = message.from || undefined;
|
|
1045
|
+
this.requestedUrl.next({ url: message.payload.url, channelId });
|
|
1046
|
+
this.navigate(message.payload.url, message.payload.extras);
|
|
1007
1047
|
}
|
|
1008
1048
|
};
|
|
1009
1049
|
this.consumerManagerService = inject(ConsumerManagerService);
|
|
@@ -1024,12 +1064,17 @@ class NavigationConsumerService {
|
|
|
1024
1064
|
/**
|
|
1025
1065
|
* Navigates to the specified URL.
|
|
1026
1066
|
* @param url - The URL to navigate to.
|
|
1067
|
+
* @param extras - Optional navigation extras forwarded from the embedded application.
|
|
1027
1068
|
*/
|
|
1028
|
-
navigate(url) {
|
|
1069
|
+
navigate(url, extras) {
|
|
1029
1070
|
const { paths, queryParams } = this.parseUrl(url);
|
|
1030
1071
|
// No need to keep these in the URL
|
|
1031
1072
|
hostQueryParams.forEach((key) => delete queryParams[key]);
|
|
1032
|
-
void this.router.navigate(paths, {
|
|
1073
|
+
void this.router.navigate(paths, {
|
|
1074
|
+
relativeTo: this.activeRoute.children.at(-1),
|
|
1075
|
+
queryParams,
|
|
1076
|
+
replaceUrl: extras?.replaceUrl
|
|
1077
|
+
});
|
|
1033
1078
|
}
|
|
1034
1079
|
/**
|
|
1035
1080
|
* @inheritdoc
|
|
@@ -1043,10 +1088,10 @@ class NavigationConsumerService {
|
|
|
1043
1088
|
stop() {
|
|
1044
1089
|
this.consumerManagerService.unregister(this);
|
|
1045
1090
|
}
|
|
1046
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1047
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1091
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NavigationConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1092
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NavigationConsumerService, providedIn: 'root' }); }
|
|
1048
1093
|
}
|
|
1049
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1094
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NavigationConsumerService, decorators: [{
|
|
1050
1095
|
type: Injectable,
|
|
1051
1096
|
args: [{
|
|
1052
1097
|
providedIn: 'root'
|
|
@@ -1089,10 +1134,10 @@ class RouteMemorizeService {
|
|
|
1089
1134
|
getRoute(channelId) {
|
|
1090
1135
|
return this.routeStack[channelId];
|
|
1091
1136
|
}
|
|
1092
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1093
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1137
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RouteMemorizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1138
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RouteMemorizeService, providedIn: 'root' }); }
|
|
1094
1139
|
}
|
|
1095
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1140
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RouteMemorizeService, decorators: [{
|
|
1096
1141
|
type: Injectable,
|
|
1097
1142
|
args: [{
|
|
1098
1143
|
providedIn: 'root'
|
|
@@ -1140,10 +1185,10 @@ class RestoreRoute {
|
|
|
1140
1185
|
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
1141
1186
|
}
|
|
1142
1187
|
}
|
|
1143
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1144
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.
|
|
1188
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RestoreRoute, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1189
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.10", ngImport: i0, type: RestoreRoute, isStandalone: true, name: "restoreRoute" }); }
|
|
1145
1190
|
}
|
|
1146
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1191
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RestoreRoute, decorators: [{
|
|
1147
1192
|
type: Pipe,
|
|
1148
1193
|
args: [{
|
|
1149
1194
|
name: 'restoreRoute'
|
|
@@ -1197,10 +1242,10 @@ class RouteMemorizeDirective {
|
|
|
1197
1242
|
}
|
|
1198
1243
|
});
|
|
1199
1244
|
}
|
|
1200
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1201
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
1245
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RouteMemorizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1246
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", 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 }); }
|
|
1202
1247
|
}
|
|
1203
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1248
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RouteMemorizeDirective, decorators: [{
|
|
1204
1249
|
type: Directive,
|
|
1205
1250
|
args: [{
|
|
1206
1251
|
selector: 'iframe[memorizeRoute]',
|
|
@@ -1237,6 +1282,14 @@ class RoutingService {
|
|
|
1237
1282
|
'1.0': async (message) => {
|
|
1238
1283
|
// Navigation has been triggered from the communication protocol request.
|
|
1239
1284
|
await this.router.navigateByUrl(message.payload.url, { state: { triggeredByMessage: true } });
|
|
1285
|
+
},
|
|
1286
|
+
// eslint-disable-next-line @stylistic/quote-props -- keep quotes for consistency with '1.0'
|
|
1287
|
+
'1.1': async (message) => {
|
|
1288
|
+
// Navigation has been triggered from the communication protocol request.
|
|
1289
|
+
await this.router.navigateByUrl(message.payload.url, {
|
|
1290
|
+
state: { triggeredByMessage: true },
|
|
1291
|
+
replaceUrl: message.payload.extras?.replaceUrl
|
|
1292
|
+
});
|
|
1240
1293
|
}
|
|
1241
1294
|
};
|
|
1242
1295
|
registerProducer(this);
|
|
@@ -1269,38 +1322,47 @@ class RoutingService {
|
|
|
1269
1322
|
// Navigation triggered by the application host, no need to request it to navigate to the same route
|
|
1270
1323
|
return !extras.skipLocationChange && !extras.state?.triggeredByMessage;
|
|
1271
1324
|
}), map(({ urlAfterRedirects }) => {
|
|
1272
|
-
const
|
|
1325
|
+
const extras = this.router.getCurrentNavigation()?.extras || {};
|
|
1326
|
+
const { channelId } = extras.state || {};
|
|
1273
1327
|
const currentRouteRegExp = subRouteOnly && this.activatedRoute.routeConfig?.path && new RegExp('^' + this.activatedRoute.routeConfig.path.replace(/(?=\W)/g, '\\'), 'i');
|
|
1274
|
-
return ({
|
|
1275
|
-
|
|
1276
|
-
|
|
1328
|
+
return ({
|
|
1329
|
+
url: currentRouteRegExp ? urlAfterRedirects.replace(currentRouteRegExp, '') : urlAfterRedirects,
|
|
1330
|
+
channelId,
|
|
1331
|
+
replaceUrl: extras.replaceUrl
|
|
1332
|
+
});
|
|
1333
|
+
})).subscribe(({ url, channelId, replaceUrl }) => {
|
|
1334
|
+
// Always emit the latest version we produce. The ConsumerManagerService dispatches to the
|
|
1335
|
+
// highest compatible minor a consumer has declared, so v1.0-only peers still navigate
|
|
1336
|
+
// correctly — they just ignore the optional extras.
|
|
1337
|
+
const message = {
|
|
1277
1338
|
type: 'navigation',
|
|
1278
|
-
version: '1.
|
|
1279
|
-
url
|
|
1339
|
+
version: '1.1',
|
|
1340
|
+
url,
|
|
1341
|
+
...(replaceUrl ? { extras: { replaceUrl: true } } : {})
|
|
1280
1342
|
};
|
|
1281
1343
|
// TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
|
|
1344
|
+
// When multiple majors are supported, sendBest should receive the latest minor of each
|
|
1345
|
+
// supported major and dispatch to peers according to their declared compatibility.
|
|
1282
1346
|
if (isEmbedded(this.window)) {
|
|
1283
|
-
this.messageService.send(
|
|
1347
|
+
this.messageService.send(message);
|
|
1348
|
+
}
|
|
1349
|
+
else if (channelId === undefined) {
|
|
1350
|
+
this.logger.warn('No channelId provided for navigation message');
|
|
1284
1351
|
}
|
|
1285
1352
|
else {
|
|
1286
|
-
|
|
1287
|
-
this.
|
|
1353
|
+
try {
|
|
1354
|
+
this.messageService.send(message, { to: [channelId] });
|
|
1288
1355
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
this.messageService.send(messageV10, { to: [channelId] });
|
|
1292
|
-
}
|
|
1293
|
-
catch (error) {
|
|
1294
|
-
this.logger.error('Error sending navigation message', error);
|
|
1295
|
-
}
|
|
1356
|
+
catch (error) {
|
|
1357
|
+
this.logger.error('Error sending navigation message', error);
|
|
1296
1358
|
}
|
|
1297
1359
|
}
|
|
1298
1360
|
});
|
|
1299
1361
|
}
|
|
1300
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1301
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1362
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RoutingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1363
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RoutingService, providedIn: 'root' }); }
|
|
1302
1364
|
}
|
|
1303
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1365
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RoutingService, decorators: [{
|
|
1304
1366
|
type: Injectable,
|
|
1305
1367
|
args: [{
|
|
1306
1368
|
providedIn: 'root'
|
|
@@ -1347,10 +1409,10 @@ class ResizeService {
|
|
|
1347
1409
|
});
|
|
1348
1410
|
afterNextRender(() => this.resizeObserver?.observe(document.body));
|
|
1349
1411
|
}
|
|
1350
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1351
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1412
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1413
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResizeService, providedIn: 'root' }); }
|
|
1352
1414
|
}
|
|
1353
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1415
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResizeService, decorators: [{
|
|
1354
1416
|
type: Injectable,
|
|
1355
1417
|
args: [{
|
|
1356
1418
|
providedIn: 'root'
|
|
@@ -1406,10 +1468,10 @@ class ThemeConsumerService {
|
|
|
1406
1468
|
stop() {
|
|
1407
1469
|
this.consumerManagerService.unregister(this);
|
|
1408
1470
|
}
|
|
1409
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1410
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1471
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ThemeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1472
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ThemeConsumerService, providedIn: 'root' }); }
|
|
1411
1473
|
}
|
|
1412
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1474
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ThemeConsumerService, decorators: [{
|
|
1413
1475
|
type: Injectable,
|
|
1414
1476
|
args: [{
|
|
1415
1477
|
providedIn: 'root'
|
|
@@ -1582,10 +1644,10 @@ class IframeActivityTrackerService {
|
|
|
1582
1644
|
this.stopActivityInterval();
|
|
1583
1645
|
this.config = undefined;
|
|
1584
1646
|
}
|
|
1585
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1586
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1647
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IframeActivityTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1648
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IframeActivityTrackerService, providedIn: 'root' }); }
|
|
1587
1649
|
}
|
|
1588
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1650
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IframeActivityTrackerService, decorators: [{
|
|
1589
1651
|
type: Injectable,
|
|
1590
1652
|
args: [{
|
|
1591
1653
|
providedIn: 'root'
|
|
@@ -1762,10 +1824,10 @@ class ActivityProducerService {
|
|
|
1762
1824
|
this.lastEmitTimestamps.clear();
|
|
1763
1825
|
this.iframeActivityTracker.stop();
|
|
1764
1826
|
}
|
|
1765
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1766
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1827
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ActivityProducerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1828
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ActivityProducerService, providedIn: 'root' }); }
|
|
1767
1829
|
}
|
|
1768
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1830
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ActivityProducerService, decorators: [{
|
|
1769
1831
|
type: Injectable,
|
|
1770
1832
|
args: [{
|
|
1771
1833
|
providedIn: 'root'
|
|
@@ -1818,10 +1880,10 @@ class ActivityConsumerService {
|
|
|
1818
1880
|
stop() {
|
|
1819
1881
|
this.consumerManagerService.unregister(this);
|
|
1820
1882
|
}
|
|
1821
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1822
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1883
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ActivityConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1884
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ActivityConsumerService, providedIn: 'root' }); }
|
|
1823
1885
|
}
|
|
1824
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1886
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ActivityConsumerService, decorators: [{
|
|
1825
1887
|
type: Injectable,
|
|
1826
1888
|
args: [{
|
|
1827
1889
|
providedIn: 'root'
|