@ama-mfe/ng-utils 13.2.0-rc.3 → 13.2.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.
@@ -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 { Injectable, inject, signal, effect, DestroyRef, afterNextRender, input, computed, ElementRef, Renderer2, Directive, SecurityContext, Pipe, untracked, HostBinding, provideAppInitializer, makeEnvironmentProviders } from '@angular/core';
5
- import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
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 { RESIZE_MESSAGE_TYPE, NAVIGATION_MESSAGE_TYPE, THEME_MESSAGE_TYPE } from '@ama-mfe/messages';
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
- import { DomSanitizer } from '@angular/platform-browser';
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.3", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
62
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", 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.3", ngImport: i0, type: ConnectDirective, decorators: [{
65
+ type: Directive,
66
+ args: [{
67
+ selector: 'iframe[connect]',
68
+ standalone: true
69
+ }]
70
+ }], ctorParameters: () => [], propDecorators: { 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.
@@ -208,53 +271,49 @@ const registerConsumer = (consumer) => {
208
271
  });
209
272
  };
210
273
 
211
- /** the error message type */
212
- const ERROR_MESSAGE_TYPE = 'error';
213
-
214
274
  /**
215
- * This service listens for resize messages and updates the height of elements based on the received messages.
275
+ * A service that handles history messages.
276
+ *
277
+ * This service listens for history messages and navigates accordingly.
216
278
  */
217
- class ResizeConsumerService {
279
+ class HistoryConsumerService {
218
280
  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
281
  /**
225
- * The type of messages this service handles ('resize').
282
+ * The type of messages this service handles.
226
283
  */
227
- this.type = RESIZE_MESSAGE_TYPE;
284
+ this.type = HISTORY_MESSAGE_TYPE;
228
285
  /**
229
- * The supported versions of resize messages and their handlers.
286
+ * @inheritdoc
230
287
  */
231
288
  this.supportedVersions = {
232
289
  /**
233
- * Use the message paylod to compute a new height and emit it via the public signal
290
+ * Use the message payload to navigate in the history
234
291
  * @param message message to consume
235
292
  */
236
- '1.0': (message) => this.newHeight.set({ height: message.payload.height, channelId: message.from })
293
+ '1.0': (message) => {
294
+ history.go(message.payload.delta);
295
+ }
237
296
  };
238
297
  this.consumerManagerService = inject(ConsumerManagerService);
239
298
  this.start();
240
299
  inject(DestroyRef).onDestroy(() => this.stop());
241
300
  }
242
301
  /**
243
- * Starts the resize handler service by registering it into the consumer manager service.
302
+ * @inheritdoc
244
303
  */
245
304
  start() {
246
305
  this.consumerManagerService.register(this);
247
306
  }
248
307
  /**
249
- * Stops the resize handler service by unregistering it from the consumer manager service.
308
+ * @inheritdoc
250
309
  */
251
310
  stop() {
252
311
  this.consumerManagerService.unregister(this);
253
312
  }
254
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
255
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
313
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: HistoryConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
314
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: HistoryConsumerService, providedIn: 'root' }); }
256
315
  }
257
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeConsumerService, decorators: [{
316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: HistoryConsumerService, decorators: [{
258
317
  type: Injectable,
259
318
  args: [{
260
319
  providedIn: 'root'
@@ -262,102 +321,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
262
321
  }], ctorParameters: () => [] });
263
322
 
264
323
  /**
265
- * This service observe changes in the document's body height.
266
- * When the height changes, it sends a resize message with the new height, to the connected peers
324
+ * Provides necessary overrides to make the module navigation in history work in an embedded context :
325
+ * - Prevent pushing states to history, replace state instead
326
+ * - Handle history navigation via History messages to let the host manage the states
267
327
  */
268
- class ResizeService {
269
- constructor() {
270
- this.messageService = inject((MessagePeerService));
271
- /**
272
- * @inheritdoc
273
- */
274
- this.types = RESIZE_MESSAGE_TYPE;
275
- registerProducer(this);
276
- }
277
- /**
278
- * @inheritdoc
279
- */
280
- handleError(message) {
281
- // eslint-disable-next-line no-console -- error handling placeholder
282
- console.error('Error in resize service message', message);
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
- }
328
+ function provideHistoryOverrides() {
329
+ return provideAppInitializer(() => {
330
+ const messageService = inject((MessagePeerService));
331
+ const navigate = (delta) => {
332
+ messageService.send({
333
+ type: 'history',
334
+ version: '1.0',
335
+ delta
336
+ });
337
+ };
338
+ Object.defineProperty(history, 'pushState', {
339
+ value: (data, unused, url) => {
340
+ history.replaceState(data, unused, url);
341
+ },
342
+ writable: false,
343
+ configurable: false
301
344
  });
302
- afterNextRender(() => this.resizeObserver?.observe(document.body));
303
- }
304
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
305
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeService, providedIn: 'root' }); }
306
- }
307
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", 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
- }
345
+ Object.defineProperty(history, 'back', {
346
+ value: () => navigate(-1),
347
+ writable: false,
348
+ configurable: false
349
349
  });
