@elliemae/pui-app-bridge 2.9.4 → 2.16.6

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.
Files changed (102) hide show
  1. package/dist/cjs/appBridge.js +380 -93
  2. package/dist/cjs/appRegistry.js +136 -0
  3. package/dist/cjs/config/app.js +15 -2
  4. package/dist/cjs/config/microFE.js +3 -3
  5. package/dist/cjs/eventManager.js +16 -16
  6. package/dist/cjs/frame.html +2 -2
  7. package/dist/cjs/frame.js +39 -14
  8. package/dist/cjs/index.html +1 -1
  9. package/dist/cjs/index.js +3 -3
  10. package/dist/cjs/loaders/script.js +5 -5
  11. package/dist/cjs/loaders/style.js +1 -0
  12. package/dist/cjs/microfeHost.js +51 -31
  13. package/dist/cjs/tests/flights/23.1/app.checksum1.js +25 -24
  14. package/dist/cjs/tests/flights/latest/app.checksum.js +25 -24
  15. package/dist/cjs/tests/hotels/23.1/app.checksum.js +27 -24
  16. package/dist/cjs/tests/hotels/latest/app.checksum.js +27 -24
  17. package/dist/cjs/tests/loan/latest/index.js +49 -57
  18. package/dist/cjs/tests/scriptingObjects/analytics.js +7 -7
  19. package/dist/cjs/tests/scriptingObjects/appraisalServiceModule.js +8 -8
  20. package/dist/cjs/tests/scriptingObjects/global.js +1 -2
  21. package/dist/cjs/tests/task/latest/index.dev.js +29 -28
  22. package/dist/cjs/tests/task/latest/index.js +29 -28
  23. package/dist/cjs/tests/travelhub/23.1/app.checksum.js +24 -26
  24. package/dist/cjs/tests/travelhub/23.1/landing.checksum1.js +5 -7
  25. package/dist/cjs/utils.js +31 -1
  26. package/dist/esm/appBridge.js +390 -95
  27. package/dist/esm/appRegistry.js +116 -0
  28. package/dist/esm/config/app.js +15 -2
  29. package/dist/esm/config/microFE.js +3 -3
  30. package/dist/esm/eventManager.js +16 -16
  31. package/dist/esm/frame.html +2 -2
  32. package/dist/esm/frame.js +29 -14
  33. package/dist/esm/index.html +1 -1
  34. package/dist/esm/loaders/script.js +5 -5
  35. package/dist/esm/loaders/style.js +1 -0
  36. package/dist/esm/microfeHost.js +55 -31
  37. package/dist/esm/tests/flights/23.1/app.checksum1.js +25 -24
  38. package/dist/esm/tests/flights/latest/app.checksum.js +25 -24
  39. package/dist/esm/tests/hotels/23.1/app.checksum.js +27 -24
  40. package/dist/esm/tests/hotels/latest/app.checksum.js +27 -24
  41. package/dist/esm/tests/loan/latest/index.js +49 -57
  42. package/dist/esm/tests/scriptingObjects/analytics.js +7 -7
  43. package/dist/esm/tests/scriptingObjects/appraisalServiceModule.js +8 -8
  44. package/dist/esm/tests/scriptingObjects/global.js +1 -2
  45. package/dist/esm/tests/task/latest/index.dev.js +29 -28
  46. package/dist/esm/tests/task/latest/index.js +29 -28
  47. package/dist/esm/tests/travelhub/23.1/app.checksum.js +24 -26
  48. package/dist/esm/tests/travelhub/23.1/landing.checksum1.js +5 -7
  49. package/dist/esm/utils.js +31 -1
  50. package/dist/public/assets/frame.671d9de68be598da64ca.html +47 -0
  51. package/dist/public/frame.html +1 -1
  52. package/dist/public/index.html +1 -1
  53. package/dist/public/js/emuiAppBridge.40c8880c94dbc97b49fd.js +51 -0
  54. package/dist/public/js/emuiAppBridge.40c8880c94dbc97b49fd.js.br +0 -0
  55. package/dist/public/js/emuiAppBridge.40c8880c94dbc97b49fd.js.gz +0 -0
  56. package/dist/public/js/emuiAppBridge.40c8880c94dbc97b49fd.js.map +1 -0
  57. package/dist/public/loan-object.js +1 -1
  58. package/dist/public/loan-object.js.br +0 -0
  59. package/dist/public/loan-object.js.gz +0 -0
  60. package/dist/public/loan-object.js.map +1 -1
  61. package/dist/types/lib/appBridge.d.ts +38 -28
  62. package/dist/types/lib/appRegistry.d.ts +41 -0
  63. package/dist/types/lib/eventManager.d.ts +4 -4
  64. package/dist/types/lib/frame.d.ts +45 -4
  65. package/dist/types/lib/index.d.ts +3 -3
  66. package/dist/types/lib/loaders/script.d.ts +2 -1
  67. package/dist/types/lib/microfeHost.d.ts +15 -25
  68. package/dist/types/lib/tests/flights/23.1/app.checksum1.d.ts +7 -0
  69. package/dist/types/lib/tests/flights/latest/app.checksum.d.ts +7 -0
  70. package/dist/types/lib/tests/hotels/23.1/app.checksum.d.ts +7 -0
  71. package/dist/types/lib/tests/hotels/latest/app.checksum.d.ts +7 -0
  72. package/dist/types/lib/tests/loan/latest/index.d.ts +11 -0
  73. package/dist/types/lib/tests/scriptingObjects/analytics.d.ts +3 -3
  74. package/dist/types/lib/tests/scriptingObjects/appraisalServiceModule.d.ts +2 -1
  75. package/dist/types/lib/tests/task/latest/index.d.ts +10 -0
  76. package/dist/types/lib/tests/task/latest/index.dev.d.ts +10 -0
  77. package/dist/types/lib/tests/travelhub/23.1/app.checksum.d.ts +7 -0
  78. package/dist/types/lib/tests/travelhub/23.1/landing.checksum1.d.ts +2 -0
  79. package/dist/types/lib/typings/appInfo.d.ts +1 -0
  80. package/dist/types/lib/typings/common.d.ts +0 -66
  81. package/dist/types/lib/typings/guest.d.ts +10 -3
  82. package/dist/types/lib/typings/host.d.ts +32 -32
  83. package/dist/types/lib/typings/window.d.ts +6 -1
  84. package/dist/types/lib/utils.d.ts +7 -0
  85. package/dist/types/tsconfig.tsbuildinfo +1 -1
  86. package/dist/umd/671d9de68be598da64ca.html +47 -0
  87. package/dist/umd/frame.html +1 -1
  88. package/dist/umd/index.html +1 -1
  89. package/dist/umd/index.js +35 -9
  90. package/dist/umd/index.js.br +0 -0
  91. package/dist/umd/index.js.gz +0 -0
  92. package/dist/umd/index.js.map +1 -1
  93. package/dist/umd/loan-object.js +1 -1
  94. package/dist/umd/loan-object.js.br +0 -0
  95. package/dist/umd/loan-object.js.gz +0 -0
  96. package/dist/umd/loan-object.js.map +1 -1
  97. package/package.json +12 -12
  98. package/dist/public/js/emuiAppBridge.34df989fae2296115611.js +0 -25
  99. package/dist/public/js/emuiAppBridge.34df989fae2296115611.js.br +0 -0
  100. package/dist/public/js/emuiAppBridge.34df989fae2296115611.js.gz +0 -0
  101. package/dist/public/js/emuiAppBridge.34df989fae2296115611.js.map +0 -1
  102. package/dist/types/lib/tests/pubsubAPI.test.d.ts +0 -1
