@elliemae/pui-app-bridge 2.22.0 → 2.23.0

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 (65) hide show
  1. package/dist/cjs/appBridge.js +33 -16
  2. package/dist/cjs/appRegistry.js +16 -10
  3. package/dist/cjs/config/app.js +21 -9
  4. package/dist/cjs/config/microFE.js +9 -8
  5. package/dist/cjs/eventManager.js +30 -23
  6. package/dist/cjs/frame.js +79 -47
  7. package/dist/cjs/index.js +3 -0
  8. package/dist/cjs/loaders/manifest.js +9 -2
  9. package/dist/cjs/loaders/script.js +3 -2
  10. package/dist/cjs/loaders/style.js +2 -1
  11. package/dist/cjs/microfeHost.js +6 -5
  12. package/dist/cjs/utils.js +3 -29
  13. package/dist/esm/appBridge.js +22 -15
  14. package/dist/esm/appRegistry.js +16 -10
  15. package/dist/esm/config/app.js +21 -9
  16. package/dist/esm/config/microFE.js +9 -8
  17. package/dist/esm/eventManager.js +30 -23
  18. package/dist/esm/frame.js +79 -47
  19. package/dist/esm/index.js +3 -0
  20. package/dist/esm/loaders/manifest.js +9 -2
  21. package/dist/esm/loaders/script.js +4 -3
  22. package/dist/esm/loaders/style.js +3 -2
  23. package/dist/esm/microfeHost.js +6 -5
  24. package/dist/esm/utils.js +3 -29
  25. package/dist/public/creditService/latest/creditService.checksum.js.map +1 -1
  26. package/dist/public/frame.html +1 -1
  27. package/dist/public/guest/businessObjects.js.map +1 -1
  28. package/dist/public/guest/util.js.map +1 -1
  29. package/dist/public/index.html +1 -1
  30. package/dist/public/init.js.map +1 -1
  31. package/dist/public/js/emuiAppBridge.24ac0c1126377d1fd21f.js +25 -0
  32. package/dist/public/js/emuiAppBridge.24ac0c1126377d1fd21f.js.br +0 -0
  33. package/dist/public/js/emuiAppBridge.24ac0c1126377d1fd21f.js.gz +0 -0
  34. package/dist/public/js/emuiAppBridge.24ac0c1126377d1fd21f.js.map +1 -0
  35. package/dist/public/loan-object.js.map +1 -1
  36. package/dist/public/loanValidation/latest/loanValidation.checksum.js.map +1 -1
  37. package/dist/public/pricingService/latest/pricingService.checksum.js.map +1 -1
  38. package/dist/public/utils.js.map +1 -1
  39. package/dist/types/lib/appBridge.d.ts +1 -1
  40. package/dist/types/lib/appRegistry.d.ts +4 -4
  41. package/dist/types/lib/config/app.d.ts +3 -4
  42. package/dist/types/lib/eventManager.d.ts +3 -1
  43. package/dist/types/lib/frame.d.ts +0 -10
  44. package/dist/types/lib/index.d.ts +1 -0
  45. package/dist/types/lib/loaders/manifest.d.ts +1 -0
  46. package/dist/types/lib/microfeHost.d.ts +1 -1
  47. package/dist/types/lib/utils.d.ts +1 -6
  48. package/dist/types/tsconfig.tsbuildinfo +1 -1
  49. package/dist/umd/creditService/latest/creditService.checksum.js.map +1 -1
  50. package/dist/umd/guest/businessObjects.js.map +1 -1
  51. package/dist/umd/guest/util.js.map +1 -1
  52. package/dist/umd/index.js +9 -35
  53. package/dist/umd/index.js.br +0 -0
  54. package/dist/umd/index.js.gz +0 -0
  55. package/dist/umd/index.js.map +1 -1
  56. package/dist/umd/init.js.map +1 -1
  57. package/dist/umd/loan-object.js.map +1 -1
  58. package/dist/umd/loanValidation/latest/loanValidation.checksum.js.map +1 -1
  59. package/dist/umd/pricingService/latest/pricingService.checksum.js.map +1 -1
  60. package/dist/umd/utils.js.map +1 -1
  61. package/package.json +25 -26
  62. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js +0 -51
  63. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.br +0 -0
  64. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.gz +0 -0
  65. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,13 +17,21 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var appBridge_exports = {};