350
- }
351
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ScalableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
352
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", 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 }); }
350
+ Object.defineProperty(history, 'forward', {
351
+ value: () => navigate(1),
352
+ writable: false,
353
+ configurable: false
354
+ });
355
+ Object.defineProperty(history, 'go', {
356
+ value: (delta) => navigate(delta),
357
+ writable: false,
358
+ configurable: false
359
+ });
360
+ });
353
361
  }
354
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ScalableDirective, decorators: [{
355
- type: Directive,
356
- args: [{
357
- selector: '[scalable]',
358
- standalone: true
359
- }]
360
- }], ctorParameters: () => [] });
361
362
 
362
363
  const SESSION_STORAGE_KEY = 'ama-mfe-host-info';
363
364
  /**
@@ -437,6 +438,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
437
438
  }]
438
439
  }] });
439
440
 
441
+ /** the error message type */
442
+ const ERROR_MESSAGE_TYPE = 'error';
443
+
444
+ /**
445
+ * A constant array of known message types and their versions.
446
+ */
447
+ const KNOWN_MESSAGES = [
448
+ {
449
+ type: ERROR_MESSAGE_TYPE,
450
+ version: '1.0'
451
+ }
452
+ ];
453
+ /**
454
+ * Returns the default options for starting a client endpoint peer connection.
455
+ * As `origin`, it will take the hostURL from {@link getHostInfo} and the `window` will be the parent window.
456
+ */
457
+ function getDefaultClientEndpointStartOptions() {
458
+ const hostInfo = getHostInfo();
459
+ if (hostInfo.hostURL) {
460
+ return {
461
+ origin: new URL(hostInfo.hostURL).origin,
462
+ window: window.parent
463
+ };
464
+ }
465
+ return {};
466
+ }
467
+ /**
468
+ * Return `true` if embedded inside an iframe, `false` otherwise
469
+ */
470
+ function isEmbedded() {
471
+ return window.top !== window.self;
472
+ }
473
+
474
+ /**
475
+ * Provide the communication protocol connection configuration
476
+ * @param connectionConfigOptions The identifier of the application in the communication protocol ecosystem plus the types of messages able to exchange and a logger object
477
+ */
478
+ function provideConnection(connectionConfigOptions) {
479
+ persistHostInfo();
480
+ const connectionId = (isEmbedded() && getHostInfo().moduleApplicationId) || connectionConfigOptions?.id;
481
+ if (!connectionId) {
482
+ (connectionConfigOptions?.logger || console).error('An id (moduleId) needs to be provided for the application in order to establish a connection inside the communication protocol');
483
+ return makeEnvironmentProviders([]);
484
+ }
485
+ const config = {
486
+ id: connectionId,
487
+ messageCheckStrategy: 'version',
488
+ knownMessages: [...KNOWN_MESSAGES, ...(connectionConfigOptions?.knownMessages || [])]
489
+ };
490
+ return makeEnvironmentProviders([
491
+ {
492
+ provide: MESSAGE_PEER_CONFIG, useValue: config
493
+ },
494
+ {
495
+ provide: MESSAGE_PEER_CONNECT_OPTIONS, useValue: getDefaultClientEndpointStartOptions()
496
+ },
497
+ {
498
+ // in the case of the ConnectionService will extend the base service 'useExisting' should be used
499
+ provide: MessagePeerService, useClass: MessagePeerService, deps: [MESSAGE_PEER_CONFIG]
500
+ },
501
+ ...isEmbedded() ? [provideHistoryOverrides()] : []
502
+ ]);
503
+ }
504
+
440
505
  /**
441
506
  * A service that handles navigation messages and routing.
442
507
  *
@@ -621,36 +686,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
621
686
  }]
622
687
  }], ctorParameters: () => [] });
623
688
 
624
- /**
625
- * A constant array of known message types and their versions.
626
- */
627
- const KNOWN_MESSAGES = [
628
- {
629
- type: ERROR_MESSAGE_TYPE,
630
- version: '1.0'
631
- }
632
- ];
633
- /**
634
- * Returns the default options for starting a client endpoint peer connection.
635
- * As `origin`, it will take the hostURL from {@link getHostInfo} and the `window` will be the parent window.
636
- */
637
- function getDefaultClientEndpointStartOptions() {
638
- const hostInfo = getHostInfo();
639
- if (hostInfo.hostURL) {
640
- return {
641
- origin: new URL(hostInfo.hostURL).origin,
642
- window: window.parent
643
- };
644
- }
645
- return {};
646
- }
647
- /**
648
- * Return `true` if embedded inside an iframe, `false` otherwise
649
- */
650
- function isEmbedded() {
651
- return window.top !== window.self;
652
- }
653
-
654
689
  /**
655
690
  * A service that keeps in sync Router navigation and navigation messages.
656
691
  *
@@ -794,6 +829,154 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
794
829
  }]
795
830
  }] });
796
831
 
832
+ /**
833
+ * This service listens for resize messages and updates the height of elements based on the received messages.
834
+ */
835
+ class ResizeConsumerService {
836
+ constructor() {
837
+ this.newHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "newHeight" }] : []));
838
+ /**
839
+ * A readonly signal that provides the new height information from the channel.
840
+ */
841
+ this.newHeightFromChannel = this.newHeight.asReadonly();
842
+ /**
843
+ * The type of messages this service handles ('resize').
844
+ */
845
+ this.type = RESIZE_MESSAGE_TYPE;
846
+ /**
847
+ * The supported versions of resize messages and their handlers.
848
+ */
849
+ this.supportedVersions = {
850
+ /**
851
+ * Use the message paylod to compute a new height and emit it via the public signal
852
+ * @param message message to consume
853
+ */
854
+ '1.0': (message) => this.newHeight.set({ height: message.payload.height, channelId: message.from })
855
+ };
856
+ this.consumerManagerService = inject(ConsumerManagerService);
857
+ this.start();
858
+ inject(DestroyRef).onDestroy(() => this.stop());
859
+ }
860
+ /**
861
+ * Starts the resize handler service by registering it into the consumer manager service.
862
+ */
863
+ start() {
864
+ this.consumerManagerService.register(this);
865
+ }
866
+ /**
867
+ * Stops the resize handler service by unregistering it from the consumer manager service.
868
+ */
869
+ stop() {
870
+ this.consumerManagerService.unregister(this);
871
+ }
872
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
873
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
874
+ }
875
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeConsumerService, decorators: [{
876
+ type: Injectable,
877
+ args: [{
878
+ providedIn: 'root'
879
+ }]
880
+ }], ctorParameters: () => [] });
881
+
882
+ /**
883
+ * This service observe changes in the document's body height.
884
+ * When the height changes, it sends a resize message with the new height, to the connected peers
885
+ */
886
+ class ResizeService {
887
+ constructor() {
888
+ this.messageService = inject((MessagePeerService));
889
+ /**
890
+ * @inheritdoc
891
+ */
892
+ this.types = RESIZE_MESSAGE_TYPE;
893
+ registerProducer(this);
894
+ }
895
+ /**
896
+ * @inheritdoc
897
+ */
898
+ handleError(message) {
899
+ // eslint-disable-next-line no-console -- error handling placeholder
900
+ console.error('Error in resize service message', message);
901
+ }
902
+ /**
903
+ * This method sets up a `ResizeObserver` to observe changes in the document's body height.
904
+ * When the height changes, it sends a resize message with the new height, to the connected peers
905
+ */
906
+ startResizeObserver() {
907
+ this.resizeObserver = new ResizeObserver(() => {
908
+ const newHeight = document.body.getBoundingClientRect().height;
909
+ if (!this.actualHeight || newHeight !== this.actualHeight) {
910
+ this.actualHeight = newHeight;
911
+ const messageV10 = {
912
+ type: 'resize',
913
+ version: '1.0',
914
+ height: this.actualHeight
915
+ };
916
+ // TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
917
+ this.messageService.send(messageV10);
918
+ }
919
+ });
920
+ afterNextRender(() => this.resizeObserver?.observe(document.body));
921
+ }
922
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
923
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeService, providedIn: 'root' }); }
924
+ }
925
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ResizeService, decorators: [{
926
+ type: Injectable,
927
+ args: [{
928
+ providedIn: 'root'
929
+ }]
930
+ }], ctorParameters: () => [] });
931
+
932
+ /**
933
+ * A directive that adjusts the height of an element based on resize messages from a specified channel.
934
+ */
935
+ class ScalableDirective {
936
+ constructor() {
937
+ /**
938
+ * The connection ID for the element, used as channel id backup
939
+ */
940
+ this.connect = input(...(ngDevMode ? [undefined, { debugName: "connect" }] : []));
941
+ /**
942
+ * The channel id
943
+ */
944
+ this.scalable = input(...(ngDevMode ? [undefined, { debugName: "scalable" }] : []));
945
+ this.resizeHandler = inject(ResizeConsumerService);
946
+ /**
947
+ * This signal checks if the current channel requesting the resize matches the channel ID from the resize handler.
948
+ * If they match, it returns the new height information; otherwise, it returns undefined.
949
+ */
950
+ this.newHeightFromChannel = computed(() => {
951
+ const channelAskingResize = this.scalable() || this.connect();
952
+ const newHeightFromChannel = this.resizeHandler.newHeightFromChannel();
953
+ if (channelAskingResize && newHeightFromChannel?.channelId === channelAskingResize) {
954
+ return newHeightFromChannel;
955
+ }
956
+ return undefined;
957
+ }, ...(ngDevMode ? [{ debugName: "newHeightFromChannel" }] : []));
958
+ const elem = inject(ElementRef);
959
+ const renderer = inject(Renderer2);
960
+ this.resizeHandler.start();
961
+ /** When a new height value is received set the height of the host element (in pixels) */
962
+ effect(() => {
963
+ const newHeightFromChannel = this.newHeightFromChannel();
964
+ if (newHeightFromChannel) {
965
+ renderer.setStyle(elem.nativeElement, 'height', `${newHeightFromChannel.height}px`);
966
+ }
967
+ });
968
+ }
969
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ScalableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
970
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", 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 }); }
971
+ }
972
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ScalableDirective, decorators: [{
973
+ type: Directive,
974
+ args: [{
975
+ selector: '[scalable]',
976
+ standalone: true
977
+ }]
978
+ }], ctorParameters: () => [] });
979
+
797
980
  /** Default suffix for an url containing a theme css file */