@@ -21,7 +21,9 @@ __export(appBridge_exports, {
21
21
  CAppBridge: () => CAppBridge
22
22
  });
23
23
  module.exports = __toCommonJS(appBridge_exports);
24
- var import_pubsub_js = require("pubsub-js");
24
+ var import_lodash = require("lodash");
25
+ var import_uuid = require("uuid");
26
+ var import_pui_scripting_object = require("@elliemae/pui-scripting-object");
25
27
  var import_microfe_common = require("@elliemae/microfe-common");
26
28
  var import_eventManager = require("./eventManager.js");
27
29
  var import_frame = require("./frame.js");
@@ -30,39 +32,104 @@ var import_app = require("./config/app.js");
30
32
  var import_microFE = require("./config/microFE.js");
31
33
  var import_loaders = require("./loaders/index.js");
32
34
  var import_microfeHost = require("./microfeHost.js");
33
- const APP_CONTAINER_ID_PREFIX = "pui-app-container-";
35
+ var import_appRegistry = require("./appRegistry.js");
36
+ const KEEP_ALIVE_INTERVAL = 12e4;
37
+ const userInteractionEvents = ["click", "scroll", "keypress", "touchstart"];
34
38
  const cssType = /\.css$/;
35
39
  const isCss = (fileName) => cssType.test(fileName);