20
30
  __export(appBridge_exports, {
21
31
  CAppBridge: () => CAppBridge
22
32
  });
23
33
  module.exports = __toCommonJS(appBridge_exports);
24
- var import_lodash = require("lodash");
34
+ var import_throttle = __toESM(require("lodash/throttle"), 1);
25
35
  var import_uuid = require("uuid");
26
36
  var import_pui_scripting_object = require("@elliemae/pui-scripting-object");
27
37
  var import_microfe_common = require("@elliemae/microfe-common");
@@ -99,7 +109,7 @@ class CAppBridge {
99
109
  this.#scriptLoader = new import_loaders.ScriptLoader(logger);
100
110
  this.#styleLoader = new import_loaders.StyleLoader(logger);
101
111
  this.#soManager = new import_microfe_common.ScriptingObjectManager();
102
- this.#eventManager = new import_eventManager.EventManager();
112
+ this.#eventManager = new import_eventManager.EventManager(this.#logger);
103
113
  }
104
114
  #isFunction(value) {
105
115
  return typeof value === "function";
@@ -228,14 +238,10 @@ class CAppBridge {
228
238
  documentEle,
229
239
  isESMModule: isJsModule
230
240
  };
231
- const styleElementIds = await this.#styleLoader.load(
232
- cssAssets,
233
- loadOptions
234
- );
235
- const scriptElementIds = await this.#scriptLoader.load(
236
- jsAssets,
237
- loadOptions
238
- );
241
+ const [styleElementIds, scriptElementIds] = await Promise.all([
242
+ this.#styleLoader.load(cssAssets, loadOptions),
243
+ this.#scriptLoader.load(jsAssets, loadOptions)
244
+ ]);
239
245
  this.#addAppToActiveAppList({
240
246
  id,
241
247
  instanceId,
@@ -317,7 +323,7 @@ class CAppBridge {
317
323
  }
318
324
  const app = this.#activeApps.get(instanceId);
319
325
  if (!app) return;
