@ama-mfe/ng-utils 14.0.0-next.2 → 14.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/fesm2022/ama-mfe-ng-utils.mjs +424 -346
- package/fesm2022/ama-mfe-ng-utils.mjs.map +1 -1
- package/index.d.ts +256 -210
- package/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/schematics/ng-add/index.js.map +0 -1
- package/schematics/ng-add/schema.js.map +0 -1
|
@@ -1,13 +1,76 @@
|
|
|
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 {
|
|
5
|
-
import {
|
|
4
|
+
import { input, inject, ElementRef, computed, SecurityContext, effect, HostBinding, Directive, Injectable, signal, DestroyRef, provideAppInitializer, Pipe, makeEnvironmentProviders, untracked, afterNextRender, Renderer2 } from '@angular/core';
|
|
5
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
6
6
|
import { LoggerService } from '@o3r/logger';
|
|
7
|
-
import {
|
|
7
|
+
import { HISTORY_MESSAGE_TYPE, NAVIGATION_MESSAGE_TYPE, RESIZE_MESSAGE_TYPE, THEME_MESSAGE_TYPE } from '@ama-mfe/messages';
|
|
8
|
+
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
8
9
|
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
|
|
9
10
|
import { Subject, filter, map } from 'rxjs';
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
class ConnectDirective {
|
|
13
|
+
/**
|
|
14
|
+
* Binds the `src` attribute of the iframe to the sanitized source URL.
|
|
15
|
+
*/
|
|
16
|
+
get srcAttr() {
|
|
17
|
+
return this.src();
|
|
18
|
+
}
|
|
19
|
+
constructor() {
|
|
20
|
+
/**
|
|
21
|
+
* The connection ID required for the message peer service.
|
|
22
|
+
*/
|
|
23
|
+
this.connect = input.required(...(ngDevMode ? [{ debugName: "connect" }] : []));
|
|
24
|
+
/**
|
|
25
|
+
* The sanitized source URL for the iframe.
|
|
26
|
+
*/
|
|
27
|
+
this.src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
|
|
28
|
+
this.messageService = inject(MessagePeerService);
|
|
29
|
+
this.domSanitizer = inject(DomSanitizer);
|
|
30
|
+
this.iframeElement = inject(ElementRef).nativeElement;
|
|
31
|
+
this.clientOrigin = computed(() => {
|
|
32
|
+
const src = this.src();
|
|
33
|
+
const srcString = src && this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, src);
|
|
34
|
+
return srcString && new URL(srcString).origin;
|
|
35
|
+
}, ...(ngDevMode ? [{ debugName: "clientOrigin" }] : []));
|
|
36
|
+
const logger = inject(LoggerService);
|
|
37
|
+
// When the origin or connection ID change - reconnect the message service
|
|
38
|
+
effect((onCleanup) => {
|
|
39
|
+
let stopHandshakeListening = () => { };
|
|
40
|
+
const origin = this.clientOrigin();
|
|
41
|
+
const id = this.connect();
|
|
42
|
+
const source = this.iframeElement.contentWindow;
|
|
43
|
+
// listen for handshakes only if we know the origin and were given a connection ID
|
|
44
|
+
if (origin && source && id) {
|
|
45
|
+
try {
|
|
46
|
+
stopHandshakeListening = this.messageService.listen({ id, source, origin });
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
logger.error(`Failed to start listening for (connection ID: ${id})`, e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// stop listening for handshakes and disconnect previous connection when:
|
|
53
|
+
// - origin/connection ID change
|
|
54
|
+
// - the directive is destroyed
|
|
55
|
+
onCleanup(() => {
|
|
56
|
+
stopHandshakeListening();
|
|
57
|
+
this.messageService.disconnect();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
62
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.13", 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
|
+
}
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ConnectDirective, decorators: [{
|
|
65
|
+
type: Directive,
|
|
66
|
+
args: [{
|
|
67
|
+
selector: 'iframe[connect]',
|
|
68
|
+
standalone: true
|
|
69
|
+
}]
|
|
70
|
+
}], ctorParameters: () => [], propDecorators: { connect: [{ type: i0.Input, args: [{ isSignal: true, alias: "connect", required: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], srcAttr: [{
|
|
71
|
+
type: HostBinding,
|
|
72
|
+
args: ['src']
|
|
73
|
+
}] } });
|
|
11
74
|
|
|
12
75
|
/**
|
|
13
76
|
* Gets the available consumers and formats them into a {@see DeclareMessages} object.
|
|
@@ -74,14 +137,16 @@ class ProducerManagerService {
|
|
|
74
137
|
.filter(({ types }) => (Array.isArray(types) ? types : [types]).includes(message.source.type));
|
|
75
138
|
const handlersPresent = handlers.length > 0;
|
|
76
139
|
if (handlersPresent) {
|
|
77
|
-
await Promise.all(
|
|
140
|
+
await Promise.all(
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/await-thenable -- `handleError` can return void or Promise<void>
|
|
142
|
+
handlers.map((handler) => handler.handleError(message)));
|
|
78
143
|
}
|
|
79
144
|
return handlersPresent;
|
|
80
145
|
}
|
|
81
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
82
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
146
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ProducerManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
147
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ProducerManagerService, providedIn: 'root' }); }
|
|
83
148
|
}
|
|
84
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
149
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ProducerManagerService, decorators: [{
|
|
85
150
|
type: Injectable,
|
|
86
151
|
args: [{
|
|
87
152
|
providedIn: 'root'
|
|
@@ -173,10 +238,10 @@ class ConsumerManagerService {
|
|
|
173
238
|
return consumers.filter((c) => c !== consumer);
|
|
174
239
|
});
|
|
175
240
|
}
|
|
176
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
177
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
241
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ConsumerManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
242
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ConsumerManagerService, providedIn: 'root' }); }
|
|
178
243
|
}
|
|
179
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
244
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ConsumerManagerService, decorators: [{
|
|
180
245
|
type: Injectable,
|
|
181
246
|
args: [{
|
|
182
247
|
providedIn: 'root'
|
|
@@ -208,53 +273,49 @@ const registerConsumer = (consumer) => {
|
|
|
208
273
|
});
|
|
209
274
|
};
|
|
210
275
|
|
|
211
|
-
/** the error message type */
|
|
212
|
-
const ERROR_MESSAGE_TYPE = 'error';
|
|
213
|
-
|
|
214
276
|
/**
|
|
215
|
-
*
|
|
277
|
+
* A service that handles history messages.
|
|
278
|
+
*
|
|
279
|
+
* This service listens for history messages and navigates accordingly.
|
|
216
280
|
*/
|
|
217
|
-
class
|
|
281
|
+
class HistoryConsumerService {
|
|
218
282
|
constructor() {
|
|
219
|
-
this.newHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "newHeight" }] : []));
|
|
220
|
-
/**
|
|
221
|
-
* A readonly signal that provides the new height information from the channel.
|
|
222
|
-
*/
|
|
223
|
-
this.newHeightFromChannel = this.newHeight.asReadonly();
|
|
224
283
|
/**
|
|
225
|
-
* The type of messages this service handles
|
|
284
|
+
* The type of messages this service handles.
|
|
226
285
|
*/
|
|
227
|
-
this.type =
|
|
286
|
+
this.type = HISTORY_MESSAGE_TYPE;
|
|
228
287
|
/**
|
|
229
|
-
*
|
|
288
|
+
* @inheritdoc
|
|
230
289
|
*/
|
|
231
290
|
this.supportedVersions = {
|
|
232
291
|
/**
|
|
233
|
-
* Use the message
|
|
292
|
+
* Use the message payload to navigate in the history
|
|
234
293
|
* @param message message to consume
|
|
235
294
|
*/
|
|
236
|
-
'1.0': (message) =>
|
|
295
|
+
'1.0': (message) => {
|
|
296
|
+
history.go(message.payload.delta);
|
|
297
|
+
}
|
|
237
298
|
};
|
|
238
299
|
this.consumerManagerService = inject(ConsumerManagerService);
|
|
239
300
|
this.start();
|
|
240
301
|
inject(DestroyRef).onDestroy(() => this.stop());
|
|
241
302
|
}
|
|
242
303
|
/**
|
|
243
|
-
*
|
|
304
|
+
* @inheritdoc
|
|
244
305
|
*/
|
|
245
306
|
start() {
|
|
246
307
|
this.consumerManagerService.register(this);
|
|
247
308
|
}
|
|
248
309
|
/**
|
|
249
|
-
*
|
|
310
|
+
* @inheritdoc
|
|
250
311
|
*/
|
|
251
312
|
stop() {
|
|
252
313
|
this.consumerManagerService.unregister(this);
|
|
253
314
|
}
|
|
254
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
255
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
315
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: HistoryConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
316
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: HistoryConsumerService, providedIn: 'root' }); }
|
|
256
317
|
}
|
|
257
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: HistoryConsumerService, decorators: [{
|
|
258
319
|
type: Injectable,
|
|
259
320
|
args: [{
|
|
260
321
|
providedIn: 'root'
|
|
@@ -262,102 +323,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImpor
|
|
|
262
323
|
}], ctorParameters: () => [] });
|
|
263
324
|
|
|
264
325
|
/**
|
|
265
|
-
*
|
|
266
|
-
*
|
|
326
|
+
* Provides necessary overrides to make the module navigation in history work in an embedded context :
|
|
327
|
+
* - Prevent pushing states to history, replace state instead
|
|
328
|
+
* - Handle history navigation via History messages to let the host manage the states
|
|
267
329
|
*/
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* This method sets up a `ResizeObserver` to observe changes in the document's body height.
|
|
286
|
-
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
287
|
-
*/
|
|
288
|
-
startResizeObserver() {
|
|
289
|
-
this.resizeObserver = new ResizeObserver(() => {
|
|
290
|
-
const newHeight = document.body.getBoundingClientRect().height;
|
|
291
|
-
if (!this.actualHeight || newHeight !== this.actualHeight) {
|
|
292
|
-
this.actualHeight = newHeight;
|
|
293
|
-
const messageV10 = {
|
|
294
|
-
type: 'resize',
|
|
295
|
-
version: '1.0',
|
|
296
|
-
height: this.actualHeight
|
|
297
|
-
};
|
|
298
|
-
// TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
|
|
299
|
-
this.messageService.send(messageV10);
|
|
300
|
-
}
|
|
330
|
+
function provideHistoryOverrides() {
|
|
331
|
+
return provideAppInitializer(() => {
|
|
332
|
+
const messageService = inject((MessagePeerService));
|
|
333
|
+
const navigate = (delta) => {
|
|
334
|
+
messageService.send({
|
|
335
|
+
type: 'history',
|
|
336
|
+
version: '1.0',
|
|
337
|
+
delta
|
|
338
|
+
});
|
|
339
|
+
};
|
|
340
|
+
Object.defineProperty(history, 'pushState', {
|
|
341
|
+
value: (data, unused, url) => {
|
|
342
|
+
history.replaceState(data, unused, url);
|
|
343
|
+
},
|
|
344
|
+
writable: false,
|
|
345
|
+
configurable: false
|
|
301
346
|
});
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: ResizeService, decorators: [{
|
|
308
|
-
type: Injectable,
|
|
309
|
-
args: [{
|
|
310
|
-
providedIn: 'root'
|
|
311
|
-
}]
|
|
312
|
-
}], ctorParameters: () => [] });
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* A directive that adjusts the height of an element based on resize messages from a specified channel.
|
|
316
|
-
*/
|
|
317
|
-
class ScalableDirective {
|
|
318
|
-
constructor() {
|
|
319
|
-
/**
|
|
320
|
-
* The connection ID for the element, used as channel id backup
|
|
321
|
-
*/
|
|
322
|
-
this.connect = input(...(ngDevMode ? [undefined, { debugName: "connect" }] : []));
|
|
323
|
-
/**
|
|
324
|
-
* The channel id
|
|
325
|
-
*/
|
|
326
|
-
this.scalable = input(...(ngDevMode ? [undefined, { debugName: "scalable" }] : []));
|
|
327
|
-
this.resizeHandler = inject(ResizeConsumerService);
|
|
328
|
-
/**
|
|
329
|
-
* This signal checks if the current channel requesting the resize matches the channel ID from the resize handler.
|
|
330
|
-
* If they match, it returns the new height information; otherwise, it returns undefined.
|
|
331
|
-
*/
|
|
332
|
-
this.newHeightFromChannel = computed(() => {
|
|
333
|
-
const channelAskingResize = this.scalable() || this.connect();
|
|
334
|
-
const newHeightFromChannel = this.resizeHandler.newHeightFromChannel();
|
|
335
|
-
if (channelAskingResize && newHeightFromChannel?.channelId === channelAskingResize) {
|
|
336
|
-
return newHeightFromChannel;
|
|
337
|
-
}
|
|
338
|
-
return undefined;
|
|
339
|
-
}, ...(ngDevMode ? [{ debugName: "newHeightFromChannel" }] : []));
|
|
340
|
-
const elem = inject(ElementRef);
|
|
341
|
-
const renderer = inject(Renderer2);
|
|
342
|
-
this.resizeHandler.start();
|
|
343
|
-
/** When a new height value is received set the height of the host element (in pixels) */
|
|
344
|
-
effect(() => {
|
|
345
|
-
const newHeightFromChannel = this.newHeightFromChannel();
|
|
346
|
-
if (newHeightFromChannel) {
|
|
347
|
-
renderer.setStyle(elem.nativeElement, 'height', `${newHeightFromChannel.height}px`);
|
|
348
|
-
}
|
|
347
|
+
Object.defineProperty(history, 'back', {
|
|
348
|
+
value: () => navigate(-1),
|
|
349
|
+
writable: false,
|
|
350
|
+
configurable: false
|
|
349
351
|
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
352
|
+
Object.defineProperty(history, 'forward', {
|
|
353
|
+
value: () => navigate(1),
|
|
354
|
+
writable: false,
|
|
355
|
+
configurable: false
|
|
356
|
+
});
|
|
357
|
+
Object.defineProperty(history, 'go', {
|
|
358
|
+
value: (delta) => navigate(delta),
|
|
359
|
+
writable: false,
|
|
360
|
+
configurable: false
|
|
361
|
+
});
|
|
362
|
+
});
|
|
353
363
|
}
|
|
354
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: ScalableDirective, decorators: [{
|
|
355
|
-
type: Directive,
|
|
356
|
-
args: [{
|
|
357
|
-
selector: '[scalable]',
|
|
358
|
-
standalone: true
|
|
359
|
-
}]
|
|
360
|
-
}], ctorParameters: () => [] });
|
|
361
364
|
|
|
362
365
|
const SESSION_STORAGE_KEY = 'ama-mfe-host-info';
|
|
363
366
|
/**
|
|
@@ -384,12 +387,13 @@ const hostQueryParams = [MFE_HOST_URL_PARAM, MFE_HOST_APPLICATION_ID_PARAM, MFE_
|
|
|
384
387
|
* - use `document.referrer` (will only work if called before any redirection in the iframe)
|
|
385
388
|
* The host application ID is taken from the search parameter {@link MFE_HOST_APPLICATION_ID_PARAM} in the URL of the iframe
|
|
386
389
|
* The module application ID is taken from the search parameter {@link MFE_APPLICATION_ID_PARAM} in the URL of the iframe
|
|
390
|
+
* @param locationParam - A {@link Location} object with information about the current location of the document. Defaults to global {@link location}.
|
|
387
391
|
*/
|
|
388
|
-
function getHostInfo() {
|
|
389
|
-
const searchParams = new URLSearchParams(
|
|
392
|
+
function getHostInfo(locationParam = location) {
|
|
393
|
+
const searchParams = new URLSearchParams(locationParam.search);
|
|
390
394
|
const storedHostInfo = JSON.parse(sessionStorage.getItem(SESSION_STORAGE_KEY) || '{}');
|
|
391
395
|
return {
|
|
392
|
-
hostURL: searchParams.get(MFE_HOST_URL_PARAM) || storedHostInfo.hostURL ||
|
|
396
|
+
hostURL: searchParams.get(MFE_HOST_URL_PARAM) || storedHostInfo.hostURL || locationParam.ancestorOrigins?.[0] || document.referrer,
|
|
393
397
|
hostApplicationId: searchParams.get(MFE_HOST_APPLICATION_ID_PARAM) || storedHostInfo.hostApplicationId,
|
|
394
398
|
moduleApplicationId: searchParams.get(MFE_MODULE_APPLICATION_ID_PARAM) || storedHostInfo.moduleApplicationId
|
|
395
399
|
};
|
|
@@ -427,16 +431,81 @@ class HostInfoPipe {
|
|
|
427
431
|
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
428
432
|
}
|
|
429
433
|
}
|
|
430
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
431
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.
|
|
434
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: HostInfoPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
435
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.13", ngImport: i0, type: HostInfoPipe, isStandalone: true, name: "hostInfo" }); }
|
|
432
436
|
}
|
|
433
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
437
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: HostInfoPipe, decorators: [{
|
|
434
438
|
type: Pipe,
|
|
435
439
|
args: [{
|
|
436
440
|
name: 'hostInfo'
|
|
437
441
|
}]
|
|
438
442
|
}] });
|
|
439
443
|
|
|
444
|
+
/** the error message type */
|
|
445
|
+
const ERROR_MESSAGE_TYPE = 'error';
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* A constant array of known message types and their versions.
|
|
449
|
+
*/
|
|
450
|
+
const KNOWN_MESSAGES = [
|
|
451
|
+
{
|
|
452
|
+
type: ERROR_MESSAGE_TYPE,
|
|
453
|
+
version: '1.0'
|
|
454
|
+
}
|
|
455
|
+
];
|
|
456
|
+
/**
|
|
457
|
+
* Returns the default options for starting a client endpoint peer connection.
|
|
458
|
+
* As `origin`, it will take the hostURL from {@link getHostInfo} and the `window` will be the parent window.
|
|
459
|
+
*/
|
|
460
|
+
function getDefaultClientEndpointStartOptions() {
|
|
461
|
+
const hostInfo = getHostInfo();
|
|
462
|
+
if (hostInfo.hostURL) {
|
|
463
|
+
return {
|
|
464
|
+
origin: new URL(hostInfo.hostURL).origin,
|
|
465
|
+
window: window.parent
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return {};
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Return `true` if embedded inside an iframe, `false` otherwise
|
|
472
|
+
* @param windowParam - A {@link window} object with information about the current window of the document. Defaults to global {@link window}.
|
|
473
|
+
*/
|
|
474
|
+
function isEmbedded(windowParam = window) {
|
|
475
|
+
return windowParam.top !== windowParam.self;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Provide the communication protocol connection configuration
|
|
480
|
+
* @param connectionConfigOptions The identifier of the application in the communication protocol ecosystem plus the types of messages able to exchange and a logger object
|
|
481
|
+
*/
|
|
482
|
+
function provideConnection(connectionConfigOptions) {
|
|
483
|
+
persistHostInfo();
|
|
484
|
+
const connectionId = (isEmbedded() && getHostInfo().moduleApplicationId) || connectionConfigOptions?.id;
|
|
485
|
+
if (!connectionId) {
|
|
486
|
+
(connectionConfigOptions?.logger || console).error('An id (moduleId) needs to be provided for the application in order to establish a connection inside the communication protocol');
|
|
487
|
+
return makeEnvironmentProviders([]);
|
|
488
|
+
}
|
|
489
|
+
const config = {
|
|
490
|
+
id: connectionId,
|
|
491
|
+
messageCheckStrategy: 'version',
|
|
492
|
+
knownMessages: [...KNOWN_MESSAGES, ...(connectionConfigOptions?.knownMessages || [])]
|
|
493
|
+
};
|
|
494
|
+
return makeEnvironmentProviders([
|
|
495
|
+
{
|
|
496
|
+
provide: MESSAGE_PEER_CONFIG, useValue: config
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
provide: MESSAGE_PEER_CONNECT_OPTIONS, useValue: getDefaultClientEndpointStartOptions()
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
// in the case of the ConnectionService will extend the base service 'useExisting' should be used
|
|
503
|
+
provide: MessagePeerService, useClass: MessagePeerService, deps: [MESSAGE_PEER_CONFIG]
|
|
504
|
+
},
|
|
505
|
+
...isEmbedded() ? [provideHistoryOverrides()] : []
|
|
506
|
+
]);
|
|
507
|
+
}
|
|
508
|
+
|
|
440
509
|
/**
|
|
441
510
|
* A service that handles navigation messages and routing.
|
|
442
511
|
*
|
|
@@ -507,10 +576,10 @@ class NavigationConsumerService {
|
|
|
507
576
|
stop() {
|
|
508
577
|
this.consumerManagerService.unregister(this);
|
|
509
578
|
}
|
|
510
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
511
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
579
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: NavigationConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
580
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: NavigationConsumerService, providedIn: 'root' }); }
|
|
512
581
|
}
|
|
513
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
582
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: NavigationConsumerService, decorators: [{
|
|
514
583
|
type: Injectable,
|
|
515
584
|
args: [{
|
|
516
585
|
providedIn: 'root'
|
|
@@ -553,25 +622,76 @@ class RouteMemorizeService {
|
|
|
553
622
|
getRoute(channelId) {
|
|
554
623
|
return this.routeStack[channelId];
|
|
555
624
|
}
|
|
556
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
557
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
625
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RouteMemorizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
626
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RouteMemorizeService, providedIn: 'root' }); }
|
|
558
627
|
}
|
|
559
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
628
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RouteMemorizeService, decorators: [{
|
|
560
629
|
type: Injectable,
|
|
561
630
|
args: [{
|
|
562
631
|
providedIn: 'root'
|
|
563
632
|
}]
|
|
564
633
|
}] });
|
|
565
634
|
|
|
566
|
-
|
|
635
|
+
/**
|
|
636
|
+
* A pipe that restores a route with optional query parameters and memory channel ID.
|
|
637
|
+
*
|
|
638
|
+
* This pipe is used to transform a URL or SafeResourceUrl by appending query parameters
|
|
639
|
+
* and adjusting the pathname based on the current active route and memorized route.
|
|
640
|
+
*/
|
|
641
|
+
class RestoreRoute {
|
|
567
642
|
constructor() {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
643
|
+
this.activeRoute = inject(ActivatedRoute);
|
|
644
|
+
this.domSanitizer = inject(DomSanitizer);
|
|
645
|
+
this.routeMemorizeService = inject(RouteMemorizeService, { optional: true });
|
|
646
|
+
this.window = inject(Window, { optional: true }) || window;
|
|
647
|
+
}
|
|
648
|
+
transform(url, options) {
|
|
649
|
+
const urlString = typeof url === 'string'
|
|
650
|
+
? url
|
|
651
|
+
: this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, url || null);
|
|
652
|
+
if (!url) {
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
if (urlString) {
|
|
656
|
+
const moduleUrl = new URL(urlString);
|
|
657
|
+
const queryParamsModule = new URLSearchParams(moduleUrl.searchParams);
|
|
658
|
+
const channelId = options?.memoryChannelId;
|
|
659
|
+
const memorizedRoute = channelId && this.routeMemorizeService?.getRoute(channelId);
|
|
660
|
+
const topWindowUrl = new URL(memorizedRoute ? this.window.origin + memorizedRoute : this.window.location.href);
|
|
661
|
+
const queryParamsTopWindow = new URLSearchParams(topWindowUrl.search);
|
|
662
|
+
if (options?.propagateQueryParams) {
|
|
663
|
+
for (const [key, value] of queryParamsTopWindow) {
|
|
664
|
+
if (options?.overrideQueryParams || !queryParamsModule.has(key)) {
|
|
665
|
+
queryParamsModule.set(key, value);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
moduleUrl.search = queryParamsModule.toString();
|
|
670
|
+
moduleUrl.pathname += topWindowUrl.pathname.split(`/${this.activeRoute.routeConfig?.path}`).pop() || '';
|
|
671
|
+
moduleUrl.pathname = moduleUrl.pathname.replace(/\/{2,}/g, '/');
|
|
672
|
+
const moduleUrlStringyfied = moduleUrl.toString();
|
|
673
|
+
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RestoreRoute, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
677
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.13", ngImport: i0, type: RestoreRoute, isStandalone: true, name: "restoreRoute" }); }
|
|
678
|
+
}
|
|
679
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RestoreRoute, decorators: [{
|
|
680
|
+
type: Pipe,
|
|
681
|
+
args: [{
|
|
682
|
+
name: 'restoreRoute'
|
|
683
|
+
}]
|
|
684
|
+
}] });
|
|
685
|
+
|
|
686
|
+
class RouteMemorizeDirective {
|
|
687
|
+
constructor() {
|
|
688
|
+
/**
|
|
689
|
+
* Whether to memorize the route.
|
|
690
|
+
* Default is true.
|
|
691
|
+
*/
|
|
692
|
+
this.memorizeRoute = input(true, ...(ngDevMode ? [{ debugName: "memorizeRoute" }] : []));
|
|
693
|
+
/**
|
|
694
|
+
* The ID used to memorize the route.
|
|
575
695
|
*/
|
|
576
696
|
this.memorizeRouteId = input(...(ngDevMode ? [undefined, { debugName: "memorizeRouteId" }] : []));
|
|
577
697
|
/**
|
|
@@ -603,52 +723,23 @@ class RouteMemorizeDirective {
|
|
|
603
723
|
return;
|
|
604
724
|
}
|
|
605
725
|
const requested = requestedUrlSignal();
|
|
606
|
-
const
|
|
607
|
-
|
|
726
|
+
const channelId = this.connect();
|
|
727
|
+
const id = this.memorizeRouteId() || channelId;
|
|
728
|
+
if (requested && id && requested.channelId === channelId) {
|
|
608
729
|
memory.memorizeRoute(id, requested.url, untracked(this.maxAge));
|
|
609
730
|
}
|
|
610
731
|
});
|
|
611
732
|
}
|
|
612
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
613
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.
|
|
733
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RouteMemorizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
734
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.13", 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 }); }
|
|
614
735
|
}
|
|
615
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RouteMemorizeDirective, decorators: [{
|
|
616
737
|
type: Directive,
|
|
617
738
|
args: [{
|
|
618
739
|
selector: 'iframe[memorizeRoute]',
|
|
619
740
|
standalone: true
|
|
620
741
|
}]
|
|
621
|
-
}], ctorParameters: () => [] });
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* A constant array of known message types and their versions.
|
|
625
|
-
*/
|
|
626
|
-
const KNOWN_MESSAGES = [
|
|
627
|
-
{
|
|
628
|
-
type: ERROR_MESSAGE_TYPE,
|
|
629
|
-
version: '1.0'
|
|
630
|
-
}
|
|
631
|
-
];
|
|
632
|
-
/**
|
|
633
|
-
* Returns the default options for starting a client endpoint peer connection.
|
|
634
|
-
* As `origin`, it will take the hostURL from {@link getHostInfo} and the `window` will be the parent window.
|
|
635
|
-
*/
|
|
636
|
-
function getDefaultClientEndpointStartOptions() {
|
|
637
|
-
const hostInfo = getHostInfo();
|
|
638
|
-
if (hostInfo.hostURL) {
|
|
639
|
-
return {
|
|
640
|
-
origin: new URL(hostInfo.hostURL).origin,
|
|
641
|
-
window: window.parent
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
return {};
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Return `true` if embedded inside an iframe, `false` otherwise
|
|
648
|
-
*/
|
|
649
|
-
function isEmbedded() {
|
|
650
|
-
return window.top !== window.self;
|
|
651
|
-
}
|
|
742
|
+
}], ctorParameters: () => [], propDecorators: { memorizeRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "memorizeRoute", required: false }] }], memorizeRouteId: [{ type: i0.Input, args: [{ isSignal: true, alias: "memorizeRouteId", required: false }] }], memorizeMaxAge: [{ type: i0.Input, args: [{ isSignal: true, alias: "memorizeMaxAge", required: false }] }], memorizeRouteMaxAge: [{ type: i0.Input, args: [{ isSignal: true, alias: "memorizeRouteMaxAge", required: false }] }], connect: [{ type: i0.Input, args: [{ isSignal: true, alias: "connect", required: false }] }] } });
|
|
652
743
|
|
|
653
744
|
/**
|
|
654
745
|
* A service that keeps in sync Router navigation and navigation messages.
|
|
@@ -662,6 +753,7 @@ class RoutingService {
|
|
|
662
753
|
this.activatedRoute = inject(ActivatedRoute);
|
|
663
754
|
this.messageService = inject((MessagePeerService));
|
|
664
755
|
this.logger = inject(LoggerService);
|
|
756
|
+
this.window = inject(Window, { optional: true }) || window;
|
|
665
757
|
/**
|
|
666
758
|
* @inheritdoc
|
|
667
759
|
*/
|
|
@@ -715,7 +807,7 @@ class RoutingService {
|
|
|
715
807
|
url
|
|
716
808
|
};
|
|
717
809
|
// TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
|
|
718
|
-
if (isEmbedded()) {
|
|
810
|
+
if (isEmbedded(this.window)) {
|
|
719
811
|
this.messageService.send(messageV10);
|
|
720
812
|
}
|
|
721
813
|
else {
|
|
@@ -733,10 +825,10 @@ class RoutingService {
|
|
|
733
825
|
}
|
|
734
826
|
});
|
|
735
827
|
}
|
|
736
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
737
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
828
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RoutingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
829
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RoutingService, providedIn: 'root' }); }
|
|
738
830
|
}
|
|
739
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
831
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: RoutingService, decorators: [{
|
|
740
832
|
type: Injectable,
|
|
741
833
|
args: [{
|
|
742
834
|
providedIn: 'root'
|
|
@@ -744,54 +836,152 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImpor
|
|
|
744
836
|
}], ctorParameters: () => [] });
|
|
745
837
|
|
|
746
838
|
/**
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
* This pipe is used to transform a URL or SafeResourceUrl by appending query parameters
|
|
750
|
-
* and adjusting the pathname based on the current active route and memorized route.
|
|
839
|
+
* This service listens for resize messages and updates the height of elements based on the received messages.
|
|
751
840
|
*/
|
|
752
|
-
class
|
|
841
|
+
class ResizeConsumerService {
|
|
753
842
|
constructor() {
|
|
754
|
-
this.
|
|
755
|
-
|
|
756
|
-
|
|
843
|
+
this.newHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "newHeight" }] : []));
|
|
844
|
+
/**
|
|
845
|
+
* A readonly signal that provides the new height information from the channel.
|
|
846
|
+
*/
|
|
847
|
+
this.newHeightFromChannel = this.newHeight.asReadonly();
|
|
848
|
+
/**
|
|
849
|
+
* The type of messages this service handles ('resize').
|
|
850
|
+
*/
|
|
851
|
+
this.type = RESIZE_MESSAGE_TYPE;
|
|
852
|
+
/**
|
|
853
|
+
* The supported versions of resize messages and their handlers.
|
|
854
|
+
*/
|
|
855
|
+
this.supportedVersions = {
|
|
856
|
+
/**
|
|
857
|
+
* Use the message paylod to compute a new height and emit it via the public signal
|
|
858
|
+
* @param message message to consume
|
|
859
|
+
*/
|
|
860
|
+
'1.0': (message) => this.newHeight.set({ height: message.payload.height, channelId: message.from })
|
|
861
|
+
};
|
|
862
|
+
this.consumerManagerService = inject(ConsumerManagerService);
|
|
863
|
+
this.start();
|
|
864
|
+
inject(DestroyRef).onDestroy(() => this.stop());
|
|
757
865
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
866
|
+
/**
|
|
867
|
+
* Starts the resize handler service by registering it into the consumer manager service.
|
|
868
|
+
*/
|
|
869
|
+
start() {
|
|
870
|
+
this.consumerManagerService.register(this);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Stops the resize handler service by unregistering it from the consumer manager service.
|
|
874
|
+
*/
|
|
875
|
+
stop() {
|
|
876
|
+
this.consumerManagerService.unregister(this);
|
|
877
|
+
}
|
|
878
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
879
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
|
|
880
|
+
}
|
|
881
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ResizeConsumerService, decorators: [{
|
|
882
|
+
type: Injectable,
|
|
883
|
+
args: [{
|
|
884
|
+
providedIn: 'root'
|
|
885
|
+
}]
|
|
886
|
+
}], ctorParameters: () => [] });
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* This service observe changes in the document's body height.
|
|
890
|
+
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
891
|
+
*/
|
|
892
|
+
class ResizeService {
|
|
893
|
+
constructor() {
|
|
894
|
+
this.messageService = inject((MessagePeerService));
|
|
895
|
+
/**
|
|
896
|
+
* @inheritdoc
|
|
897
|
+
*/
|
|
898
|
+
this.types = RESIZE_MESSAGE_TYPE;
|
|
899
|
+
registerProducer(this);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* @inheritdoc
|
|
903
|
+
*/
|
|
904
|
+
handleError(message) {
|
|
905
|
+
// eslint-disable-next-line no-console -- error handling placeholder
|
|
906
|
+
console.error('Error in resize service message', message);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* This method sets up a `ResizeObserver` to observe changes in the document's body height.
|
|
910
|
+
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
911
|
+
*/
|
|
912
|
+
startResizeObserver() {
|
|
913
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
914
|
+
const newHeight = document.body.getBoundingClientRect().height;
|
|
915
|
+
if (!this.actualHeight || newHeight !== this.actualHeight) {
|
|
916
|
+
this.actualHeight = newHeight;
|
|
917
|
+
const messageV10 = {
|
|
918
|
+
type: 'resize',
|
|
919
|
+
version: '1.0',
|
|
920
|
+
height: this.actualHeight
|
|
921
|
+
};
|
|
922
|
+
// TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
|
|
923
|
+
this.messageService.send(messageV10);
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
afterNextRender(() => this.resizeObserver?.observe(document.body));
|
|
927
|
+
}
|
|
928
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
929
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ResizeService, providedIn: 'root' }); }
|
|
930
|
+
}
|
|
931
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ResizeService, decorators: [{
|
|
932
|
+
type: Injectable,
|
|
933
|
+
args: [{
|
|
934
|
+
providedIn: 'root'
|
|
935
|
+
}]
|
|
936
|
+
}], ctorParameters: () => [] });
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* A directive that adjusts the height of an element based on resize messages from a specified channel.
|
|
940
|
+
*/
|
|
941
|
+
class ScalableDirective {
|
|
942
|
+
constructor() {
|
|
943
|
+
/**
|
|
944
|
+
* The connection ID for the element, used as channel id backup
|
|
945
|
+
*/
|
|
946
|
+
this.connect = input(...(ngDevMode ? [undefined, { debugName: "connect" }] : []));
|
|
947
|
+
/**
|
|
948
|
+
* The channel id
|
|
949
|
+
*/
|
|
950
|
+
this.scalable = input(...(ngDevMode ? [undefined, { debugName: "scalable" }] : []));
|
|
951
|
+
this.resizeHandler = inject(ResizeConsumerService);
|
|
952
|
+
/**
|
|
953
|
+
* This signal checks if the current channel requesting the resize matches the channel ID from the resize handler.
|
|
954
|
+
* If they match, it returns the new height information; otherwise, it returns undefined.
|
|
955
|
+
*/
|
|
956
|
+
this.newHeightFromChannel = computed(() => {
|
|
957
|
+
const channelAskingResize = this.scalable() || this.connect();
|
|
958
|
+
const newHeightFromChannel = this.resizeHandler.newHeightFromChannel();
|
|
959
|
+
if (channelAskingResize && newHeightFromChannel?.channelId === channelAskingResize) {
|
|
960
|
+
return newHeightFromChannel;
|
|
961
|
+
}
|
|
763
962
|
return undefined;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
for (const [key, value] of queryParamsTopWindow) {
|
|
774
|
-
if (options?.overrideQueryParams || !queryParamsModule.has(key)) {
|
|
775
|
-
queryParamsModule.set(key, value);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
963
|
+
}, ...(ngDevMode ? [{ debugName: "newHeightFromChannel" }] : []));
|
|
964
|
+
const elem = inject(ElementRef);
|
|
965
|
+
const renderer = inject(Renderer2);
|
|
966
|
+
this.resizeHandler.start();
|
|
967
|
+
/** When a new height value is received set the height of the host element (in pixels) */
|
|
968
|
+
effect(() => {
|
|
969
|
+
const newHeightFromChannel = this.newHeightFromChannel();
|
|
970
|
+
if (newHeightFromChannel) {
|
|
971
|
+
renderer.setStyle(elem.nativeElement, 'height', `${newHeightFromChannel.height}px`);
|
|
778
972
|
}
|
|
779
|
-
|
|
780
|
-
moduleUrl.pathname += topWindowUrl.pathname.split(`/${this.activeRoute.routeConfig?.path}`).pop() || '';
|
|
781
|
-
moduleUrl.pathname = moduleUrl.pathname.replace(/\/{2,}/g, '/');
|
|
782
|
-
const moduleUrlStringyfied = moduleUrl.toString();
|
|
783
|
-
return typeof url === 'string' ? moduleUrlStringyfied : this.domSanitizer.bypassSecurityTrustResourceUrl(moduleUrlStringyfied);
|
|
784
|
-
}
|
|
973
|
+
});
|
|
785
974
|
}
|
|
786
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
787
|
-
/** @nocollapse */ static { this.ɵ
|
|
975
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ScalableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
976
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.13", 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 }); }
|
|
788
977
|
}
|
|
789
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
790
|
-
type:
|
|
978
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ScalableDirective, decorators: [{
|
|
979
|
+
type: Directive,
|
|
791
980
|
args: [{
|
|
792
|
-
|
|
981
|
+
selector: '[scalable]',
|
|
982
|
+
standalone: true
|
|
793
983
|
}]
|
|
794
|
-
}] });
|
|
984
|
+
}], ctorParameters: () => [], propDecorators: { connect: [{ type: i0.Input, args: [{ isSignal: true, alias: "connect", required: false }] }], scalable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scalable", required: false }] }] } });
|
|
795
985
|
|
|
796
986
|
/** Default suffix for an url containing a theme css file */
|
|
797
987
|
const THEME_URL_SUFFIX = '-theme.css';
|
|
@@ -871,13 +1061,14 @@ class ThemeProducerService {
|
|
|
871
1061
|
constructor() {
|
|
872
1062
|
this.messageService = inject((MessagePeerService));
|
|
873
1063
|
this.logger = inject(LoggerService);
|
|
1064
|
+
this.window = inject(Window, { optional: true }) || window;
|
|
874
1065
|
/**
|
|
875
1066
|
* The type of messages this service handles ('theme').
|
|
876
1067
|
*/
|
|
877
1068
|
this.types = THEME_MESSAGE_TYPE;
|
|
878
1069
|
registerProducer(this);
|
|
879
1070
|
// get the current theme name from the url (if any) and emit a first value for the current theme
|
|
880
|
-
const parentUrl = new URL(window.location.toString());
|
|
1071
|
+
const parentUrl = new URL(this.window.location.toString());
|
|
881
1072
|
const selectedThemeName = parentUrl.searchParams.get(THEME_QUERY_PARAM_NAME);
|
|
882
1073
|
this.currentThemeSelection = signal(selectedThemeName
|
|
883
1074
|
? {
|
|
@@ -941,10 +1132,10 @@ class ThemeProducerService {
|
|
|
941
1132
|
this.logger.error('Error in theme service message', message);
|
|
942
1133
|
this.revertToPreviousTheme();
|
|
943
1134
|
}
|
|
944
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
945
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
1135
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ThemeProducerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1136
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ThemeProducerService, providedIn: 'root' }); }
|
|
946
1137
|
}
|
|
947
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
1138
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ThemeProducerService, decorators: [{
|
|
948
1139
|
type: Injectable,
|
|
949
1140
|
args: [{
|
|
950
1141
|
providedIn: 'root'
|
|
@@ -977,10 +1168,10 @@ class ApplyTheme {
|
|
|
977
1168
|
}
|
|
978
1169
|
return undefined;
|
|
979
1170
|
}
|
|
980
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
981
|
-
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.
|
|
1171
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ApplyTheme, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1172
|
+
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.13", ngImport: i0, type: ApplyTheme, isStandalone: true, name: "applyTheme" }); }
|
|
982
1173
|
}
|
|
983
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
1174
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ApplyTheme, decorators: [{
|
|
984
1175
|
type: Pipe,
|
|
985
1176
|
args: [{
|
|
986
1177
|
name: 'applyTheme'
|
|
@@ -1036,132 +1227,19 @@ class ThemeConsumerService {
|
|
|
1036
1227
|
stop() {
|
|
1037
1228
|
this.consumerManagerService.unregister(this);
|
|
1038
1229
|
}
|
|
1039
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
1040
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
1230
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ThemeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1231
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ThemeConsumerService, providedIn: 'root' }); }
|
|
1041
1232
|
}
|
|
1042
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
1233
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ThemeConsumerService, decorators: [{
|
|
1043
1234
|
type: Injectable,
|
|
1044
1235
|
args: [{
|
|
1045
1236
|
providedIn: 'root'
|
|
1046
1237
|
}]
|
|
1047
1238
|
}], ctorParameters: () => [] });
|
|
1048
1239
|
|
|
1049
|
-
class ConnectDirective {
|
|
1050
|
-
/**
|
|
1051
|
-
* Binds the `src` attribute of the iframe to the sanitized source URL.
|
|
1052
|
-
*/
|
|
1053
|
-
get srcAttr() {
|
|
1054
|
-
return this.src();
|
|
1055
|
-
}
|
|
1056
|
-
constructor() {
|
|
1057
|
-
/**
|
|
1058
|
-
* The connection ID required for the message peer service.
|
|
1059
|
-
*/
|
|
1060
|
-
this.connect = input.required(...(ngDevMode ? [{ debugName: "connect" }] : []));
|
|
1061
|
-
/**
|
|
1062
|
-
* The sanitized source URL for the iframe.
|
|
1063
|
-
*/
|
|
1064
|
-
this.src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
|
|
1065
|
-
this.messageService = inject(MessagePeerService);
|
|
1066
|
-
this.domSanitizer = inject(DomSanitizer);
|
|
1067
|
-
this.iframeElement = inject(ElementRef).nativeElement;
|
|
1068
|
-
this.clientOrigin = computed(() => {
|
|
1069
|
-
const src = this.src();
|
|
1070
|
-
const srcString = src && this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, src);
|
|
1071
|
-
return srcString && new URL(srcString).origin;
|
|
1072
|
-
}, ...(ngDevMode ? [{ debugName: "clientOrigin" }] : []));
|
|
1073
|
-
const logger = inject(LoggerService);
|
|
1074
|
-
// When the origin or connection ID change - reconnect the message service
|
|
1075
|
-
effect((onCleanup) => {
|
|
1076
|
-
let stopHandshakeListening = () => { };
|
|
1077
|
-
const origin = this.clientOrigin();
|
|
1078
|
-
const id = this.connect();
|
|
1079
|
-
const source = this.iframeElement.contentWindow;
|
|
1080
|
-
// listen for handshakes only if we know the origin and were given a connection ID
|
|
1081
|
-
if (origin && source && id) {
|
|
1082
|
-
try {
|
|
1083
|
-
stopHandshakeListening = this.messageService.listen({ id, source, origin });
|
|
1084
|
-
}
|
|
1085
|
-
catch (e) {
|
|
1086
|
-
logger.error(`Failed to start listening for (connection ID: ${id})`, e);
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
// stop listening for handshakes and disconnect previous connection when:
|
|
1090
|
-
// - origin/connection ID change
|
|
1091
|
-
// - the directive is destroyed
|
|
1092
|
-
onCleanup(() => {
|
|
1093
|
-
stopHandshakeListening();
|
|
1094
|
-
this.messageService.disconnect();
|
|
1095
|
-
});
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1099
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.2.4", 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 }); }
|
|
1100
|
-
}
|
|
1101
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: ConnectDirective, decorators: [{
|
|
1102
|
-
type: Directive,
|
|
1103
|
-
args: [{
|
|
1104
|
-
selector: 'iframe[connect]',
|
|
1105
|
-
standalone: true
|
|
1106
|
-
}]
|
|
1107
|
-
}], ctorParameters: () => [], propDecorators: { srcAttr: [{
|
|
1108
|
-
type: HostBinding,
|
|
1109
|
-
args: ['src']
|
|
1110
|
-
}] } });
|
|
1111
|
-
|
|
1112
|
-
/**
|
|
1113
|
-
* No operation function used to override the history API methods.
|
|
1114
|
-
*/
|
|
1115
|
-
function noop() { }
|
|
1116
|
-
/**
|
|
1117
|
-
* If an iframe is not sandboxed or is of the same origin, navigation inside it will mess up the main window history.
|
|
1118
|
-
* This disables writing to and overriding history API from inside the iframe to prevent this.
|
|
1119
|
-
* Theoretically, this might break applications that rely on reading history API from inside the iframe.
|
|
1120
|
-
*
|
|
1121
|
-
* This should also allow having `CustomPathLocationStrategy` in the iframe if necessary.
|
|
1122
|
-
*/
|
|
1123
|
-
function provideDisableHistoryWrites() {
|
|
1124
|
-
return provideAppInitializer(() => {
|
|
1125
|
-
Object.defineProperty(history, 'pushState', { value: noop, writable: false, configurable: false });
|
|
1126
|
-
Object.defineProperty(history, 'replaceState', { value: noop, writable: false, configurable: false });
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
/**
|
|
1131
|
-
* Provide the communication protocol connection configuration
|
|
1132
|
-
* @param connectionConfigOptions The identifier of the application in the communication protocol ecosystem plus the types of messages able to exchange and a logger object
|
|
1133
|
-
*/
|
|
1134
|
-
function provideConnection(connectionConfigOptions) {
|
|
1135
|
-
persistHostInfo();
|
|
1136
|
-
const connectionId = (isEmbedded() && getHostInfo().moduleApplicationId) || connectionConfigOptions?.id;
|
|
1137
|
-
if (!connectionId) {
|
|
1138
|
-
(connectionConfigOptions?.logger || console).error('An id (moduleId) needs to be provided for the application in order to establish a connection inside the communication protocol');
|
|
1139
|
-
return makeEnvironmentProviders([]);
|
|
1140
|
-
}
|
|
1141
|
-
const config = {
|
|
1142
|
-
id: connectionId,
|
|
1143
|
-
messageCheckStrategy: 'version',
|
|
1144
|
-
knownMessages: [...KNOWN_MESSAGES, ...(connectionConfigOptions?.knownMessages || [])]
|
|
1145
|
-
};
|
|
1146
|
-
return makeEnvironmentProviders([
|
|
1147
|
-
{
|
|
1148
|
-
provide: MESSAGE_PEER_CONFIG, useValue: config
|
|
1149
|
-
},
|
|
1150
|
-
{
|
|
1151
|
-
provide: MESSAGE_PEER_CONNECT_OPTIONS, useValue: getDefaultClientEndpointStartOptions()
|
|
1152
|
-
},
|
|
1153
|
-
{
|
|
1154
|
-
// in the case of the ConnectionService will extend the base service 'useExisting' should be used
|
|
1155
|
-
provide: MessagePeerService, useClass: MessagePeerService, deps: [MESSAGE_PEER_CONFIG]
|
|
1156
|
-
},
|
|
1157
|
-
// deactivate history writes to avoid embedded app writing to the host history
|
|
1158
|
-
...isEmbedded() ? [provideDisableHistoryWrites()] : []
|
|
1159
|
-
]);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
1240
|
/**
|
|
1163
1241
|
* Generated bundle index. Do not edit.
|
|
1164
1242
|
*/
|
|
1165
1243
|
|
|
1166
|
-
export { ApplyTheme, ConnectDirective, ConsumerManagerService, ERROR_MESSAGE_TYPE, HostInfoPipe, 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, applyInitialTheme, applyTheme, downloadApplicationThemeCss, getAvailableConsumers, getDefaultClientEndpointStartOptions, getHostInfo, getStyle, hostQueryParams, isEmbedded, isErrorMessage, persistHostInfo, provideConnection, registerConsumer, registerProducer, sendError };
|
|
1244
|
+
export { ApplyTheme, ConnectDirective, ConsumerManagerService, ERROR_MESSAGE_TYPE, HistoryConsumerService, HostInfoPipe, 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, applyInitialTheme, applyTheme, downloadApplicationThemeCss, getAvailableConsumers, getDefaultClientEndpointStartOptions, getHostInfo, getStyle, hostQueryParams, isEmbedded, isErrorMessage, persistHostInfo, provideConnection, provideHistoryOverrides, registerConsumer, registerProducer, sendError };
|
|
1167
1245
|
//# sourceMappingURL=ama-mfe-ng-utils.mjs.map
|