36
40
  class CAppBridge {
41
+ /**
42
+ * instance of the pui diagnostics logger
43
+ */
37
44
  #logger;
45
+ /**
46
+ * release version of the product
47
+ */
38
48
  #version;
49
+ /**
50
+ * instance of the script loader
51
+ */
39
52
  #scriptLoader;
53
+ /**
54
+ * instance of the style loader
55
+ */
40
56
  #styleLoader;
57
+ /**
58
+ * instance of the app config
59
+ */
41
60
  #appConfig;
61
+ /**
62
+ * instance of the micro frontend config
63
+ */
42
64
  #microFEConfig = new import_microFE.CMicroFEConfig();
65
+ /**
66
+ * instance of the scripting object manager
67
+ */
43
68
  #soManager;
69
+ /**
70
+ * instance of the event manager
71
+ */
44
72
  #eventManager;
73
+ /**
74
+ * list of active apps
75
+ */
45
76
  #activeApps = /* @__PURE__ */ new Map();
77
+ /**
78
+ * instance of the app registry
79
+ */
80
+ #appRegistry = new import_appRegistry.CAppRegistry();
81
+ /**
82
+ * flag to extend parent session when user interacts with the guest application
83
+ */
84
+ #extendSession = true;
46
85
  /**
47
86
  * Create a new instance of the AppBridge
48
- * @param {AppBridgeParams} params - parameter for the constructor
87
+ * @param {AppBridgeParams} options - App Bridge constructor parameters
49
88
  */
50
- constructor(params) {
51
- const { logger } = params;
89
+ constructor(options) {
90
+ const { logger } = options;
52
91
  if (!logger) throw new Error("logger is required");
53
- this.#logger = params.logger;
54
- this.#version = params.version;
92
+ this.#logger = options.logger;
93
+ this.#version = options.version;
94
+ this.#extendSession = options.extendSession ?? true;
55
95
  this.#appConfig = new import_app.CAppConfig({
56
- version: params.version,
57
- baseUrl: params.appConfigBaseUrl
96
+ version: options.version,
97
+ baseUrl: options.appConfigBaseUrl
58
98
  });
59
99
  this.#scriptLoader = new import_loaders.ScriptLoader(logger);
60
100
  this.#styleLoader = new import_loaders.StyleLoader(logger);
61
101
  this.#soManager = new import_microfe_common.ScriptingObjectManager();
62
102
  this.#eventManager = new import_eventManager.EventManager();
63
103
  }
64
- #removeAssetsFromDOM = (id, documentEle = document) => {
65
- const { elementIds } = this.#activeApps.get(id) || {};
104
+ #isFunction(value) {
105
+ return typeof value === "function";
106
+ }
107
+ /**
108
+ * check if the value is a proxy event
109
+ * @param value
110
+ * @returns
111
+ */
112
+ #isProxyEvent = (value) => (
113
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
114
+ value instanceof import_microfe_common.ProxyEvent || // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
115
+ typeof value?.subscribe === "function"
116
+ );
117
+ // support v1 scripting objects
118
+ /**
119
+ * format error message for app not found
120
+ * @param id app id
121
+ * @param instanceId unique instance id
122
+ * @returns error message
123
+ */
124
+ #getAppNotFoundError = (id, instanceId) => `Application ${id} with instance id ${instanceId} is not found. Most probably the appId property of app.config.json is not set to ${id}`;
125
+ /**
126
+ * remove assets from DOM
127
+ * @param instanceId unique instance id of the app
128
+ * @param documentEle document element of the app
129
+ * @returns void
130
+ */
131
+ #removeAssetsFromDOM = (instanceId, documentEle = document) => {
132
+ const { elementIds } = this.#activeApps.get(instanceId) || {};
66
133
  if (elementIds) {
67
134
  elementIds.forEach((elementId) => {
68
135
  const ele = documentEle.getElementById(elementId);
@@ -70,9 +137,26 @@ class CAppBridge {
70
137
  });
71
138
  }
72
139
  };