320
- app.keepAlive = (0, import_lodash.throttle)(
326
+ app.keepAlive = (0, import_throttle.default)(
321
327
  async () => {
322
328
  try {
323
329
  await appObj.keepSessionAlive();
@@ -349,11 +355,15 @@ class CAppBridge {
349
355
  */
350
356
  #clearSession = (app) => {
351
357
  if (!this.#extendSession || !app) return;
352
- const { keepAlive } = app;
358
+ const { keepAlive, instanceId } = app;
353
359
  if (keepAlive) {
354
- userInteractionEvents.forEach((eventType) => {
355
- document.removeEventListener(eventType, keepAlive);
356
- });
360
+ const frameEle = import_frame.Frame.get(instanceId);
361
+ const targetDoc = frameEle?.contentDocument;
362
+ if (targetDoc) {
363
+ userInteractionEvents.forEach((eventType) => {
364
+ targetDoc.removeEventListener(eventType, keepAlive);
365
+ });
366
+ }
357
367
  app.keepAlive?.cancel();
358
368
  }
359
369
  };
@@ -442,6 +452,7 @@ class CAppBridge {
442
452
  * Close guest micro frontend application
443
453
  * @param instanceId unique instance id of the application
444
454
  */
455
+ // eslint-disable-next-line max-statements
445
456
  closeApp = async (instanceId) => {
446
457
  if (!instanceId) throw new Error("instanceId is required");
447
458
  const app = this.#activeApps.get(instanceId);
@@ -451,9 +462,14 @@ class CAppBridge {
451
462
  );
452
463
  return;
453
464
  }
454
- this.#activeApps.delete(instanceId);
455
465
  const { id } = app;
456
466
  const appConfig = this.#microFEConfig.getConfigById(id);
467
+ if (!appConfig) {
468
+ this.#logger.warn(`Configuration for application ${id} is not found`);
469
+ this.#activeApps.delete(instanceId);
470
+ import_frame.Frame.remove(instanceId);
471
+ return;
472
+ }
457
473
  const { hostUrl } = appConfig;
458
474
  try {
459
475
  this.#clearSession(app);
@@ -468,6 +484,7 @@ class CAppBridge {
468
484
  documentEle: frameEle.contentDocument
469
485
  });
470
486
  }
487
+ this.#activeApps.delete(instanceId);
471
488
  import_frame.Frame.remove(instanceId);
472
489
  }
473
490
  };
@@ -34,7 +34,12 @@ class CAppRegistry {
34
34
  * @param param0.appId
35
35
  * @param param0.app
36
36
  */
37
+ // eslint-disable-next-line complexity
37
38
  #registerApp = ({ appId, app }) => {
39
+ if (!appId) throw new Error("appId is required");
40
+ if (appId === "__proto__" || appId === "constructor" || appId === "prototype") {
41
+ throw new Error(`Invalid appId: ${appId}`);
42
+ }
38
43
  if (!app?.uuid) throw new Error("application uuid is required");
39
44
  window.emui[appId] = window.emui[appId] || [];
40
45
  if (Array.isArray(window.emui[appId])) {
@@ -51,7 +56,7 @@ class CAppRegistry {
51
56
  if (existingApp?.uuid === app.uuid) {
52
57
  existingApp = { ...existingApp, ...app };
53
58
  window.emui[appId] = [existingApp];
54
- } else if (typeof existingApp.init === void 0) {
59
+ } else if (typeof existingApp.init === "undefined") {
55
60
  window.emui[appId] = [{ ...existingApp, ...app }];
56
61
  } else {
57
62
  window.emui[appId] = [window.emui[appId], app];
@@ -90,6 +95,11 @@ class CAppRegistry {
90
95
  instanceId,
91
96
  documentEle
92
97
  }) => {
98
+ if (!instanceId) {
99
+ throw new Error(
100
+ `instanceId (uuid) is required to register application ${id}`
101
+ );
102
+ }
93
103
  const newAppInstance = {
94
104
  uuid: instanceId,
95
105
  init: null,
@@ -98,11 +108,6 @@ class CAppRegistry {
98
108
  };
99
109
  const appInstances = window.emui[id];
100
110
  if (appInstances) {
101
- if (!instanceId) {
102
- throw new Error(
103
- `Application ${id} is already loaded. uuid is required to load multiple instances of the same app`
104
- );
105
- }
106
111
  if (Array.isArray(appInstances)) {
107
112
  appInstances.push(newAppInstance);
108
113
  } else {
@@ -118,10 +123,10 @@ class CAppRegistry {
118
123
  };
119
124
  /**
120
125
  * delete app from global emui window variable
121
- * @param id.id app id
122
- * @param id.instanceId unique instance id
123
- * @param id.id.id
124
- * @param id.id.instanceId
126
+ * @param id.id
127
+ * @param id app id
128
+ * @param instanceId unique instance id
129
+ * @param id.instanceId
125
130
  */
126
131
  delete = ({ id, instanceId }) => {
127
132
  if (Array.isArray(window.emui[id])) {
@@ -129,6 +134,7 @@ class CAppRegistry {
129
134
  (app) => app.uuid === instanceId
130
135
  );
131
136
  if (index > -1) window.emui[id].splice(index, 1);
137
+ if (window.emui[id].length === 0) delete window.emui[id];
132
138
  } else {
133
139
  delete window.emui[id];
134
140
  }
@@ -33,7 +33,7 @@ __export(app_exports, {
33
33
  MicroAppManager: () => MicroAppManager
34
34
  });
35
35
  module.exports = __toCommonJS(app_exports);
36
- var import_clone = __toESM(require("lodash/clone"), 1);
36
+ var import_cloneDeep = __toESM(require("lodash/cloneDeep"), 1);
37
37
  var import_get = __toESM(require("lodash/get"), 1);
38
38
  var import_set = __toESM(require("lodash/set"), 1);
39
39
  var import_has = __toESM(require("lodash/has"), 1);
@@ -60,10 +60,9 @@ class CAppConfig {
60
60
  this.#baseUrl = (0, import_utils.appendTrailingSlash)(params?.baseUrl || "");
61
61
  }
62
62
  #parse = (data) => {
63
- const { activeEnv } = data;
64
- const activeEnvConfig = data.env[activeEnv] || {};
65
- if (data.env) delete data.env;
66
- this.#gAppConfig = (0, import_merge.default)(data, activeEnvConfig);
63
+ const { activeEnv, env, ...rest } = data;
64
+ const activeEnvConfig = env[activeEnv] || {};
65
+ this.#gAppConfig = (0, import_merge.default)({}, rest, activeEnvConfig);
67
66
  };
68
67
  /**
69
68
  * Get value for the given app config key
@@ -71,14 +70,15 @@ class CAppConfig {
71
70
  * @param defaultValue default value to return if key is not found
72
71
  * @returns value for the given key
73
72
  */
74
- get = (key = "", defaultValue = null) => (0, import_clone.default)((0, import_get.default)(this.#gAppConfig, key, defaultValue));
73
+ get = (key = "", defaultValue = null) => (0, import_cloneDeep.default)((0, import_get.default)(this.#gAppConfig, key, defaultValue));
75
74
  /**
76
75
  * Set value for the given app config key
77
76
  * @param key key to set value for
78
77
  * @param value value to set
79
- * @returns app config instance
80
78
  */
81
- set = (key, value) => (0, import_set.default)(this.#gAppConfig, key, value);
79
+ set = (key, value) => {
80
+ (0, import_set.default)(this.#gAppConfig, key, value);
81
+ };
82
82
  /**
83
83
  * Check if the given key exists in app config
84
84
  * @param key key to check
@@ -104,9 +104,21 @@ class CAppConfig {
104
104
  * @param assetPath url path to load app config from
105
105
  * @param configUrl
106
106
  */
107
+ // eslint-disable-next-line complexity
107
108
  load = async (configUrl) => {
108
109
  const appConfigUrl = configUrl ?? `${this.#getVersionedBaseUrl()}app.config.json`;
109
- const response = await fetch(appConfigUrl);
110
+ let response;
111
+ try {
112
+ response = await fetch(appConfigUrl);
113
+ } catch (err) {
114
+ if (!configUrl && this.#version !== import_constant.LATEST_VERSION) {
115
+ await this.load(`${this.#baseUrl}latest/app.config.json`);
116
+ return;
117
+ }
118
+ throw new Error("Failed to fetch app config", {
119
+ cause: err
120
+ });
121
+ }
110
122
  if (response.ok) {
111
123
  try {
112
124
  const data = await response.json();
@@ -41,7 +41,6 @@ const isCIBuild = () => process.env.CI === "true";
41
41
  const isProduction = (mode) => {
42
42
  if (isCIBuild()) return true;
43
43
  return String(mode).toLowerCase() === import_app.LaunchMode.PRODUCTION.toLowerCase();
44
- return true;
45
44
  };
46
45
  const getPathName = (url) => url ? new URL(url).pathname : "";
47
46
  const getAbsoluteUrl = (url) => {
@@ -97,12 +96,13 @@ const getConfig = ({
97
96
  config,
98
97
  version = "latest"
99
98
  }) => {
100
- const { mode = import_app.LaunchMode.PRODUCTION } = config;
101
- const envConfig = isProduction(mode) ? config.production : config.development;
102
- if (config.production)
103
- delete config.production;
104
- if (config.development)
105
- delete config.development;
99
+ const {
100
+ mode = import_app.LaunchMode.PRODUCTION,
101
+ production,
102
+ development,
103
+ ...restConfig
104
+ } = config;
105
+ const envConfig = isProduction(mode) ? production : development;
106
106
  const newConfig = (0, import_merge.default)(
107
107
  { id },
108
108
  {
@@ -112,7 +112,8 @@ const getConfig = ({
112
112
  securityContext: import_common.SecurityContext.USER,
113
113
  isJsModule: true
114
114
  },
115
- config,
115
+ restConfig,
116
+ { mode },
116
117
  envConfig
117
118
  );
118
119
  newConfig.hostUrl = (0, import_utils.appendTrailingSlash)(
@@ -22,22 +22,22 @@ __export(eventManager_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(eventManager_exports);
24
24
  var import_uuid = require("uuid");
25
- const flatten = (source, target = []) => {
26
- const retVal = target || [];
27
- if (source && source.forEach) {
28
- source.forEach((item) => {
29
- flatten(item, retVal);
30
- });
31
- } else if (typeof source !== "undefined") {
32
- retVal.push(source);
33
- }
34
- return retVal;
35
- };
36
25
  class EventManager {
37
26
  /**
38
27
  * event listeners
39
28
  */
40
29
  #listeners = /* @__PURE__ */ new Map();
30
+ /**
31
+ * cache for compiled regex patterns used in filter criteria
32
+ */
33
+ #regexCache = /* @__PURE__ */ new WeakMap();
34
+ /**
35
+ * logger instance
36
+ */
37
+ #logger;
38
+ constructor(logger) {
39
+ this.#logger = logger;
40
+ }
41
41
  /**
42
42
  * call an async function with a timeout
43
43
  * @param promise async function to call
@@ -58,21 +58,24 @@ class EventManager {
58
58
  */
59
59
  #emitEvent = (param) => {
60
60
  const { eventName, scriptingObject, eventParams, listeners } = param;
61
- listeners.map(async (listener) => {
61
+ listeners.forEach((listener) => {
62
62
  if (listener.criteria) {
63
63
  const matches = eventParams ? this.#matchesCriteria(eventParams, listener.criteria) : false;
64
64
  if (!matches) {
65
65
  return;
66
66
  }
67
67
  }
68
- try {
69
- await listener.callback({
68
+ Promise.resolve(
69
+ listener.callback({
70
70
  obj: scriptingObject,
71
71
  eventName,
72
72
  eventParams
73
- });
74
- } catch (e) {
75
- }
73
+ })
74
+ ).catch((e) => {
75
+ this.#logger?.warn(
76
+ `Error in event listener for "${eventName}": ${e.message}`
77
+ );
78
+ });
76
79
  });
77
80
  };
78
81
  #emitEventWithFeedback = async ({
@@ -101,7 +104,7 @@ class EventManager {
101
104
  );
102
105
  });
103
106
  const retValues = await Promise.all(promises);
104
- return flatten(retValues);
107
+ return retValues.flat(Infinity).filter((v) => v !== void 0);
105
108
  };
106
109
  /**
107
110
  * Checks if a given payload matches all specified filter criteria.
@@ -135,12 +138,16 @@ class EventManager {
135
138
  return !Number.isNaN(numValue) && numValue < condition.lt;
136
139
  }
137
140
  if ("regex" in condition) {
138
- try {
139
- const regex = new RegExp(condition.regex);
140
- return typeof value === "string" && regex.test(value);
141
- } catch {
142
- return false;
141
+ let regex = this.#regexCache.get(condition);
142
+ if (regex === void 0) {
143
+ try {
144
+ regex = new RegExp(condition.regex);
145
+ } catch {
146
+ regex = null;
147
+ }
148
+ this.#regexCache.set(condition, regex);
143
149
  }
150
+ return regex !== null && typeof value === "string" && regex.test(value);
144
151
  }
145
152
  return false;
146
153
  });
package/dist/cjs/frame.js CHANGED
@@ -40,58 +40,90 @@ const create = ({
40
40
  manifestPath,
41
41
  hostUrl,
42
42
  options
43
- }) => new Promise((resolve, reject) => {
44
- const iframeContainer = document.createElement("div");
45
- iframeContainer.setAttribute(
46
- "style",
47
- "display: flex;width: 100%;height: 100%;flex-direction: column;overflow: hidden;"
48
- );
49
- const frame = document.createElement("iframe");
50
- frame.setAttribute("id", instanceId);
51
- frame.setAttribute("data-testid", id);
52
- frame.setAttribute("title", options.title);
53
- frame.setAttribute("allowfullscreen", "true");
54
- frame.setAttribute("allowtransparency", "true");
55
- if (options.permissionPolicy)
56
- frame.setAttribute("allow", options.permissionPolicy);
57
- if (options.sandbox) frame.setAttribute("sandbox", options.sandbox);
58
- frame.setAttribute(
59
- "style",
60
- options.style ?? "flex-grow: 1;border: none;margin: 0;padding: 0;display: block;min-width: 100%;height: 100%;"
61
- );
62
- const baseSrc = options.src ?? (import_frame.default.default ?? import_frame.default);
63
- const srcWithParams = options.queryParams ? `${baseSrc}${baseSrc.includes("?") ? "&" : "?"}${options.queryParams}` : baseSrc;
64
- frame.setAttribute("src", srcWithParams);
65
- frame.addEventListener("load", () => {
66
- if (!frame.contentDocument) {
67
- reject(new Error("Frame content window is null"));
68
- return;
43
+ }) => (
44
+ // eslint-disable-next-line max-statements
45
+ new Promise((resolve, reject) => {
46
+ const iframeContainer = document.createElement("div");
47
+ iframeContainer.setAttribute(
48
+ "style",
49
+ "display: flex;width: 100%;height: 100%;flex-direction: column;overflow: hidden;"
50
+ );
51
+ const frame = document.createElement("iframe");
52
+ frame.setAttribute("id", instanceId);
53
+ frame.setAttribute("data-testid", id);
54
+ frame.setAttribute("title", options.title);
55
+ frame.setAttribute("allowfullscreen", "true");
56
+ frame.setAttribute("allowtransparency", "true");
57
+ if (options.permissionPolicy)
58
+ frame.setAttribute("allow", options.permissionPolicy);
59
+ if (options.sandbox) frame.setAttribute("sandbox", options.sandbox);
60
+ frame.setAttribute(
61
+ "style",
62
+ options.style ?? "flex-grow: 1;border: none;margin: 0;padding: 0;display: block;min-width: 100%;height: 100%;"
63
+ );
64
+ const baseSrc = options.src ?? (import_frame.default.default ?? import_frame.default);
65
+ let srcWithParams = baseSrc;
66
+ if (options.queryParams) {
67
+ const sanitized = options.queryParams.replace(
68
+ /[^a-zA-Z0-9&=_.~%+-]/g,
69
+ ""
70
+ );
71
+ srcWithParams = `${baseSrc}${baseSrc.includes("?") ? "&" : "?"}${sanitized}`;
69
72
  }
70
- const documentEle = frame.contentDocument;
71
- const ele = documentEle.getElementById(FRAME_APP_CONTAINER_ID_PREFIX);
72
- if (ele) {
73
- ele.id = `${ele.id}${id}`;
74
- }
75
- let baseTag = documentEle.getElementsByTagName("base")?.[0];
76
- if (baseTag) {
77
- baseTag.href = new URL(manifestPath, hostUrl).href;
78
- } else {
79
- baseTag = documentEle.createElement("base");
80
- baseTag.href = new URL(manifestPath, hostUrl).href;
81
- documentEle.getElementsByTagName("head")[0].appendChild(baseTag);
82
- }
83
- resolve(frame);
84
- });
85
- iframeContainer.appendChild(frame);
86
- const { containerId } = options;
87
- const parentElement = document.getElementById(containerId ?? "") ?? document.body;
88
- parentElement.appendChild(iframeContainer);
89
- });
73
+ frame.setAttribute("src", srcWithParams);
74
+ const FRAME_LOAD_TIMEOUT_MS = 1e4;
75
+ let settled = false;
76
+ const timeoutId = setTimeout(() => {
77
+ if (!settled) {
78
+ settled = true;
79
+ reject(
80
+ new Error(
81
+ `iframe for ${id} failed to load within ${FRAME_LOAD_TIMEOUT_MS}ms`
82
+ )
83
+ );
84
+ }
85
+ }, FRAME_LOAD_TIMEOUT_MS);
86
+ frame.addEventListener("error", () => {
87
+ if (!settled) {
88
+ settled = true;
89
+ clearTimeout(timeoutId);
90
+ reject(new Error(`iframe for ${id} failed to load`));
91
+ }
92
+ });
93
+ frame.addEventListener("load", () => {
94
+ if (settled) return;
95
+ settled = true;
96
+ clearTimeout(timeoutId);
97
+ if (!frame.contentDocument) {
98
+ reject(new Error("Frame content window is null"));
99
+ return;
100
+ }
101
+ const documentEle = frame.contentDocument;
102
+ const ele = documentEle.getElementById(FRAME_APP_CONTAINER_ID_PREFIX);
103
+ if (ele) {
104
+ ele.id = `${ele.id}${id}`;
105
+ }
106
+ let baseTag = documentEle.getElementsByTagName("base")?.[0];
107
+ if (baseTag) {
108
+ baseTag.href = new URL(manifestPath, hostUrl).href;
109
+ } else {
110
+ baseTag = documentEle.createElement("base");
111
+ baseTag.href = new URL(manifestPath, hostUrl).href;
112
+ documentEle.getElementsByTagName("head")[0].appendChild(baseTag);
113
+ }
114
+ resolve(frame);
115
+ });
116
+ iframeContainer.appendChild(frame);
117
+ const { containerId } = options;
118
+ const parentElement = document.getElementById(containerId ?? "") ?? document.body;
119
+ parentElement.appendChild(iframeContainer);
120
+ })
121
+ );
90
122
  const get = (instanceId) => document.getElementById(instanceId);
91
123
  const remove = (instanceId) => {
92
124
  const frameEle = get(instanceId);
93
125
  if (frameEle) {
94
- frameEle.remove();
126
+ frameEle.parentElement?.remove();
95
127
  }
96
128
  };
97
129
  const Frame = {
package/dist/cjs/index.js CHANGED
@@ -20,6 +20,8 @@ var index_exports = {};
20
20
  __export(index_exports, {
21
21
  CAppBridge: () => import_appBridge.CAppBridge,
22
22
  Event: () => import_microfe_common.Event,
23
+ LaunchMode: () => import_app.LaunchMode,
24
+ MicroAppManager: () => import_app.MicroAppManager,
23
25
  ScriptLoader: () => import_script.ScriptLoader,
24
26
  ScriptingObject: () => import_microfe_common.ScriptingObject,
25
27
  SecurityContext: () => import_common.SecurityContext,
@@ -27,6 +29,7 @@ __export(index_exports, {
27
29
  });
28
30
  module.exports = __toCommonJS(index_exports);
29
31
  var import_common = require("./typings/common.js");
32
+ var import_app = require("./config/app.js");
30
33
  var import_microfe_common = require("@elliemae/microfe-common");
31
34
  var import_appBridge = require("./appBridge.js");
32
35
  var import_script = require("./loaders/script.js");
@@ -32,17 +32,23 @@ const isValidHttpUrl = (fileName) => {
32
32
  }
33
33
  return url.protocol === "http:" || url.protocol === "https:";
34
34
  };
35
+ const manifestCache = /* @__PURE__ */ new Map();
36
+ const clearManifestCache = () => manifestCache.clear();
35
37
  const get = async ({
36
38
  hostUrl,
37
39
  manifestPath
38
40
  }) => {
39
41
  const url = new URL(`${manifestPath}manifest.json`, hostUrl);
40
- const response = await fetch((0, import_utils.removeDoubleSlash)(url.href));
42
+ const manifestUrl = (0, import_utils.removeDoubleSlash)(url.href);
43
+ const cached = manifestCache.get(manifestUrl);
44
+ if (cached) return cached;
45
+ const response = await fetch(manifestUrl);
41
46
  if (response.ok) {
42
47
  const { headers } = response;
43
48
  const contentType = headers?.get?.("content-type") ?? "";
44
49
  if (contentType.includes("application/json")) {
45
50
  const data = await response.json();
51
+ manifestCache.set(manifestUrl, data);
46
52
  return data;
47
53
  }
48
54
  throw new Error(`manifest ${url.href} is not a valid json`);
@@ -67,5 +73,6 @@ const getFullFileNameofAssets = (manifest, assetNames = []) => assetNames.reduce
67
73
  }, []);
68
74
  const ManifestLoader = {
69
75
  get,
70
- getFullFileNameofAssets
76
+ getFullFileNameofAssets,
77
+ clearCache: clearManifestCache
71
78
  };
@@ -185,6 +185,7 @@ class ScriptLoader {
185
185
  if (!ele) {
186
186
  this.#logger.warn(`script with id ${elementId} not found`);
187
187
  resolve();
188
+ return;
188
189
  }
189
190
  ele.remove();
190
191
  resolve();
@@ -203,7 +204,7 @@ class ScriptLoader {
203
204
  * ```
204
205
  */
205
206
  removeDynamicImportedScripts = (hostUrl, documentEle) => {
206
- const hostPattern = new RegExp(hostUrl, "i");
207
+ const hostPattern = new RegExp((0, import_utils.escapeRegExp)(hostUrl), "i");
207
208
  const scriptElements = documentEle.getElementsByTagName("script");
208
209
  for (let index = scriptElements.length - 1; index >= 0; index -= 1) {
209
210
  const scriptEle = scriptElements[index];
@@ -225,7 +226,7 @@ class ScriptLoader {
225
226
  * ```
226
227
  */
227
228
  removePrefetchLinks = (hostUrl, documentEle) => {
228
- const hostPattern = new RegExp(hostUrl, "i");
229
+ const hostPattern = new RegExp((0, import_utils.escapeRegExp)(hostUrl), "i");
229
230
  const prefetchElements = documentEle.querySelectorAll('[rel="prefetch"]');
230
231
  for (let index = prefetchElements.length - 1; index >= 0; index -= 1) {
231
232
  const ele = prefetchElements[index];
@@ -132,6 +132,7 @@ class StyleLoader {
132
132
  if (!ele) {
133
133
  this.#logger.warn(`style with id ${elementId} not found`);
134
134
  resolve();
135
+ return;
135
136
  }
136
137
  ele.remove();
137
138
  resolve();
@@ -151,7 +152,7 @@ class StyleLoader {
151
152
  * ```
152
153
  */
153
154
  removeDynamicImportedStyles = (hostUrl, documentEle) => {
154
- const hostPattern = new RegExp(hostUrl, "i");
155
+ const hostPattern = new RegExp((0, import_utils.escapeRegExp)(hostUrl), "i");
155
156
  const prefetchElements = documentEle.querySelectorAll('[rel="stylesheet"]');
156
157
  for (let index = prefetchElements.length - 1; index >= 0; index -= 1) {
157
158
  const ele = prefetchElements[index];
@@ -24,7 +24,6 @@ module.exports = __toCommonJS(microfeHost_exports);
24
24
  var import_microfe_common = require("@elliemae/microfe-common");
25
25
  var import_constant = require("./constant.js");
26
26
  class CMicroFEHost {
27
- #logger;
28
27
  /**
29
28
  * unique id of the iframe container
30
29
  */
@@ -39,7 +38,6 @@ class CMicroFEHost {
39
38
  */
40
39
  constructor(params) {
41
40
  this.#guest = params.guest;
42
- this.#logger = params.logger;
43
41
  this.#containerId = params.containerId;
44
42
  this.#version = params?.version || import_constant.LATEST_VERSION;
45
43
  this.#soManager = params.soManager;
@@ -59,8 +57,9 @@ class CMicroFEHost {
59
57
  * @returns scripting object reference
60
58
  */
61
59
  getObject = (objectId) => {
62
- const so = this.#soManager.getObject(objectId, this.#guest);
63
- if (so) {
60
+ try {
61
+ const so = this.#soManager.getObject(objectId, this.#guest);
62
+ if (!so) return Promise.resolve(null);
64
63
  const proxy = new import_microfe_common.ScriptingObjectProxy(so.id, so.objectType);
65
64
  Object.keys(so).forEach((propName) => {
66
65
  const propValue = so[propName];
@@ -90,8 +89,9 @@ class CMicroFEHost {
90
89
  }
91
90
  });
92
91
  return Promise.resolve(proxy);
92
+ } catch (err) {
93
+ return Promise.reject(err);
93
94
  }
94
- return Promise.resolve(null);
95
95
  };
96
96
  /**
97
97
  * set the size of the guest application iframe window
@@ -103,6 +103,7 @@ class CMicroFEHost {
103
103
  const frameEle = document.getElementById(this.#containerId);
104
104
  if (frameEle) {
105
105
  frameEle.style.height = `${size.height}px`;
106
+ frameEle.style.width = `${size.width}px`;
106
107
  }
107
108
  };
108
109
  /**