@monterosa/sdk-launcher-kit 0.19.0-rc.6 → 2.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  import { Logger, Sdk, getSdk, LogLevel, configure } from '@monterosa/sdk-core';
2
- import { createError, getGlobal, Emitter, subscribe, delay, checkAvailability, setItem, clear, getItem, removeItem, throttle } from '@monterosa/sdk-util';
3
- import { v4 } from 'uuid';
4
- import { fetchListings } from '@monterosa/sdk-interact-kit';
2
+ import { createError, getGlobal, Emitter, getErrorMessage, generateUUID, subscribe, delay, throttle } from '@monterosa/sdk-util';
3
+ import { fetchListings } from '@monterosa/sdk-interact-interop';
5
4
 
6
5
  /**
7
6
  * @license
@@ -45,7 +44,7 @@ const logger = new Logger('@monterosa/sdk-launcher-kit');
45
44
  * @license
46
45
  * @monterosa/sdk-launcher-kit
47
46
  *
48
- * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
47
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
49
48
  *
50
49
  * More details on the license can be found at https://www.monterosa.co/sdk/license
51
50
  */
@@ -58,23 +57,29 @@ const logger = new Logger('@monterosa/sdk-launcher-kit');
58
57
  var Action;
59
58
  (function (Action) {
60
59
  /**
61
- * Notifies that communication channel is ready and
62
- * messages can be {@link sendSdkMessage | sent}
60
+ * Notifies that the Experience has initialised and is ready to receive messages.
61
+ * This also signals that the communication bridge is ready.
62
+ * This triggers the `initialised` lifecycle state.
63
63
  */
64
- Action["OnReady"] = "onBridgeReady";
64
+ Action["OnInitialised"] = "onInitialised";
65
65
  /**
66
- * Notifies that intrisic size of child Experience has changed
66
+ * Notifies that UI of child Experience is loaded and ready to be visible.
67
+ * When this action is fired the loader will be hidden and
68
+ * the `ready` lifecycle state is triggered.
67
69
  */
68
- Action["OnResize"] = "onIntrinsicSizeChanged";
70
+ Action["OnReady"] = "onReady";
69
71
  /**
70
- * Notifies that UI of child Experience is loaded.
71
- * When this action is fired the loader will be hidden
72
+ * Notifies that intrinsic size of child Experience has changed
72
73
  */
73
- Action["OnUILoaded"] = "onUILoaded";
74
+ Action["OnResize"] = "onIntrinsicSizeChanged";
74
75
  /**
75
76
  * Notifies child Experience about the request to load more data
76
77
  */
77
78
  Action["OnMoreDataRequested"] = "onMoreDataRequested";
79
+ /**
80
+ * Notifies about a share request
81
+ */
82
+ Action["OnShare"] = "onShare";
78
83
  })(Action || (Action = {}));
79
84
  /**
80
85
  * @internal
@@ -89,7 +94,7 @@ var Source;
89
94
  * @license
90
95
  * @monterosa/sdk-launcher-kit
91
96
  *
92
- * Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
97
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
93
98
  *
94
99
  * More details on the license can be found at https://www.monterosa.co/sdk/license
95
100
  */
@@ -104,13 +109,13 @@ var QueryParam;
104
109
  */
105
110
  QueryParam["Host"] = "h";
106
111
  /**
107
- * Represents the Project Id of the application. This parameter is added by
112
+ * Represents the Project ID of the application. This parameter is added by
108
113
  * Studio and the embed URL already contains it.
109
114
  */
110
115
  QueryParam["Project"] = "p";
111
116
  /**
112
- * Represents the Event Id of the application. This parameter is added to the
113
- * experience URL when the eventId is provided in the experience config.
117
+ * Represents the Event ID of the application. This parameter is added to the
118
+ * Experience URL when the eventId is provided in the Experience config.
114
119
  */
115
120
  QueryParam["Event"] = "e";
116
121
  /**
@@ -172,9 +177,15 @@ var BridgeError;
172
177
  * This error is thrown when the specified timeout is not a positive number.
173
178
  */
174
179
  BridgeError["InvalidRequestTimeoutError"] = "invalid_request_timeout_error";
180
+ /**
181
+ * Indicates a request timed out waiting for a response.
182
+ * This error is thrown when a bridge request exceeds the specified timeout duration.
183
+ */
184
+ BridgeError["RequestTimeoutError"] = "request_timeout_error";
175
185
  })(BridgeError || (BridgeError = {}));
176
186
  const BridgeErrorMessages = {
177
187
  [BridgeError.InvalidRequestTimeoutError]: () => 'Request timeout must be greater than 0',
188
+ [BridgeError.RequestTimeoutError]: (action, timeout) => `Request timeout: action "${action}" did not receive a response within ${timeout}ms`,
178
189
  };
179
190
 
180
191
  /**
@@ -192,8 +203,9 @@ const Config = {
192
203
  requestTimeout: 20000,
193
204
  };
194
205
  /**
195
- * Sets a new timeout value for requests (default is 20_000 mseconds).
206
+ * Sets a new timeout value for requests (default is 20,000 ms).
196
207
  *
208
+ * @remarks
197
209
  * This function updates the request timeout in the application's configuration.
198
210
  * It ensures that the new timeout value is a positive number, and throws
199
211
  * an error if the value is non-positive.
@@ -259,9 +271,7 @@ function handleWindowMessage({ data }) {
259
271
  receiveMessage(message);
260
272
  }
261
273
  catch (err) {
262
- if (err instanceof Error) {
263
- console.error(err.message);
264
- }
274
+ console.error(getErrorMessage(err));
265
275
  }
266
276
  }
267
277
  /**
@@ -317,7 +327,7 @@ const VERSION$1 = '1.0.0';
317
327
  * @license
318
328
  * @monterosa/sdk-launcher-kit
319
329
  *
320
- * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
330
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
321
331
  *
322
332
  * More details on the license can be found at https://www.monterosa.co/sdk/license
323
333
  */
@@ -326,10 +336,10 @@ const globals = getGlobal();
326
336
  * @internal
327
337
  */