73
- #addAppToActiveAppList = (id, documentEle, elementIds) => {
74
- const app = window.emui?.[id] || {};
75
- this.#activeApps.set(id, {
140
+ /**
141
+ * add app to active app list
142
+ * @param param0
143
+ * @param param0.id
144
+ * @param param0.instanceId
145
+ * @param elementIds
146
+ * @param param0.documentEle
147
+ */
148
+ #addAppToActiveAppList = ({
149
+ id,
150
+ instanceId,
151
+ documentEle
152
+ }, elementIds) => {
153
+ const app = this.#appRegistry.get({ id, instanceId });
154
+ if (!app) {
155
+ throw new Error(this.#getAppNotFoundError(id, instanceId));
156
+ }
157
+ this.#activeApps.set(instanceId, {
158
+ id,
159
+ instanceId,
76
160
  elementIds,
77
161
  guest: {
78
162
  guestWindow: documentEle?.defaultView,
@@ -83,29 +167,37 @@ class CAppBridge {
83
167
  #waitAndInitApplication = (options, requests) => {
84
168
  const {
85
169
  id,
170
+ instanceId,
171
+ containerId,
86
172
  name,
87
173
  hostUrl,
88
174
  manifestPath,
89
175
  homeRoute,
176
+ initialRoute,
90
177
  history,
91
178
  theme,
92
179
  documentEle
93
180
  } = options;
94
- return Promise.all(requests).then(this.#addAppToActiveAppList.bind(null, id, documentEle)).then(
181
+ return Promise.all(requests).then(
182
+ this.#addAppToActiveAppList.bind(null, { id, instanceId, documentEle })
183
+ ).then(
95
184
  this.#initApplication.bind(null, {
96
185
  id,
186
+ instanceId,
187
+ containerId,
97
188
  name,
98
189
  hostUrl,
99
190
  manifestPath,
100
191
  homeRoute,
192
+ initialRoute,
101
193
  history,
102
194
  theme
103
195
  })
104
196
  ).catch((err) => {
105
- const message = `Application load failed. Unable to load one or more resources for appId: ${options.id}. ${err.message}`;
197
+ const message = `Application load failed. Unable to load one or more resources for ${options.id} with instance id ${instanceId}. ${err.message}`;
106
198
  this.#logger.error({
107
199
  message,
108
- appId: options.id,
200
+ appId: id,
109
201
  exception: err
110
202
  });
111
203
  throw new Error(message);
@@ -113,28 +205,31 @@ class CAppBridge {
113
205
  };
114
206
  #initApplication = async ({
115
207
  id,
116
- name,
208
+ instanceId,
209
+ containerId,
117
210
  hostUrl,
118
211
  manifestPath,
119
212
  homeRoute,
213
+ initialRoute,
120
214
  history,
121
215
  theme
122
216
  }) => {
123
- const app = window.emui?.[id];
217
+ const app = this.#appRegistry.get({ id, instanceId });
124
218
  if (!app) {
125
219
  throw new Error(
126
- `Application ${name} with ${id} is not found. Most probably the appId property of app.config.json is not set to ${id}`
220
+ `Application ${id} with instance id ${instanceId} is not found. Most probably the appId property of app.config.json is not set to ${id}`
127
221
  );
128
222
  }
129
223
  if (!app.init || typeof app.init !== "function")
130
224
  throw new Error(
131
- `Application ${name} with id ${id} doesn't expose init method`
225
+ `Application ${id} with instance id ${instanceId} doesn't expose init method`
132
226
  );
133
227
  const host = new import_microfeHost.CMicroFEHost({
134
228
  guest: {
135
229
  id
136
230
  },
137
231
  version: this.#version,
232
+ containerId,
138
233
  logger: this.#logger,
139
234
  soManager: this.#soManager,
140
235
  eventManager: this.#eventManager
@@ -144,6 +239,7 @@ class CAppBridge {
144
239
  hostUrl,
145
240
  manifestPath,
146
241
  homeRoute,
242
+ initialRoute,
147
243
  prevState: null,
148
244
  history,
149
245
  theme,
@@ -153,8 +249,10 @@ class CAppBridge {
153
249
  });
154
250
  };
155
251
  #loadApp = async (options) => {
156
- const { id, files, name, hostUrl, documentEle } = options;
157
- this.#logger.debug(`Application ${id} is loading...`);
252
+ const { id, instanceId, files, name, hostUrl, documentEle, isJsModule } = options;
253
+ this.#logger.debug(
254
+ `Application ${id} with instance id ${instanceId} is loading...`
255
+ );
158
256
  let assets = files;
159
257
  const manifest = await import_loaders.ManifestLoader.get(options);
160
258
  assets = import_loaders.ManifestLoader.getFullFileNameofAssets(manifest, files);
@@ -166,55 +264,191 @@ class CAppBridge {
166
264
  hostUrl,
167
265
  documentEle,
168
266
  fileName,
169
- index: counter
267
+ index: counter,
268
+ isJsModule
170
269
  };
171
270
  return !isCss(fileName) ? this.#scriptLoader.add(resourceOptions) : this.#styleLoader.add(resourceOptions);
172
271
  });
173
272
  await this.#waitAndInitApplication(options, requests);