798
981
  const THEME_URL_SUFFIX = '-theme.css';
799
982
  /** Default name for the query parameter containing the theme name */
@@ -1047,122 +1230,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
1047
1230
  }]
1048
1231
  }], ctorParameters: () => [] });
1049
1232
 
1050
- class ConnectDirective {
1051
- /**
1052
- * Binds the `src` attribute of the iframe to the sanitized source URL.
1053
- */
1054
- get srcAttr() {
1055
- return this.src();
1056
- }
1057
- constructor() {
1058
- /**
1059
- * The connection ID required for the message peer service.
1060
- */
1061
- this.connect = input.required(...(ngDevMode ? [{ debugName: "connect" }] : []));
1062
- /**
1063
- * The sanitized source URL for the iframe.
1064
- */
1065
- this.src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
1066
- this.messageService = inject(MessagePeerService);
1067
- this.domSanitizer = inject(DomSanitizer);
1068
- this.iframeElement = inject(ElementRef).nativeElement;
1069
- this.clientOrigin = computed(() => {
1070
- const src = this.src();
1071
- const srcString = src && this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, src);
1072
- return srcString && new URL(srcString).origin;
1073
- }, ...(ngDevMode ? [{ debugName: "clientOrigin" }] : []));
1074
- const logger = inject(LoggerService);
1075
- // When the origin or connection ID change - reconnect the message service
1076
- effect((onCleanup) => {
1077
- let stopHandshakeListening = () => { };
1078
- const origin = this.clientOrigin();
1079
- const id = this.connect();
1080
- const source = this.iframeElement.contentWindow;
1081
- // listen for handshakes only if we know the origin and were given a connection ID
1082
- if (origin && source && id) {
1083
- try {
1084
- stopHandshakeListening = this.messageService.listen({ id, source, origin });
1085
- }
1086
- catch (e) {
1087
- logger.error(`Failed to start listening for (connection ID: ${id})`, e);
1088
- }
1089
- }
1090
- // stop listening for handshakes and disconnect previous connection when:
1091
- // - origin/connection ID change
1092
- // - the directive is destroyed
1093
- onCleanup(() => {
1094
- stopHandshakeListening();
1095
- this.messageService.disconnect();
1096
- });
1097
- });
1098
- }
1099
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1100
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", 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 }); }
1101
- }
1102
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ConnectDirective, decorators: [{
1103
- type: Directive,
1104
- args: [{
1105
- selector: 'iframe[connect]',
1106
- standalone: true
1107
- }]
1108
- }], ctorParameters: () => [], propDecorators: { srcAttr: [{
1109
- type: HostBinding,
1110
- args: ['src']
1111
- }] } });
1112
-
1113
- /**
1114
- * No operation function used to override the history API methods.
1115
- */
1116
- function noop() { }
1117
- /**
1118
- * If an iframe is not sandboxed or is of the same origin, navigation inside it will mess up the main window history.
1119
- * This disables writing to and overriding history API from inside the iframe to prevent this.
1120
- * Theoretically, this might break applications that rely on reading history API from inside the iframe.
1121
- *
1122
- * This should also allow having `CustomPathLocationStrategy` in the iframe if necessary.
1123
- */
1124
- function provideDisableHistoryWrites() {
1125
- return provideAppInitializer(() => {
1126
- Object.defineProperty(history, 'pushState', { value: noop, writable: false, configurable: false });
1127
- Object.defineProperty(history, 'replaceState', { value: noop, writable: false, configurable: false });
1128
- });
1129
- }
1130
-
1131
- /**
1132
- * Provide the communication protocol connection configuration
1133
- * @param connectionConfigOptions The identifier of the application in the communication protocol ecosystem plus the types of messages able to exchange and a logger object
1134
- */
1135
- function provideConnection(connectionConfigOptions) {
1136
- persistHostInfo();
1137
- const connectionId = (isEmbedded() && getHostInfo().moduleApplicationId) || connectionConfigOptions?.id;
1138
- if (!connectionId) {
1139
- (connectionConfigOptions?.logger || console).error('An id (moduleId) needs to be provided for the application in order to establish a connection inside the communication protocol');
1140
- return makeEnvironmentProviders([]);
1141
- }
1142
- const config = {
1143
- id: connectionId,
1144
- messageCheckStrategy: 'version',
1145
- knownMessages: [...KNOWN_MESSAGES, ...(connectionConfigOptions?.knownMessages || [])]
1146
- };
1147
- return makeEnvironmentProviders([
1148
- {
1149
- provide: MESSAGE_PEER_CONFIG, useValue: config
1150
- },
1151
- {
1152
- provide: MESSAGE_PEER_CONNECT_OPTIONS, useValue: getDefaultClientEndpointStartOptions()
1153
- },
1154
- {
1155
- // in the case of the ConnectionService will extend the base service 'useExisting' should be used
1156
- provide: MessagePeerService, useClass: MessagePeerService, deps: [MESSAGE_PEER_CONFIG]
1157
- },
1158
- // deactivate history writes to avoid embedded app writing to the host history
1159
- ...isEmbedded() ? [provideDisableHistoryWrites()] : []
1160
- ]);
1161
- }
1162
-
1163
1233
  /**
1164
1234
  * Generated bundle index. Do not edit.
1165
1235
  */
1166
1236
 
1167
- 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 };
1237
+ 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 };
1168
1238
  //# sourceMappingURL=ama-mfe-ng-utils.mjs.map