328
338
  class BridgeImpl extends Emitter {
329
- constructor(id = v4()) {
339
+ constructor(id = generateUUID()) {
330
340
  super();
331
341
  this.id = id;
332
- this.recipientReady = false;
342
+ this.recipientInitialised = false;
333
343
  this.messagesQueue = [];
334
344
  globals.monterosaSdk.emitter.on('message', this.handleMessage.bind(this));
335
345
  }
@@ -353,10 +363,10 @@ class BridgeImpl extends Emitter {
353
363
  return;
354
364
  }
355
365
  logger.log(`Received a ${respondingTo === null ? 'message' : 'response'}`, message);
356
- if (action === Action.OnReady) {
357
- this.recipientReady = true;
366
+ if (action === Action.OnInitialised) {
367
+ this.recipientInitialised = true;
358
368
  if (respondingTo === null) {
359
- this.send(Action.OnReady, {}, Source.Sdk, id);
369
+ this.send(Action.OnInitialised, {}, Source.Sdk, id);
360
370
  }
361
371
  while (this.messagesQueue.length > 0) {
362
372
  this.postMessage(this.messagesQueue.shift());
@@ -366,7 +376,7 @@ class BridgeImpl extends Emitter {
366
376
  }
367
377
  createMessage(action, payload, sourceName, respondingTo = null) {
368
378
  return {
369
- id: v4(),
379
+ id: generateUUID(),
370
380
  respondingTo,
371
381
  action,
372
382
  sourceName,
@@ -378,7 +388,7 @@ class BridgeImpl extends Emitter {
378
388
  }
379
389
  postMessage(message) {
380
390
  var _a, _b, _c, _d;
381
- if (!this.recipientReady && message.action !== Action.OnReady) {
391
+ if (!this.recipientInitialised && message.action !== Action.OnInitialised) {
382
392
  this.messagesQueue.push(message);
383
393
  return;
384
394
  }
@@ -413,8 +423,9 @@ class BridgeImpl extends Emitter {
413
423
  * Start the timeout, when it finishes it should reject Promise.race below
414
424
  */
415
425
  const countdown = new Promise((_, reject) => {
416
- timeoutRef = setTimeout(() => reject(new Error(`The request for action '${action}' exceeded the timeout ` +
417
- `limit of ${timeout}ms and was aborted.`)), timeout);
426
+ timeoutRef = setTimeout(() => {
427
+ reject(createError(BridgeError.RequestTimeoutError, BridgeErrorMessages, action, timeout));
428
+ }, timeout);
418
429
  });
419
430
  /**
420
431
  * Start the request and wait for the message with the respondingTo
@@ -450,7 +461,7 @@ class BridgeImpl extends Emitter {
450
461
  * @license
451
462
  * @monterosa/sdk-launcher-kit
452
463
  *
453
- * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
464
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
454
465
  *
455
466
  * More details on the license can be found at https://www.monterosa.co/sdk/license
456
467
  */
@@ -467,6 +478,24 @@ function getBridge(id) {
467
478
  bridges.set(id, bridge);
468
479
  return bridge;
469
480
  }
481
+ /**
482
+ * Returns true if the page is inside an iframe.
483
+ *
484
+ * @internal
485
+ */
486
+ function isInsideIframe() {
487
+ if (typeof window === 'undefined') {
488
+ return false;
489
+ }
490
+ try {
491
+ return window.self !== window.top;
492
+ }
493
+ catch (_a) {
494
+ // SecurityError is thrown when accessing window.top in a cross-origin iframe.
495
+ // If we can't access it, we're definitely in an iframe.
496
+ return true;
497
+ }
498
+ }
470
499
  /**
471
500
  * @internal
472
501
  */
@@ -477,6 +506,9 @@ function getParentBridge() {
477
506
  if (parentBridge !== undefined) {
478
507
  return parentBridge;
479
508
  }
509
+ if (!isInsideIframe()) {
510
+ return null;
511
+ }
480
512
  const url = new URL(window.location.href);
481
513
  const bridgeId = url.searchParams.get(QueryParam.BridgeId);
482
514
  if (bridgeId === null) {
@@ -492,9 +524,7 @@ function sendSdkMessage(bridged, action, payload = {}) {
492
524
  return bridged.bridge.send(action, payload, Source.Sdk);
493
525
  }
494
526
  /**
495
- * This function allows to send a simple message with action name and payload to
496
- * a recipient which can be either parent application (from Experience to parent
497
- * application) or child Experience (from parent page to Experience).
527
+ * Sends a message with an action name and payload to a parent application or child Experience.
498
528
  *
499
529
  * @remarks
500
530
  * Usage example in parent application:
@@ -503,13 +533,15 @@ function sendSdkMessage(bridged, action, payload = {}) {
503
533
  * ```typescript
504
534
  * const experience = getExperience();
505
535
  *
506
- * // message to Experience can be sent only when its ready
507
- * onUILoaded(experience, async () => {
508
- * sendMessage(
509
- * experience,
510
- * 'my_action',
511
- * { key: 'value' }
512
- * );
536
+ * // message to Experience can be sent only when its initialised
537
+ * onStateChanged(experience, (state) => {
538
+ * if (state === 'initialised') {
539
+ * sendMessage(
540
+ * experience,
541
+ * 'my_action',
542
+ * { key: 'value' }
543
+ * );
544
+ * }
513
545
  * });
514
546
  *
515
547
  * embed(experience);
@@ -545,29 +577,30 @@ function sendSdkRequest(bridged, action, payload = {}, timeout = Config.requestT
545
577
  return bridged.bridge.request(action, payload, timeout, Source.Sdk);
546
578
  }
547
579
  /**
548
- * This function allows to send a request with action name and payload to
549
- * a recipient which can be either parent application (from Experience to parent
550
- * application) or child Experience (from parent page to Experience). It is similar
551
- * to {@link sendMessage} with only one difference is that it returns a Promise which
552
- * resolves if the recipient response with {@link respondToMessage}. Otherwise it
553
- * will rejects after a certain timeout.
580
+ * Sends a request and returns a Promise that resolves when the recipient responds.
554
581
  *
555
582
  * @remarks
583
+ * Similar to {@link sendMessage} but returns a Promise which
584
+ * resolves if the recipient responds with {@link respondToMessage}. Otherwise it
585
+ * rejects after a certain timeout.
586
+ *
556
587
  * Usage example in parent application:
557
588
  *
558
589
  * @example
559
590
  * ```typescript
560
591
  * const experience = getExperience();
561
592
  *
562
- * // request to Experience can be sent only when its ready
563
- * onUILoaded(experience, async () => {
564
- * const response = await sendRequest(
565
- * experience,
566
- * 'my_action',
567
- * { key: 'value' }
568
- * );
593
+ * // request to Experience can be sent only when its initialised
594
+ * onStateChanged(experience, async (state) => {
595
+ * if (state === 'initialised') {
596
+ * const response = await sendRequest(
597
+ * experience,
598
+ * 'my_action',
599
+ * { key: 'value' }
600
+ * );
569
601
  *
570
- * console.log(response);
602
+ * console.log(response);
603
+ * }
571
604
  * });
572
605
  *
573
606
  * embed(experience);
@@ -631,6 +664,8 @@ function onSdkMessage(bridged, callback) {
631
664
  return onMessageFunc(bridged, Source.Sdk, callback);
632
665
  }
633
666
  /**
667
+ * @internal
668
+ *
634
669
  * Adds an observer for when user message is received
635
670
  *
636
671
  * @param bridged - Instance of either {@link ParentApplication} or {@link Experience}
@@ -646,7 +681,7 @@ function onMessage(bridged, callback) {
646
681
  * @license
647
682
  * @monterosa/sdk-launcher-kit
648
683
  *
649
- * Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
684
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
650
685
  *
651
686
  * More details on the license can be found at https://www.monterosa.co/sdk/license
652
687
  */
@@ -657,7 +692,9 @@ class ExperienceImpl {
657
692
  constructor(sdk, config) {
658
693
  this.sdk = sdk;
659
694
  this._parameters = {};
660
- this.id = v4();
695
+ this.id = generateUUID();
696
+ this.state = 'unmounted';
697
+ this.customElement = null;
661
698
  this._config = config;
662
699
  this.bridge = getBridge(this.id);
663
700
  if (config.parameters !== undefined) {
@@ -755,7 +792,7 @@ class ExperienceImpl {
755
792
  * @license
756
793
  * @monterosa/sdk-launcher-kit
757
794
  *
758
- * Copyright © 2025 Monterosa Productions Limited. All rights reserved.
795
+ * Copyright © 2025-2026 Monterosa Productions Limited. All rights reserved.
759
796
  *
760
797
  * More details on the license can be found at https://www.monterosa.co/sdk/license
761
798
  */
@@ -763,11 +800,11 @@ const integrations = new Map();
763
800
  function addIntegration(container, integration) {
764
801
  integrations.set(container, integration);
765
802
  }
766
- function getIntegration(container) {
767
- return integrations.get(container);
768
- }
769
- function getIntegrations() {
770
- return Array.from(integrations.values());
803
+ function getIntegration(containerOrExperience) {
804
+ if (containerOrExperience instanceof HTMLElement) {
805
+ return integrations.get(containerOrExperience);
806
+ }
807
+ return Array.from(integrations.values()).find((integration) => integration.experience === containerOrExperience);
771
808
  }
772
809
  function deleteIntegration(container) {
773
810
  integrations.delete(container);
@@ -779,6 +816,61 @@ function hasIntegration(containerOrExperience) {
779
816
  return Array.from(integrations.values()).some((integration) => integration.experience === containerOrExperience);
780
817
  }
781
818
 
819
+ /**
820
+ * @license
821
+ * @monterosa/sdk-launcher-kit
822
+ *
823
+ * Copyright © 2026 Monterosa Productions Limited. All rights reserved.
824
+ *
825
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
826
+ */
827
+ /**
828
+ * Event emitter for Experience state changes.
829
+ *
830
+ * @internal
831
+ */
832
+ const stateEmitter = new Emitter();
833
+ /**
834
+ * Updates the Experience state and emits a state change event.
835
+ *
836
+ * @param experience - The Experience instance to update
837
+ * @param newState - The target state to transition to
838
+ *
839
+ * @internal
840
+ */
841
+ function updateExperienceState(experience, newState) {
842
+ const currentState = experience.state;
843
+ // No-op if already in the target state
844
+ if (currentState === newState) {
845
+ return;
846
+ }
847
+ // Cast to ExperienceImpl to access the mutable state property
848
+ experience.state = newState;
849
+ stateEmitter.emit('stateChanged', experience, newState);
850
+ }
851
+ function onStateChanged(experienceOrCallback, callback) {
852
+ if (typeof experienceOrCallback === 'function') {
853
+ // Global handler: onStateChanged(callback)
854
+ const globalCallback = experienceOrCallback;
855
+ stateEmitter.on('stateChanged', globalCallback);
856
+ return () => {
857
+ stateEmitter.off('stateChanged', globalCallback);
858
+ };
859
+ }
860
+ // Instance handler: onStateChanged(experience, callback)
861
+ const experience = experienceOrCallback;
862
+ const instanceCallback = callback;
863
+ const wrappedCallback = (exp, state) => {
864
+ if (exp === experience) {
865
+ instanceCallback(state);
866
+ }
867
+ };
868
+ stateEmitter.on('stateChanged', wrappedCallback);
869
+ return () => {
870
+ stateEmitter.off('stateChanged', wrappedCallback);
871
+ };
872
+ }
873
+
782
874
  /**
783
875
  * @license
784
876
  * @monterosa/sdk-launcher-kit
@@ -972,13 +1064,13 @@ function unstashStyles(element) {
972
1064
  element.removeAttribute('data-stash');
973
1065
  }
974
1066
 
975
- var version = "0.19.0-rc.6";
1067
+ var version = "2.0.0-rc.2";
976
1068
 
977
1069
  /**
978
1070
  * @license
979
1071
  * @monterosa/sdk-launcher-kit
980
1072
  *
981
- * Copyright © 2022-2025 Monterosa Productions Limited. All rights reserved.
1073
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
982
1074
  *
983
1075
  * More details on the license can be found at https://www.monterosa.co/sdk/license
984
1076
  */
@@ -988,12 +1080,6 @@ var version = "0.19.0-rc.6";
988
1080
  * @internal
989
1081
  */
990
1082
  const VERSION = version;
991
- /**
992
- * Event emitter for the launcher kit.
993
- *
994
- * @internal
995
- */
996
- const emitter = new Emitter();
997
1083
  /**
998
1084
  * Creates an iframe with the provided parameters.
999
1085
  *
@@ -1018,6 +1104,9 @@ function createIFrame(url, options = {}) {
1018
1104
  if (options.allowFullScreen === true) {
1019
1105
  iframe.setAttribute('allowfullscreen', '');
1020
1106
  }
1107
+ if (options.title !== undefined) {
1108
+ iframe.setAttribute('title', options.title);
1109
+ }
1021
1110
  /**
1022
1111
  * Though scrolling attribute is deprecated we still have to rely on it as some
1023
1112
  * browsers (e.g. Chrome, as of now version 103) just ignores overflow: hidden.
@@ -1074,45 +1163,16 @@ function getExperience(sdkOrConfig, config) {
1074
1163
  const experience = new ExperienceImpl(sdk, experienceConfig);
1075
1164
  return experience;
1076
1165
  }
1077
- function onUILoaded(experience) {
1166
+ async function waitForAction(experience, action) {
1078
1167
  return new Promise((resolve) => {
1079
- const unsubscribe = onSdkMessage(experience, ({ action }) => {
1080
- if (action === Action.OnUILoaded) {
1168
+ const unsubscribe = onSdkMessage(experience, (message) => {
1169
+ if (message.action === action) {
1081
1170
  unsubscribe();
1082
1171
  resolve();
1083
1172
  }
1084
1173
  });
1085
1174
  });
1086
1175
  }
1087
- /**
1088
- * Subscribe to the event when Experience is embedded into the container.
1089
- * Also calls the callback immediately for all currently embedded Experiences.
1090
- *
1091
- * @param callback - Callback to be called when Experience is embedded.
1092
- */
1093
- function onExperienceEmbedded(callback) {
1094
- const integrations = getIntegrations();
1095
- for (const integration of integrations) {
1096
- if (integration.embedded) {
1097
- callback(integration.experience, integration.container);
1098
- }
1099
- }
1100
- emitter.on('embedded', callback);
1101
- return () => {
1102
- emitter.off('embedded', callback);
1103
- };
1104
- }
1105
- /**
1106
- * Subscribe to the event when Experience is unmounted from the container.
1107
- *
1108
- * @param callback - Callback to be called when Experience is unmounted.
1109
- */
1110
- function onExperienceUnmounted(callback) {
1111
- emitter.on('unmounted', callback);
1112
- return () => {
1113
- emitter.off('unmounted', callback);
1114
- };
1115
- }
1116
1176
  async function experienceReady(experience) {
1117
1177
  /**
1118
1178
  * Bumper delay during which loading state can't be hidden even
@@ -1124,10 +1184,7 @@ async function experienceReady(experience) {
1124
1184
  * be hidden even if Experience is still loading
1125
1185
  */
1126
1186
  const timeout = delay(LOADER_TIMEOUT);
1127
- /**
1128
- * Promisifying OnUILoaded communication bridge event
1129
- */
1130
- const loaded = onUILoaded(experience);
1187
+ const loaded = waitForAction(experience, Action.OnReady);
1131
1188
  const hasFullyLoadedExperience = Promise.all([bumper, loaded]);
1132
1189
  const eitherTimeoutOrFullyLoaded = Promise.race([
1133
1190
  timeout,
@@ -1136,10 +1193,12 @@ async function experienceReady(experience) {
1136
1193
  return eitherTimeoutOrFullyLoaded;
1137
1194
  }
1138
1195
  /**
1139
- * Embeds web Experience app into iframe. There is only one app can be
1140
- * associated with Experience and it is configured in
1141
- * Monterosa / Interaction Cloud. Please refer the developer guide to get
1142
- * more information on what is app and how to configure it:
1196
+ * Embeds a web Experience app into an iframe.
1197
+ *
1198
+ * @remarks
1199
+ * There is only one app that can be associated with an Experience and it is
1200
+ * configured in Monterosa / Interaction Cloud. Please refer to the developer
1201
+ * guide for more information:
1143
1202
  * {@link https://products.monterosa.co/mic/developer-guides/whats-an-app}
1144
1203
  *
1145
1204
  * @example
@@ -1148,8 +1207,8 @@ async function experienceReady(experience) {
1148
1207
  *
1149
1208
  * embed(experience, 'container-id');
1150
1209
  * ```
1151
- * @param {Experience} - An instance of Experience
1152
- * @param {HTMLElement | string} containerOrId - HTML element instance or
1210
+ * @param experience - An instance of Experience
1211
+ * @param containerOrId - HTML element instance or
1153
1212
  * element id where iframe is embedded into.
1154
1213
  *
1155
1214
  * @public
@@ -1175,7 +1234,6 @@ async function embed(experience, containerOrId) {
1175
1234
  container,
1176
1235
  experience,
1177
1236
  controller,
1178
- embedded: false,
1179
1237
  };
1180
1238
  addIntegration(container, integration);
1181
1239
  if (experience.config.supportsLoadingState === true) {
@@ -1192,23 +1250,67 @@ async function embed(experience, containerOrId) {
1192
1250
  scrolling: !experience.config.autoresizesHeight,
1193
1251
  allow: experience.config.allow,
1194
1252
  allowFullScreen: experience.config.allowFullScreen,
1253
+ title: experience.config.title,
1195
1254
  });
1196
1255
  container.appendChild(iframe);
1197
1256
  concealIFrame(iframe);
1198
- integration.embedded = true;
1199
- emitter.emit('embedded', experience, container);
1257
+ updateExperienceState(experience, 'mounted');
1258
+ await waitForAction(experience, Action.OnInitialised);
1259
+ if (controller.signal.aborted) {
1260
+ return;
1261
+ }
1262
+ updateExperienceState(experience, 'initialised');
1200
1263
  await experienceReady(experience);
1201
1264
  if (controller.signal.aborted) {
1202
1265
  return;
1203
1266
  }
1267
+ updateExperienceState(experience, 'ready');
1204
1268
  revealIFrame(iframe);
1205
1269
  if (experience.config.supportsLoadingState === true) {
1206
1270
  await hide(container);
1207
1271
  }
1208
1272
  }
1273
+ /**
1274
+ * Removes an Experience from its container and resets its state to `unmounted`.
1275
+ *
1276
+ * This is the preferred method for removing an embedded Experience.
1277
+ * Use {@link onStateChanged} to observe when the Experience transitions
1278
+ * to the `unmounted` state.
1279
+ *
1280
+ * @example
1281
+ * ```javascript
1282
+ * const experience = getExperience(sdk, { eventId: 'poll-123' });
1283
+ * embed(experience, container);
1284
+ *
1285
+ * // Later, to remove:
1286
+ * unembed(experience);
1287
+ * ```
1288
+ *
1289
+ * @param experience - The Experience instance to unembed
1290
+ *
1291
+ * @public
1292
+ */
1293
+ function unembed(experience) {
1294
+ const integration = getIntegration(experience);
1295
+ if (!integration) {
1296
+ throw new Error('This Experience is not currently embedded. ' +
1297
+ 'Please ensure the Experience is embedded before calling unembed().');
1298
+ }
1299
+ const { container } = integration;
1300
+ integration.controller.abort();
1301
+ while (container.lastElementChild) {
1302
+ container.removeChild(container.lastElementChild);
1303
+ }
1304
+ deleteIntegration(container);
1305
+ container.style.height = '';
1306
+ updateExperienceState(experience, 'unmounted');
1307
+ }
1209
1308
  /**
1210
1309
  * Unmounts web Experience app which was previously embedded in the container.
1211
1310
  *
1311
+ * @deprecated Use {@link unembed} instead, which takes an Experience instance
1312
+ * rather than a container.
1313
+ *
1212
1314
  * @example
1213
1315
  * ```javascript
1214
1316
  * unmount('container-id');
@@ -1220,6 +1322,7 @@ async function embed(experience, containerOrId) {
1220
1322
  * @public
1221
1323
  */
1222
1324
  function unmount(containerOrId) {
1325
+ console.warn('[launcher-kit] unmount() is deprecated. Use unembed(experience) instead.');
1223
1326
  const container = containerOrId instanceof HTMLElement
1224
1327
  ? containerOrId
1225
1328
  : document.getElementById(containerOrId);
@@ -1229,17 +1332,16 @@ function unmount(containerOrId) {
1229
1332
  }
1230
1333
  const integration = getIntegration(container);
1231
1334
  if (!integration) {
1232
- throw new Error(`No experience found embedded in container "${containerOrId}". ` +
1233
- 'Please ensure the experience is embedded before calling unmount().');
1335
+ throw new Error(`No Experience found embedded in container "${containerOrId}". ` +
1336
+ 'Please ensure the Experience is embedded before calling unmount().');
1234
1337
  }
1235
1338
  integration.controller.abort();
1236
1339
  while (container.lastElementChild) {
1237
1340
  container.removeChild(container.lastElementChild);
1238
1341
  }
1239
- integration.embedded = false;
1240
1342
  deleteIntegration(container);
1241
1343
  container.style.height = '';
1242
- emitter.emit('unmounted', integration.experience, container);
1344
+ updateExperienceState(integration.experience, 'unmounted');
1243
1345
  }
1244
1346
  /**
1245
1347
  * Informs the Experience that more data should be loaded and displayed on the UI.
@@ -1265,339 +1367,34 @@ function enableLogging(logLevelOrFlag = true) {
1265
1367
  * @license
1266
1368
  * @monterosa/sdk-launcher-kit
1267
1369
  *
1268
- * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
1370
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
1269
1371
  *
1270
1372
  * More details on the license can be found at https://www.monterosa.co/sdk/license
1271
1373
  */
1272
- class ParentApplicationImpl {
1273
- constructor(bridge) {
1274
- this.bridge = bridge;
1275
- }
1276
- }
1277
-
1278
- /**
1279
- * @license
1280
- * @monterosa/sdk-launcher-kit
1281
- *
1282
- * Copyright © 2023 Monterosa Productions Limited. All rights reserved.
1283
- *
1284
- * More details on the license can be found at https://www.monterosa.co/sdk/license
1285
- */
1286
- let parentApplication;
1287
- /**
1288
- * Returns instance of parent application.
1289
- *
1290
- * @returns Returns {@link ParentApplication}
1291
- * if an Experience resides within the parent application, and it was embedded
1292
- * using {@link embed} function. Otherwise it returns `null`.
1293
- */
1294
- function getParentApplication() {
1295
- if (parentApplication !== undefined) {
1296
- return parentApplication;
1297
- }
1298
- const parentBridge = getParentBridge();
1299
- if (parentBridge === null) {
1300
- return null;
1301
- }
1302
- parentApplication = new ParentApplicationImpl(parentBridge);
1303
- return parentApplication;
1304
- }
1305
-
1306
- /**
1307
- * @license
1308
- * @monterosa/sdk-launcher-kit
1309
- *
1310
- * Copyright © 2023 Monterosa Productions Limited. All rights reserved.
1311
- *
1312
- * More details on the license can be found at https://www.monterosa.co/sdk/license
1313
- */
1314
- class StorageImpl {
1374
+ const ELEMENT_NAME = 'monterosa-experience';
1375
+ class MonterosaExperienceElement extends HTMLElement {
1315
1376
  constructor() {
1316
- this.memoryStore = {};
1317
- this.accessible = checkAvailability();
1318
- this._persistent = false;
1319
- }
1320
- set persistent(newValue) {
1321
- const oldValue = this._persistent;
1322
- if (oldValue === newValue) {
1323
- return;
1324
- }
1325
- const swapToStorage = newValue === true && this.accessible;
1326
- const swapToMemory = newValue === false && this.accessible;
1327
- if (swapToStorage) {
1328
- for (const [key, value] of Object.entries(this.memoryStore)) {
1329
- setItem(key, value);
1330
- }
1331
- }
1332
- if (swapToMemory) {
1333
- clear();
1334
- }
1335
- this._persistent = newValue;
1336
- }
1337
- get persistent() {
1338
- return this._persistent;
1339
- }
1340
- getItem(key) {
1341
- if (this.persistent && this.accessible) {
1342
- return getItem(key);
1343
- }
1344
- if (Object.prototype.hasOwnProperty.call(this.memoryStore, key)) {
1345
- return this.memoryStore[key];
1346
- }
1347
- return null;
1348
- }
1349
- setItem(key, value) {
1350
- if (this.persistent && this.accessible) {
1351
- setItem(key, value);
1352
- }
1353
- this.memoryStore[key] = value;
1354
- }
1355
- removeItem(key) {
1356
- if (this.persistent && this.accessible) {
1357
- removeItem(key);
1358
- }
1359
- delete this.memoryStore[key];
1360
- }
1361
- clear() {
1362
- if (this.persistent && this.accessible) {
1363
- clear();
1364
- }
1365
- this.memoryStore = {};
1366
- }
1367
- }
1368
-
1369
- /**
1370
- * @license
1371
- * @monterosa/sdk-launcher-kit
1372
- *
1373
- * Copyright © 2023 Monterosa Productions Limited. All rights reserved.
1374
- *
1375
- * More details on the license can be found at https://www.monterosa.co/sdk/license
1376
- */
1377
- /**
1378
- * @internal
1379
- */
1380
- var StorageAction;
1381
- (function (StorageAction) {
1382
- StorageAction["Read"] = "storageRead";
1383
- StorageAction["Write"] = "storageWrite";
1384
- StorageAction["Remove"] = "storageRemove";
1385
- StorageAction["Clear"] = "storageClear";
1386
- })(StorageAction || (StorageAction = {}));
1387
- /**
1388
- * @internal
1389
- */
1390
- const storage = new StorageImpl();
1391
- /**
1392
- * @internal
1393
- */
1394
- const unsubs$1 = new Map();
1395
- /**
1396
- * @internal
1397
- */
1398
- function handleExperienceEmbedded$1(experience) {
1399
- const unsub = onSdkMessage(experience, (message) => {
1400
- if (!Object.values(StorageAction).includes(message.action)) {
1401
- return;
1402
- }
1403
- const { key, value } = message.payload;
1404
- const respond = (payload) => respondToSdkMessage(experience, message, payload);
1405
- try {
1406
- switch (message.action) {
1407
- case StorageAction.Read:
1408
- if (key === undefined) {
1409
- throw new Error('Key is required');
1410
- }
1411
- respond({
1412
- key,
1413
- value: storage.getItem(key),
1414
- });
1415
- break;
1416
- case StorageAction.Write:
1417
- if (key === undefined || value === undefined) {
1418
- throw new Error('Key and value are required');
1419
- }
1420
- storage.setItem(key, value);
1421
- respond({ key });
1422
- break;
1423
- case StorageAction.Remove:
1424
- if (key === undefined) {
1425
- throw new Error('Key is required');
1426
- }
1427
- storage.removeItem(key);
1428
- respond({ key });
1429
- break;
1430
- case StorageAction.Clear:
1431
- storage.clear();
1432
- respond();
1433
- break;
1434
- }
1435
- }
1436
- catch (error) {
1437
- if (error instanceof Error) {
1438
- respond(Object.assign(Object.assign({}, (key !== undefined && { key })), { error: error.message }));
1439
- }
1440
- }
1441
- });
1442
- unsubs$1.set(experience.id, unsub);
1443
- }
1444
- /**
1445
- * @internal
1446
- */
1447
- function handleExperienceUnmounted$1(experience) {
1448
- const unsub = unsubs$1.get(experience.id);
1449
- if (unsub) {
1450
- unsub();
1451
- unsubs$1.delete(experience.id);
1452
- }
1453
- }
1454
- /**
1455
- * The `setStoragePersistent` function is a simple function that allows to
1456
- * control the persistence of the SDK storage. If the argument `persistent` is
1457
- * set to `true`, then the storage will be persistent across browser sessions
1458
- * and if it is set to `false`, then the storage will save to memory.
1459
- * It is important to note that the use of persistent storage may be subject
1460
- * to laws and regulations, such as those related to data privacy and protection.
1461
- *
1462
- * @remarks
1463
- * - We transition from persistent to memory and memory to persistent in
1464
- * a seamless manner for you
1465
- *
1466
- * - By default we store in memory
1467
- *
1468
- * - The value of storage persistent persists across session (aka put it to true,
1469
- * it will remain so, put it back to false, it will remain so)
1470
- *
1471
- * - You have the responsibility to comply with any laws and regulations, and
1472
- * store only the data if you know it's valid to do so
1473
- *
1474
- * @param persistent - Determines whether or not SDK storage should persist
1475
- * across browser sessions.
1476
- */
1477
- function setStoragePersistent(persistent) {
1478
- storage.persistent = persistent;
1479
- }
1480
- /**
1481
- * The function allows to read data from the SDK storage.
1482
- *
1483
- * @param key - The name of the item to be read from storage.
1484
- *
1485
- * @throws
1486
- * The function throws an error if there is a timeout reading the data
1487
- * from the parent application storage.
1488
- *
1489
- * @returns A promise that resolves to the value
1490
- * of the item in storage or `null` if the item doesn't exist.
1491
- */
1492
- async function storageRead(key) {
1493
- const parentApp = getParentApplication();
1494
- let value = null;
1495
- if (parentApp !== null) {
1496
- const result = await sendSdkRequest(parentApp, StorageAction.Read, {
1497
- key,
1498
- });
1499
- if (result.payload.error !== undefined) {
1500
- throw new Error(result.payload.error);
1501
- }
1502
- value = result.payload.value;
1377
+ super(...arguments);
1378
+ this._experience = null;
1503
1379
  }
1504
- else {
1505
- value = storage.getItem(key);
1506
- }
1507
- return Promise.resolve(value);
1508
- }
1509
- /**
1510
- * The function allows to write data to the SDK storage.
1511
- *
1512
- * @param key - A name of the item to be stored.
1513
- * @param value - A value to be stored.
1514
- *
1515
- * @throws
1516
- * The function throws an error if there is a timeout writing the data
1517
- * to the parent's application storage.
1518
- *
1519
- * @returns A promise that resolves to `void` once the data has
1520
- * been successfully stored.
1521
- */
1522
- async function storageWrite(key, value) {
1523
- const parentApp = getParentApplication();
1524
- if (parentApp !== null) {
1525
- const result = await sendSdkRequest(parentApp, StorageAction.Write, {
1526
- key,
1527
- value,
1528
- });
1529
- if (result.payload.error !== undefined) {
1530
- throw new Error(result.payload.error);
1531
- }
1532
- }
1533
- else {
1534
- storage.setItem(key, value);
1535
- }
1536
- return Promise.resolve();
1537
- }
1538
- /**
1539
- * The function allows to remove an item from the SDK storage.
1540
- *
1541
- * @param key - A name of the item to be removed.
1542
- *
1543
- * @throws
1544
- * The function throws an error if there is a timeout removing the data
1545
- * from the parent's application storage.
1546
- *
1547
- * @returns A promise that resolves to `void` once the data has
1548
- * been successfully removed.
1549
- */
1550
- async function storageRemove(key) {
1551
- const parentApp = getParentApplication();
1552
- if (parentApp !== null) {
1553
- const result = await sendSdkRequest(parentApp, StorageAction.Remove, {
1554
- key,
1555
- });
1556
- if (result.payload.error !== undefined) {
1557
- throw new Error(result.payload.error);
1558
- }
1559
- }
1560
- else {
1561
- storage.removeItem(key);
1380
+ /**
1381
+ * The host URL for the Experience.
1382
+ */
1383
+ get host() {
1384
+ return this.getAttribute('host') || '';
1562
1385
  }
1563
- return Promise.resolve();
1564
- }
1565
- /**
1566
- * The function allows to clear all data from the SDK storage.
1567
- *
1568
- * @throws
1569
- * The function throws an error if there is a timeout clearing the parent's
1570
- * application storage.
1571
- *
1572
- * @returns A promise that resolves to `void` once the data has been
1573
- * successfully cleared.
1574
- */
1575
- async function storageClear() {
1576
- const parentApp = getParentApplication();
1577
- if (parentApp !== null) {
1578
- const result = await sendSdkRequest(parentApp, StorageAction.Clear);
1579
- if (result.payload.error !== undefined) {
1580
- throw new Error(result.payload.error);
1581
- }
1386
+ /**
1387
+ * The project identifier.
1388
+ */
1389
+ get projectId() {
1390
+ return this.getAttribute('projectId') || '';
1582
1391
  }
1583
- else {
1584
- storage.clear();
1392
+ /**
1393
+ * The event identifier (optional).
1394
+ */
1395
+ get eventId() {
1396
+ return this.getAttribute('eventId') || undefined;
1585
1397
  }
1586
- return Promise.resolve();
1587
- }
1588
- onExperienceEmbedded(handleExperienceEmbedded$1);
1589
- onExperienceUnmounted(handleExperienceUnmounted$1);
1590
-
1591
- /**
1592
- * @license
1593
- * @monterosa/sdk-launcher-kit
1594
- *
1595
- * Copyright © 2022-2025 Monterosa Productions Limited. All rights reserved.
1596
- *
1597
- * More details on the license can be found at https://www.monterosa.co/sdk/license
1598
- */
1599
- const ELEMENT_NAME = 'monterosa-experience';
1600
- class MonterosaExperienceElement extends HTMLElement {
1601
1398
  async connectedCallback() {
1602
1399
  const host = this.getAttribute('host');
1603
1400
  if (host === null) {
@@ -1609,7 +1406,6 @@ class MonterosaExperienceElement extends HTMLElement {
1609
1406
  }
1610
1407
  const autoresizesHeight = this.getAttribute('autoresizesHeight') !== null;
1611
1408
  const hidesHeadersAndFooters = this.getAttribute('hidesHeadersAndFooters') !== null;
1612
- const persistentStorage = this.getAttribute('persistentStorage') !== null;
1613
1409
  const eventId = this.getAttribute('eventId') !== null
1614
1410
  ? this.getAttribute('eventId')
1615
1411
  : undefined;
@@ -1628,6 +1424,9 @@ class MonterosaExperienceElement extends HTMLElement {
1628
1424
  parameters[paramName] = attr.value;
1629
1425
  }
1630
1426
  }
1427
+ const title = this.getAttribute('title') !== null
1428
+ ? this.getAttribute('title')
1429
+ : undefined;
1631
1430
  const sdk = configure({
1632
1431
  host,
1633
1432
  projectId,
@@ -1640,25 +1439,23 @@ class MonterosaExperienceElement extends HTMLElement {
1640
1439
  allow,
1641
1440
  allowFullScreen,
1642
1441
  parameters,
1442
+ title,
1643
1443
  });
1644
- this.style.height = '250px';
1645
- this.style.display = 'block';
1646
- this.style.position = 'relative';
1647
- if (persistentStorage) {
1648
- setStoragePersistent(true);
1649
- }
1444
+ // Link the custom element to the experience
1445
+ experience.customElement = this;
1446
+ this._experience = experience;
1650
1447
  embed(experience, this);
1651
1448
  // eslint-disable-next-line
1652
1449
  onSdkMessage(experience, ({ payload, action }) => {
1653
1450
  switch (action) {
1654
- case Action.OnReady:
1451
+ case Action.OnInitialised:
1655
1452
  break;
1656
1453
  case Action.OnResize: {
1657
1454
  // const { width, height } = payload;
1658
1455
  break;
1659
1456
  }
1660
- case Action.OnUILoaded:
1661
- this.dispatchEvent(new CustomEvent('uiloaded', {
1457
+ case Action.OnReady:
1458
+ this.dispatchEvent(new CustomEvent('ready', {
1662
1459
  bubbles: true,
1663
1460
  cancelable: false,
1664
1461
  composed: true,
@@ -1668,7 +1465,16 @@ class MonterosaExperienceElement extends HTMLElement {
1668
1465
  });
1669
1466
  }
1670
1467
  disconnectedCallback() {
1671
- unmount(this);
1468
+ const experience = this._experience;
1469
+ this._experience = null;
1470
+ if (experience) {
1471
+ try {
1472
+ unembed(experience);
1473
+ }
1474
+ catch (_a) {
1475
+ // Experience may already have been unembedded
1476
+ }
1477
+ }
1672
1478
  }
1673
1479
  }
1674
1480
  if (!customElements.get(ELEMENT_NAME)) {
@@ -1679,15 +1485,20 @@ if (!customElements.get(ELEMENT_NAME)) {
1679
1485
  * @license
1680
1486
  * @monterosa/sdk-launcher-kit
1681
1487
  *
1682
- * Copyright © 2025 Monterosa Productions Limited. All rights reserved.
1488
+ * Copyright © 2025-2026 Monterosa Productions Limited. All rights reserved.
1683
1489
  *
1684
1490
  * More details on the license can be found at https://www.monterosa.co/sdk/license
1685
1491
  */
1686
- const unsubs = new Map();
1687
- function handleExperienceEmbedded(experience, container) {
1492
+ const unsubs$1 = new Map();
1493
+ function handleExperienceEmbedded$1(experience) {
1688
1494
  if (!experience.config.autoresizesHeight) {
1689
1495
  return;
1690
1496
  }
1497
+ const integration = getIntegration(experience);
1498
+ if (!integration) {
1499
+ return;
1500
+ }
1501
+ const { container } = integration;
1691
1502
  const unsub = onSdkMessage(experience, ({ action, payload }) => {
1692
1503
  if (action === Action.OnResize) {
1693
1504
  /**
@@ -1698,17 +1509,23 @@ function handleExperienceEmbedded(experience, container) {
1698
1509
  container.style.height = `${payload.height}px`;
1699
1510
  }
1700
1511
  });
1701
- unsubs.set(experience.id, unsub);
1512
+ unsubs$1.set(experience.id, unsub);
1702
1513
  }
1703
- function handleExperienceUnmounted(experience) {
1704
- const unsub = unsubs.get(experience.id);
1514
+ function handleExperienceUnmounted$1(experience) {
1515
+ const unsub = unsubs$1.get(experience.id);
1705
1516
  if (unsub) {
1706
1517
  unsub();
1707
- unsubs.delete(experience.id);
1518
+ unsubs$1.delete(experience.id);
1708
1519
  }
1709
1520
  }
1710
- onExperienceEmbedded(handleExperienceEmbedded);
1711
- onExperienceUnmounted(handleExperienceUnmounted);
1521
+ onStateChanged((experience, state) => {
1522
+ if (state === 'mounted') {
1523
+ handleExperienceEmbedded$1(experience);
1524
+ }
1525
+ else if (state === 'unmounted') {
1526
+ handleExperienceUnmounted$1(experience);
1527
+ }
1528
+ });
1712
1529
 
1713
1530
  /**
1714
1531
  * @license
@@ -1718,6 +1535,51 @@ onExperienceUnmounted(handleExperienceUnmounted);
1718
1535
  *
1719
1536
  * More details on the license can be found at https://www.monterosa.co/sdk/license
1720
1537
  */
1538
+ /**
1539
+ * @internal
1540
+ */
1541
+ class ParentApplicationImpl {
1542
+ constructor(bridge) {
1543
+ this.bridge = bridge;
1544
+ }
1545
+ }
1546
+
1547
+ /**
1548
+ * @license
1549
+ * @monterosa/sdk-launcher-kit
1550
+ *
1551
+ * Copyright © 2023 Monterosa Productions Limited. All rights reserved.
1552
+ *
1553
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1554
+ */
1555
+ let parentApplication;
1556
+ /**
1557
+ * Returns instance of parent application.
1558
+ *
1559
+ * @returns Returns {@link ParentApplication}
1560
+ * if an Experience resides within the parent application, and it was embedded
1561
+ * using {@link embed} function. Otherwise it returns `null`.
1562
+ */
1563
+ function getParentApplication() {
1564
+ if (parentApplication !== undefined) {
1565
+ return parentApplication;
1566
+ }
1567
+ const parentBridge = getParentBridge();
1568
+ if (parentBridge === null) {
1569
+ return null;
1570
+ }
1571
+ parentApplication = new ParentApplicationImpl(parentBridge);
1572
+ return parentApplication;
1573
+ }
1574
+
1575
+ /**
1576
+ * @license
1577
+ * @monterosa/sdk-launcher-kit
1578
+ *
1579
+ * Copyright © 2022-2026 Monterosa Productions Limited. All rights reserved.
1580
+ *
1581
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1582
+ */
1721
1583
  function getUrlParam(param) {
1722
1584
  let queryString = {};
1723
1585
  if (typeof window !== 'undefined') {
@@ -1727,12 +1589,13 @@ function getUrlParam(param) {
1727
1589
  return urlParams.get(param);
1728
1590
  }
1729
1591
  /**
1592
+ * Returns whether the Experience should hide its header and footer.
1593
+ *
1594
+ * @remarks
1730
1595
  * The SDK on the parent application will by default request a child
1731
1596
  * Experience to hide its header and footer by setting a query parameter called
1732
1597
  * `micHideHeaderAndFooter` to `1`.
1733
1598
  *
1734
- * This function will return true if that is the case, and false otherwise.
1735
- *
1736
1599
  * When true, the developer of a child Experience is expected to hide it's headers
1737
1600
  * and footers as the parent application is actively providing them.
1738
1601
  *
@@ -1753,51 +1616,70 @@ function isAutoresizesHeight() {
1753
1616
  return getUrlParam(QueryParam.AutoresizesHeight) === '1';
1754
1617
  }
1755
1618
  /**
1756
- * This function is used to notify the parent application that the Experience
1757
- * is ready to be used. It's intended to be called by the Experience itself and
1758
- * not by the parent application.
1619
+ * Notifies the parent application that the Experience has initialised and
1620
+ * is ready to receive messages. This triggers the `initialised` lifecycle state.
1621
+ *
1622
+ * Call this method when your Experience has completed its initial setup and
1623
+ * is ready to receive configuration or other messages from the parent application.
1759
1624
  *
1760
1625
  * @example
1761
1626
  * ```javascript
1762
- * import { sendReady } from '@monterosa/sdk-launcher-kit';
1627
+ * import { sendInitialised } from '@monterosa/sdk-launcher-kit';
1763
1628
  *
1764
- * sendReady();
1629
+ * // After your Experience has initialised
1630
+ * sendInitialised();
1765
1631
  * ```
1766
- *
1767
- * @returns void
1768
1632
  */
1769
- function sendReady() {
1633
+ function sendInitialised() {
1770
1634
  const parentApp = getParentApplication();
1771
1635
  if (parentApp === null) {
1772
- console.log('Unable to send ready message, as there is no parent application');
1636
+ console.log('Unable to send initialised message, as there is no parent application');
1773
1637
  return;
1774
1638
  }
1775
- sendSdkRequest(parentApp, Action.OnReady);
1639
+ sendSdkRequest(parentApp, Action.OnInitialised);
1776
1640
  }
1777
1641
  /**
1778
- * Sends a message to the SDK of the parent application informing it that
1779
- * the UI of this Experience has completed loading.
1642
+ * Notifies the parent application that the Experience UI is fully loaded
1643
+ * and ready to be displayed. This triggers the `ready` lifecycle state.
1644
+ *
1645
+ * @remarks
1646
+ * Call this method when your UI has completed loading and you are ready to
1647
+ * display data to the user. This can be when your network calls have successfully
1648
+ * requested data and you have updated the DOM, or when an error has occurred and
1649
+ * you want to take over the UI to provide details to the user.
1780
1650
  *
1781
- * You should call this method when your UI has completed loading and you are ready to
1782
- * display data to the user. This can be when your network calls have successfully requested
1783
- * data and you have updated the DOM for it, or when an error has occurred and you want
1784
- * to take over the UI to provide details to the user.
1651
+ * The SDK will use this message to dismiss the loading indicator, allowing a
1652
+ * seamless transition between a native loading UI and the fully loaded Experience.
1785
1653
  *
1786
- * The SDK will use this message to dismiss the loading indicator, allowing a seamless
1787
- * transition between a native loading UI, into a fully loaded, child Experience.
1654
+ * If this call is not made, the parent SDK will add a grace period to ensure
1655
+ * the web app has sufficient time to load, and eventually dismiss the loading
1656
+ * state to avoid infinite loading scenarios.
1657
+ *
1658
+ * @example
1659
+ * ```javascript
1660
+ * import { sendReady } from '@monterosa/sdk-launcher-kit';
1788
1661
  *
1789
- * If this call is not made, the parent SDK will add a grace period to ensure the web app
1790
- * has sufficient time to load, and eventually dismiss the loading state, whether Experience
1791
- * is ready or not, so as to avoid scenarios were a defect leaves an infinite loading state
1792
- * presented to the user.
1662
+ * // After your UI has finished loading
1663
+ * sendReady();
1664
+ * ```
1793
1665
  */
1794
- function sendFinishedLoadingUI() {
1666
+ function sendReady() {
1795
1667
  const parentApp = getParentApplication();
1796
1668
  if (parentApp === null) {
1797
- console.log('Unable to send finished loading UI message, as there is no parent application');
1669
+ console.log('Unable to send ready message, as there is no parent application');
1798
1670
  return;
1799
1671
  }
1800
- sendSdkMessage(parentApp, Action.OnUILoaded);
1672
+ sendSdkMessage(parentApp, Action.OnReady);
1673
+ }
1674
+ /**
1675
+ * @deprecated Use {@link sendReady} instead.
1676
+ *
1677
+ * Sends a message to the SDK of the parent application informing it that
1678
+ * the UI of this Experience has completed loading.
1679
+ */
1680
+ function sendFinishedLoadingUI() {
1681
+ console.warn('[launcher-kit] sendFinishedLoadingUI() is deprecated. Use sendReady() instead.');
1682
+ sendReady();
1801
1683
  }
1802
1684
  /**
1803
1685
  * Adds an observer for when more data is requested by the parent application
@@ -1823,14 +1705,20 @@ function onMoreDataRequested(callback) {
1823
1705
  * Adds an observer for when experience is fully embedded and ready to accept
1824
1706
  * incoming messages
1825
1707
  *
1708
+ * @deprecated Use `onStateChanged(experience, (state) => {
1709
+ * if (state === 'initialised') ...
1710
+ * })` instead.
1711
+ *
1826
1712
  * @param experience - Experience instance
1827
1713
  * @param callback - The callback that is triggered when experience is embedded
1828
1714
  * @returns The unsubscribe function. When it's called, the observer will be
1829
1715
  * removed and ready event will not be received
1830
1716
  */
1831
1717
  function onReady(experience, callback) {
1718
+ console.warn('[launcher-kit] onReady() is deprecated. Use onStateChanged(' +
1719
+ "experience, (state) => { if (state === 'initialised') ... }) instead.");
1832
1720
  return onSdkMessage(experience, ({ action }) => {
1833
- if (action === Action.OnReady) {
1721
+ if (action === Action.OnInitialised) {
1834
1722
  callback();
1835
1723
  }
1836
1724
  });
@@ -1848,10 +1736,10 @@ const sendExperienceSizeThrottled = throttle((width, height) => {
1848
1736
  trailing: true,
1849
1737
  });
1850
1738
  /**
1851
- * Sends the experience size to the parent application.
1739
+ * Sends the Experience size to the parent page.
1852
1740
  *
1853
- * @param width - The width of the experience.
1854
- * @param height - The height of the experience.
1741
+ * @param width - The width of the Experience.
1742
+ * @param height - The height of the Experience.
1855
1743
  */
1856
1744
  function sendExperienceSize(width, height) {
1857
1745
  sendExperienceSizeThrottled(width, height);
@@ -1874,5 +1762,134 @@ function reportExperienceSizeChanges(element) {
1874
1762
  return () => observer.unobserve(element);
1875
1763
  }
1876
1764
 
1877
- export { Action, VERSION$1 as BRIDGE_VERSION, BridgeError, BridgeImpl, QueryParam, Source, StorageAction, VERSION, embed, emitter, enableLogging, getBridge, getExperience, getParentApplication, getParentBridge, handleExperienceEmbedded$1 as handleExperienceEmbedded, handleExperienceUnmounted$1 as handleExperienceUnmounted, isAutoresizesHeight, onExperienceEmbedded, onExperienceUnmounted, onMessage, onMoreDataRequested, onReady, onSdkMessage, reportExperienceSizeChanges, requestMoreData, respondToMessage, respondToSdkMessage, sendExperienceSize, sendExperienceSizeThrottled, sendFinishedLoadingUI, sendMessage, sendReady, sendRequest, sendSdkMessage, sendSdkRequest, setRequestTimeout, setStoragePersistent, shouldHideHeaderAndFooter, storage, storageClear, storageRead, storageRemove, storageWrite, unmount };
1878
- //# sourceMappingURL=index.esm.js.map
1765
+ /**
1766
+ * @license
1767
+ * share.ts
1768
+ * launcher-kit
1769
+ *
1770
+ * Copyright © 2026 Monterosa Productions Limited. All rights reserved.
1771
+ *
1772
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1773
+ */
1774
+ /**
1775
+ * Share requests block on the native share tray which waits for user
1776
+ * interaction (composing a message, picking a target app, etc.). The default
1777
+ * bridge timeout of 20 seconds is too short for this. 5 minutes gives users
1778
+ * ample time while still acting as a safety net against lost messages.
1779
+ */
1780
+ const SHARE_REQUEST_TIMEOUT = 300000;
1781
+ var ShareError;
1782
+ (function (ShareError) {
1783
+ ShareError["ParentAppError"] = "parent_app_error";
1784
+ ShareError["ShareFailed"] = "share_failed";
1785
+ })(ShareError || (ShareError = {}));
1786
+ const ShareErrorMessages = {
1787
+ [ShareError.ParentAppError]: (error) => `Parent application error: ${error}`,
1788
+ [ShareError.ShareFailed]: (error) => `Share failed: ${error}`,
1789
+ };
1790
+ /**
1791
+ * Executes share using the Web Share API.
1792
+ *
1793
+ * @internal
1794
+ */
1795
+ async function executeShare(data) {
1796
+ try {
1797
+ await navigator.share({
1798
+ url: data.url,
1799
+ title: data.title,
1800
+ text: data.description,
1801
+ });
1802
+ return 'success';
1803
+ }
1804
+ catch (err) {
1805
+ if (err instanceof DOMException && err.name === 'AbortError') {
1806
+ return 'cancelled';
1807
+ }
1808
+ throw createError(ShareError.ShareFailed, ShareErrorMessages, getErrorMessage(err));
1809
+ }
1810
+ }
1811
+ /**
1812
+ * Initiates a share action. When running inside a parent application,
1813
+ * the share request is sent to the parent via the communication bridge.
1814
+ * When running standalone, `navigator.share()` is called directly.
1815
+ *
1816
+ * @param data - The data to share
1817
+ */
1818
+ async function share(data) {
1819
+ const parentApp = getParentApplication();
1820
+ if (parentApp !== null) {
1821
+ const response = await sendSdkRequest(parentApp, Action.OnShare, data, SHARE_REQUEST_TIMEOUT);
1822
+ if (response.payload.result === 'failure') {
1823
+ throw createError(ShareError.ParentAppError, ShareErrorMessages, response.payload.message);
1824
+ }
1825
+ // 'success' and 'cancelled' both resolve silently
1826
+ return;
1827
+ }
1828
+ await executeShare(data);
1829
+ }
1830
+ /**
1831
+ * Registers a callback to be called when a share request is received
1832
+ * from a child Experience.
1833
+ *
1834
+ * @param experience - The Experience instance to listen on
1835
+ * @param callback - Called with ShareData when a share request is received
1836
+ * @returns Unsubscribe function
1837
+ */
1838
+ function onShare(experience, callback) {
1839
+ return onSdkMessage(experience, (message) => {
1840
+ if (message.action === Action.OnShare) {
1841
+ callback(message.payload);
1842
+ }
1843
+ });
1844
+ }
1845
+ const unsubs = new Map();
1846
+ function handleExperienceEmbedded(experience) {
1847
+ const unsub = onSdkMessage(experience, async (message) => {
1848
+ if (message.action !== Action.OnShare) {
1849
+ return;
1850
+ }
1851
+ try {
1852
+ const parentApp = getParentApplication();
1853
+ let payload;
1854
+ if (parentApp !== null) {
1855
+ const response = await sendSdkRequest(parentApp, Action.OnShare, message.payload, SHARE_REQUEST_TIMEOUT);
1856
+ payload = response.payload;
1857
+ }
1858
+ else {
1859
+ const result = await executeShare(message.payload);
1860
+ payload = {
1861
+ result,
1862
+ message: result === 'success' ? 'Share successful' : 'Share cancelled',
1863
+ data: {},
1864
+ };
1865
+ }
1866
+ respondToSdkMessage(experience, message, payload);
1867
+ }
1868
+ catch (err) {
1869
+ respondToSdkMessage(experience, message, {
1870
+ result: 'failure',
1871
+ message: getErrorMessage(err),
1872
+ data: {},
1873
+ });
1874
+ }
1875
+ });
1876
+ unsubs.set(experience.id, unsub);
1877
+ }
1878
+ function handleExperienceUnmounted(experience) {
1879
+ const unsub = unsubs.get(experience.id);
1880
+ if (unsub) {
1881
+ unsub();
1882
+ unsubs.delete(experience.id);
1883
+ }
1884
+ }
1885
+ onStateChanged((experience, state) => {
1886
+ if (state === 'mounted') {
1887
+ handleExperienceEmbedded(experience);
1888
+ }
1889
+ else if (state === 'unmounted') {
1890
+ handleExperienceUnmounted(experience);
1891
+ }
1892
+ });
1893
+
1894
+ export { Action, VERSION$1 as BRIDGE_VERSION, BridgeError, BridgeImpl, ExperienceImpl, ParentApplicationImpl, QueryParam, ShareError, ShareErrorMessages, Source, VERSION, embed, enableLogging, executeShare, getBridge, getExperience, getParentApplication, getParentBridge, isAutoresizesHeight, isInsideIframe, onMessage, onMoreDataRequested, onReady, onSdkMessage, onShare, onStateChanged, reportExperienceSizeChanges, requestMoreData, respondToMessage, respondToSdkMessage, sendExperienceSize, sendExperienceSizeThrottled, sendFinishedLoadingUI, sendInitialised, sendMessage, sendReady, sendRequest, sendSdkMessage, sendSdkRequest, setRequestTimeout, share, shouldHideHeaderAndFooter, stateEmitter, unembed, unmount, updateExperienceState };
1895
+ //# sourceMappingURL=index.js.map