174
- this.#logger.audit({ message: "Application loaded", appId: id });
273
+ this.#logger.audit(
274
+ `Application ${id} with instance id ${instanceId} loaded`
275
+ );
175
276
  };
176
- #unloadApp = ({ id, hostUrl, documentEle }) => {
177
- this.#logger.debug(`Application ${id} unloading...`);
178
- const app = window.emui?.[id];
277
+ #unloadApp = ({ id, instanceId, hostUrl, documentEle }) => {
278
+ this.#logger.debug(
279
+ `Application ${id} with instance id ${instanceId} unloading...`
280
+ );
281
+ const app = this.#appRegistry.get({
282
+ id,
283
+ instanceId
284
+ });
179
285
  if (!app) return;
180
- this.#removeAssetsFromDOM(id, documentEle);
286
+ this.#removeAssetsFromDOM(instanceId, documentEle);
181
287
  this.#scriptLoader.removeDynamicImportedScripts(hostUrl, documentEle);
182
288
  this.#scriptLoader.removePrefetchLinks(hostUrl, documentEle);
183
289
  this.#styleLoader.removeDynamicImportedStyles(hostUrl, documentEle);
184
- if (window.emui?.[id]) delete window.emui[id];
185
- this.#activeApps.delete(id);
186
- this.#logger.info({ message: "Application unloaded", appId: id });
290
+ this.#appRegistry.delete({ id, instanceId });
291
+ this.#logger.audit(
292
+ `Application ${id} with instance id ${instanceId} unloaded`
293
+ );
187
294
  };
188
- #mountApp = async ({ id, name }) => {
189
- const app = window.emui?.[id] || {};
295
+ #mountApp = async ({ id, instanceId }) => {
296
+ const app = this.#appRegistry.get({ id, instanceId });
190
297
  if (!app?.mount || typeof app?.mount !== "function")
191
298
  throw new Error(
192
- `Application ${name} with id ${id} doesn't expose mount method`
299
+ `Application ${id} with instance id ${instanceId} doesn't expose mount method`
193
300
  );
194
301
  return app.mount({
195
- containerId: `${APP_CONTAINER_ID_PREFIX}${id}`,
302
+ containerId: `${import_frame.FRAME_APP_CONTAINER_ID_PREFIX}${id}`,
196
303
  hostBreakpoint: (0, import_window.getCurrentBreakpoint)(),
197
304
  hostViewportSize: (0, import_window.getViewportSize)()
198
305
  });
199
306
  };
200
- #unmountApp = ({ id, name }) => {
201
- const app = window.emui?.[id] || {};
307
+ #unmountApp = ({ id, instanceId }) => {
308
+ const app = this.#appRegistry.get({ id, instanceId });
202
309
  if (!app?.unmount) return null;
203
310
  if (typeof app.unmount !== "function")
204
311
  throw new Error(
205
- `Application ${name} with id ${id} doesn't expose unmount method`
312
+ `Application ${id} with instance id ${instanceId} doesn't expose unmount method`
206
313
  );
207
314
  return app.unmount({
208
- containerId: `${APP_CONTAINER_ID_PREFIX}${id}`
315
+ containerId: `${import_frame.FRAME_APP_CONTAINER_ID_PREFIX}${id}`
209
316
  });
210
317
  };
318
+ /**
319
+ * manage session for the guest application by calling the keepSessionAlive method exposed by parent
320
+ * @param root0
321
+ * @param root0.id
322
+ * @param root0.instanceId
323
+ */
324
+ #manageSession = ({ id, instanceId }) => {
325
+ if (!this.#extendSession) return;
326
+ try {
327
+ const appObj = this.#soManager.getObject(
328
+ import_pui_scripting_object.ScriptingObjectNames.Application
329
+ );
330
+ if (!appObj) {
331
+ this.#logger.warn({
332
+ message: `Application scripting object not available for ${id} to manage session`
333
+ });
334
+ return;
335
+ }
336
+ const app = this.#activeApps.get(instanceId);
337
+ if (!app) return;
338
+ app.keepAlive = (0, import_lodash.throttle)(
339
+ async () => {
340
+ try {
341
+ await appObj.keepSessionAlive();
342
+ } catch (e) {
343
+ this.#logger.error(
344
+ `Error keeping session alive. ${e.message}`
345
+ );
346
+ }
347
+ },
348
+ KEEP_ALIVE_INTERVAL
349
+ // throttle time
350
+ );
351
+ const frameEle = import_frame.Frame.get(instanceId);
352
+ userInteractionEvents.forEach((eventType) => {
353
+ frameEle?.contentDocument?.addEventListener(eventType, app.keepAlive);
354
+ });
355
+ } catch (err) {
356
+ this.#logger.warn({
357
+ message: `Application scripting object not available for ${id} to manage session`,
358
+ exception: err
359
+ });
360
+ }
361
+ };
362
+ /**
363
+ * clear session management for the guest application
364
+ * @param instanceId unique instance id of the application
365
+ * @param app
366
+ * @returns
367
+ */
368
+ #clearSession = (app) => {
369
+ if (!this.#extendSession || !app) return;
370
+ const { keepAlive } = app;
371
+ if (keepAlive) {
372
+ userInteractionEvents.forEach((eventType) => {
373
+ document.removeEventListener(eventType, keepAlive);
374
+ });
375
+ app.keepAlive?.cancel();
376
+ }
377
+ };
211
378
  /**
212
379
  * registers scripting object to the host
213
380
  * @param {ValueOf<AppObjects>} so scripting object
214
381
  * @param {AddScriptingObjectParams} params params to add scripting object
215
382
  */
216
383
  addScriptingObject = (so, params) => {
217
- this.#soManager.addScriptingObject(so, params);
384
+ if ((0, import_microfe_common.isScriptingObjectProxy)(so)) {
385
+ const clonedSo = this.cloneScriptingObject(so);
386
+ this.#soManager.addScriptingObject(clonedSo, params);
387
+ } else {
388
+ this.#soManager.addScriptingObject(so, params);
389
+ }
390
+ };
391
+ /**
392
+ * Create new Scripting Object from SSF scripting object proxy
393
+ * @param proxy - reference to the scripting object obtained through getObject method
394
+ * @returns cloned version of the scripting object
395
+ */
396
+ cloneScriptingObject = (proxy) => {
397
+ if (!proxy) throw new Error("proxy is required");
398
+ const so = new import_microfe_common.ScriptingObject(proxy.id, proxy.objectType);
399
+ let unsubscribers = [];
400
+ Object.keys(proxy).forEach((propName) => {
401
+ const propValue = proxy[propName];
402
+ if (this.#isProxyEvent(propValue)) {
403
+ const event = new import_microfe_common.Event({
404
+ name: propValue.name || propName,
405
+ objectId: so.id
406
+ });
407
+ Object.defineProperty(so, propName, {
408
+ value: event,
409
+ enumerable: true
410
+ });
411
+ const listener = ({
412
+ eventParams,
413
+ eventOptions
414
+ }) => this.dispatchEvent({
415
+ event,
416
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
417
+ eventParams,
418
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
419
+ eventOptions
420
+ });
421
+ const token = propValue.subscribe(listener);
422
+ unsubscribers.push(() => {
423
+ propValue.unsubscribe(token);
424
+ });
425
+ } else if (this.#isFunction(propValue)) {
426
+ Object.defineProperty(so, propName, {
427
+ value: async (...args) => {
428
+ const retVal = await propValue(...args);
429
+ return (0, import_microfe_common.isScriptingObjectProxy)(retVal) ? this.cloneScriptingObject(retVal) : retVal;
430
+ },
431
+ enumerable: true
432
+ });
433
+ if (propName === "dispose") {
434
+ const defaultImpl = so.dispose;
435
+ Object.defineProperty(so, propName, {
436
+ value: () => {
437
+ so._dispose();
438
+ return defaultImpl.apply(so);
439
+ },
440
+ enumerable: true
441
+ });
442
+ }
443
+ }
444
+ });
445
+ so._dispose = () => {
446
+ unsubscribers.forEach((unsub) => {
447
+ unsub?.();
448
+ });
449
+ unsubscribers = [];
450
+ };
451
+ return so;
218
452
  };
219
453
  /**
220
454
  * Close all active guest micro frontend applications
@@ -224,43 +458,68 @@ class CAppBridge {
224
458
  };
225
459
  /**
226
460
  * Close guest micro frontend application
227
- * @param id unique id of guest application
461
+ * @param instanceId unique instance id of the application
228
462
  */
229
- closeApp = async (id) => {
230
- if (!id) throw new Error("id is required");
231
- const appConfig = this.#microFEConfig.getConfigById(id);
232
- if (!appConfig) {
233
- throw new Error(`Application with id ${id} is not found`);
463
+ closeApp = async (instanceId) => {
464
+ if (!instanceId) throw new Error("instanceId is required");
465
+ const app = this.#activeApps.get(instanceId);
466
+ if (!app) {
467
+ this.#logger.warn(
468
+ `Application with instance id ${instanceId} is not found`
469
+ );
470
+ return;
234
471
  }
235
- const { name, hostUrl } = appConfig;
472
+ this.#activeApps.delete(instanceId);
473
+ const { id } = app;
474
+ const appConfig = this.#microFEConfig.getConfigById(id);
475
+ const { hostUrl } = appConfig;
236
476
  try {
237
- await this.#unmountApp({ id, name });
477
+ this.#clearSession(app);
478
+ await this.#unmountApp({ id, instanceId });
238
479
  } finally {
239
- const frameEle = import_frame.Frame.get(id);
240
- if (!frameEle?.contentDocument) {
241
- throw new Error(`Iframe for application with id ${id} is not found`);
480
+ const frameEle = import_frame.Frame.get(instanceId);
481
+ if (frameEle?.contentDocument) {
482
+ this.#unloadApp({
483
+ id,
484
+ instanceId,
485
+ hostUrl,
486
+ documentEle: frameEle.contentDocument
487
+ });
242
488
  }
243
- this.#unloadApp({ id, hostUrl, documentEle: frameEle.contentDocument });
244
- this.#soManager.removeAllScriptingObjects(id);
245
- import_frame.Frame.remove(id);
489
+ import_frame.Frame.remove(instanceId);
246
490
  }
247
491
  };
248
492
  /**
249
493
  * dispatch event to guest microfrontend application
250
- * @param {DispatchEventParams<EventId, Params>} params - event parameters
494
+ * @param {DispatchEventParams<EventId, Params, Options>} params - event parameters
251
495
  */
252
- dispatchEvent = async (params) => this.#eventManager.dispatchEvent(params);
496
+ dispatchEvent = async (params) => {
497
+ const {
498
+ event: { id, name }
499
+ } = params;
500
+ if (!id) throw new Error("Event Id is required");
501
+ const objectId = id.split(".")?.[0];
502
+ const scriptingObject = this.#soManager.getObject(objectId);
503
+ if (!scriptingObject) {
504
+ this.#logger.warn(
505
+ `Attempt to dispatch event ${name} on unknown object ${objectId}`
506
+ );
507
+ return Promise.resolve();
508
+ }
509
+ return this.#eventManager.dispatchEvent(scriptingObject, params);
510
+ };
253
511
  /**
254
- * Get guest by id
512
+ * Get App by instanceId
255
513
  * @param id unique id of guest
514
+ * @param instanceId
256
515
  * @returns guest instance
257
516
  */
258
- getGuest = (id) => this.#activeApps.get(id)?.guest;
517
+ getApp = (instanceId) => this.#activeApps.get(instanceId)?.guest;
259
518
  /**
260
- * Get list of active guests
519
+ * Get list of active apps
261
520
  * @returns list of active guests
262
521
  */
263
- getGuests = () => [...this.#activeApps.values()].map((app) => app.guest);
522
+ getApps = () => [...this.#activeApps.values()].map((app) => app.guest);
264
523
  /**
265
524
  * Initialize appBridge
266
525
  */
@@ -273,86 +532,114 @@ class CAppBridge {
273
532
  };
274
533
  /**
275
534
  * Mount guest micro frontend application into DOM
276
- * @param id unique id of guest micro frontend application
277
- * @throws Error if application with given id is not found in configuration
535
+ * @param instanceId unique instance id of guest micro frontend application
536
+ * @throws Error if application with given instance id is not found in configuration
278
537
  */
279
- mountApp = async (id) => {
280
- if (!id) throw new Error("id is required");
538
+ mountApp = async (instanceId) => {
539
+ if (!instanceId) throw new Error("instanceId is required");
540
+ const { id } = this.#activeApps.get(instanceId) || {};
541
+ if (!id) {
542
+ throw new Error(
543
+ `Application with instance id ${instanceId} is not found`
544
+ );
545
+ }
281
546
  const appConfig = this.#microFEConfig.getConfigById(id);
282
547
  if (!appConfig) {
283
548
  throw new Error(`Application with id ${id} is not found`);
284
549
  }
285
- await this.#mountApp(appConfig);
550
+ await this.#mountApp({ ...appConfig, instanceId });
286
551
  };
287
552
  /**
288
553
  * Open guest micro frontend application
289
554
  * @param {OpenAppParams} params - options to open guest application
290
555
  */
291
556
  openApp = async (params) => {
292
- const { id, frameOptions, history, theme } = params;
557
+ const { id, frameOptions, history, theme, homeRoute, initialRoute } = params;
558
+ const instanceId = (0, import_uuid.v4)();
293
559
  const appConfig = this.#microFEConfig.getConfigById(id);
294
560
  if (!appConfig) {
295
- throw new Error(`Application with id ${id} is not found`);
561
+ throw new Error(`Application ${id} is not found in app config`);
296
562
  }
297
- if (import_frame.Frame.get(id))
298
- throw new Error(`Application with id ${id} is already open`);
299
- const frameEle = await import_frame.Frame.create(id, {
300
- title: appConfig.name,
301
- ...frameOptions
563
+ const frameEle = await import_frame.Frame.create({
564
+ id,
565
+ instanceId,
566
+ manifestPath: appConfig.manifestPath,
567
+ hostUrl: appConfig.hostUrl,
568
+ options: {
569
+ title: appConfig.name,
570
+ ...frameOptions
571
+ }
302
572
  });
303
573
  if (!frameEle?.contentDocument)
304
574
  throw new Error("unable to create iframe for the microapp");
305
575
  try {
576
+ this.#appRegistry.add({
577
+ id,
578
+ instanceId,
579
+ documentEle: frameEle.contentDocument
580
+ });
306
581
  await this.#loadApp({
307
- ...appConfig,
582
+ instanceId,
308
583
  history,
309
584
  theme,
310
- documentEle: frameEle.contentDocument
585
+ documentEle: frameEle.contentDocument,
586
+ containerId: frameOptions?.containerId,
587
+ ...appConfig,
588
+ homeRoute: homeRoute ?? appConfig.homeRoute,
589
+ initialRoute
311
590
  });
312
- await this.#mountApp(appConfig);
313
- return this.#activeApps.get(id)?.guest;
591
+ await this.#mountApp({ instanceId, ...appConfig });
592
+ this.#manageSession({ id, instanceId });
593
+ return instanceId;
314
594
  } catch (err) {
315
595
  this.#unloadApp({
316
596
  id,
597
+ instanceId,
317
598
  hostUrl: appConfig.hostUrl,
318
599
  documentEle: frameEle.contentDocument
319
600
  });
320
- import_frame.Frame.remove(id);
601
+ import_frame.Frame.remove(instanceId);
321
602
  throw err;
322
603
  }
323
604
  };
324
- /**
325
- * emit event to all subscribers (deprecated)
326
- * @deprecated use dispatchEvent instead
327
- * @param eventId unique id of the event. The format is [scripting object name].[event name]
328
- * @param data data to be sent to the subscribers of the event
329
- * @returns true if event is published successfully
330
- */
331
- publish = (eventId, data) => (0, import_pubsub_js.publish)(eventId, data);
332
605
  /**
333
606
  * remove all listeners
334
607
  */
335
- removeAllEventListeners = () => {
608
+ removeAllEventSubscriptions = () => {
336
609
  this.#eventManager.unsubscribeAll();
337
610
  };
611
+ /**
612
+ * removes all scripting objects from host
613
+ * @param guestId unique id of the guest application
614
+ */
615
+ removeAllScriptingObjects = (guestId) => {
616
+ this.#soManager.removeAllScriptingObjects(guestId);
617
+ };
338
618
  /**
339
619
  * removes scripting object from the host
340
620
  * @param objectId unique id of the scripting object
621
+ * @param guestId
341
622
  */
342
- removeScriptingObject = (objectId) => {
343
- this.#soManager.removeScriptingObject(objectId);
623
+ removeScriptingObject = (objectId, guestId) => {
624
+ this.#soManager.removeScriptingObject(objectId, guestId);
344
625
  };
345
626
  /**
346
627
  * Unmount guest micro frontend application from DOM
347
- * @param id unique id of guest micro frontend application
628
+ * @param instanceId unique instance id of guest micro frontend application
348
629
  * @throws Error if application with given id is not found in configuration
349
630
  */
350
- unmountApp = async (id) => {
351
- if (!id) throw new Error("id is required");
631
+ unmountApp = async (instanceId) => {
632
+ if (!instanceId) throw new Error("instanceId is required");
633
+ const { id } = this.#activeApps.get(instanceId) || {};
634
+ if (!id) {
635
+ throw new Error(
636
+ `Application with instance id ${instanceId} is not found`
637
+ );
638
+ }
352
639
  const appConfig = this.#microFEConfig.getConfigById(id);
353
640
  if (!appConfig) {
354
641
  throw new Error(`Application with id ${id} is not found`);
355
642
  }
356
- await this.#unmountApp(appConfig);
643
+ await this.#unmountApp({ ...appConfig, instanceId });
357
644
  };
358
645
  }