@elliemae/pui-app-bridge 2.21.3 → 2.22.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 (73) hide show
  1. package/dist/cjs/appBridge.js +43 -61
  2. package/dist/cjs/index.html +1 -1
  3. package/dist/cjs/index.js +5 -1
  4. package/dist/cjs/loaders/script.js +162 -10
  5. package/dist/cjs/loaders/style.js +98 -9
  6. package/dist/esm/appBridge.js +43 -61
  7. package/dist/esm/index.html +1 -1
  8. package/dist/esm/index.js +5 -1
  9. package/dist/esm/loaders/script.js +162 -10
  10. package/dist/esm/loaders/style.js +98 -9
  11. package/dist/public/creditService/latest/creditService.checksum.js +1 -1
  12. package/dist/public/creditService/latest/creditService.checksum.js.br +0 -0
  13. package/dist/public/creditService/latest/creditService.checksum.js.gz +0 -0
  14. package/dist/public/creditService/latest/creditService.checksum.js.map +1 -1
  15. package/dist/public/frame.html +1 -1
  16. package/dist/public/index.html +1 -1
  17. package/dist/public/init.js +1 -1
  18. package/dist/public/init.js.br +0 -0
  19. package/dist/public/init.js.gz +0 -0
  20. package/dist/public/init.js.map +1 -1
  21. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js +51 -0
  22. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.br +0 -0
  23. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.gz +0 -0
  24. package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.map +1 -0
  25. package/dist/public/latest/app.config.json +6 -6
  26. package/dist/public/loanValidation/latest/loanValidation.checksum.js +1 -1
  27. package/dist/public/loanValidation/latest/loanValidation.checksum.js.br +0 -0
  28. package/dist/public/loanValidation/latest/loanValidation.checksum.js.gz +0 -0
  29. package/dist/public/loanValidation/latest/loanValidation.checksum.js.map +1 -1
  30. package/dist/public/pricingService/latest/pricingService.checksum.js +2 -2
  31. package/dist/public/pricingService/latest/pricingService.checksum.js.br +0 -0
  32. package/dist/public/pricingService/latest/pricingService.checksum.js.gz +0 -0
  33. package/dist/public/pricingService/latest/pricingService.checksum.js.map +1 -1
  34. package/dist/public/utils.js +1 -1
  35. package/dist/public/utils.js.br +0 -0
  36. package/dist/public/utils.js.gz +0 -0
  37. package/dist/public/utils.js.map +1 -1
  38. package/dist/types/lib/index.d.ts +2 -0
  39. package/dist/types/lib/loaders/script.d.ts +99 -5
  40. package/dist/types/lib/loaders/style.d.ts +77 -4
  41. package/dist/types/lib/tests/loaders/script.test.d.ts +1 -0
  42. package/dist/types/tsconfig.tsbuildinfo +1 -1
  43. package/dist/umd/creditService/latest/creditService.checksum.js +1 -1
  44. package/dist/umd/creditService/latest/creditService.checksum.js.br +0 -0
  45. package/dist/umd/creditService/latest/creditService.checksum.js.gz +0 -0
  46. package/dist/umd/creditService/latest/creditService.checksum.js.map +1 -1
  47. package/dist/umd/index.html +1 -1
  48. package/dist/umd/index.js +17 -17
  49. package/dist/umd/index.js.br +0 -0
  50. package/dist/umd/index.js.gz +0 -0
  51. package/dist/umd/index.js.map +1 -1
  52. package/dist/umd/init.js +1 -1
  53. package/dist/umd/init.js.br +0 -0
  54. package/dist/umd/init.js.gz +0 -0
  55. package/dist/umd/init.js.map +1 -1
  56. package/dist/umd/latest/app.config.json +6 -6
  57. package/dist/umd/loanValidation/latest/loanValidation.checksum.js +1 -1
  58. package/dist/umd/loanValidation/latest/loanValidation.checksum.js.br +0 -0
  59. package/dist/umd/loanValidation/latest/loanValidation.checksum.js.gz +0 -0
  60. package/dist/umd/loanValidation/latest/loanValidation.checksum.js.map +1 -1
  61. package/dist/umd/pricingService/latest/pricingService.checksum.js +2 -2
  62. package/dist/umd/pricingService/latest/pricingService.checksum.js.br +0 -0
  63. package/dist/umd/pricingService/latest/pricingService.checksum.js.gz +0 -0
  64. package/dist/umd/pricingService/latest/pricingService.checksum.js.map +1 -1
  65. package/dist/umd/utils.js +1 -1
  66. package/dist/umd/utils.js.br +0 -0
  67. package/dist/umd/utils.js.gz +0 -0
  68. package/dist/umd/utils.js.map +1 -1
  69. package/package.json +9 -9
  70. package/dist/public/js/emuiAppBridge.b2ffda17fd59a1937af8.js +0 -51
  71. package/dist/public/js/emuiAppBridge.b2ffda17fd59a1937af8.js.br +0 -0
  72. package/dist/public/js/emuiAppBridge.b2ffda17fd59a1937af8.js.gz +0 -0
  73. package/dist/public/js/emuiAppBridge.b2ffda17fd59a1937af8.js.map +0 -1
@@ -144,12 +144,14 @@ class CAppBridge {
144
144
  * @param param0.instanceId
145
145
  * @param elementIds
146
146
  * @param param0.documentEle
147
+ * @param param0.elementIds
147
148
  */
148
149
  #addAppToActiveAppList = ({
149
150
  id,
150
151
  instanceId,
151
- documentEle
152
- }, elementIds) => {
152
+ documentEle,
153
+ elementIds
154
+ }) => {
153
155
  const app = this.#appRegistry.get({ id, instanceId });
154
156
  if (!app) {
155
157
  throw new Error(this.#getAppNotFoundError(id, instanceId));
@@ -164,45 +166,6 @@ class CAppBridge {
164
166
  }
165
167
  });
166
168
  };
167
- #waitAndInitApplication = (options, requests) => {
168
- const {
169
- id,
170
- instanceId,
171
- containerId,
172
- name,
173
- hostUrl,
174
- manifestPath,
175
- homeRoute,
176
- initialRoute,
177
- history,
178
- theme,
179
- documentEle
180
- } = options;
181
- return Promise.all(requests).then(
182
- this.#addAppToActiveAppList.bind(null, { id, instanceId, documentEle })
183
- ).then(
184
- this.#initApplication.bind(null, {
185
- id,
186
- instanceId,
187
- containerId,
188
- name,
189
- hostUrl,
190
- manifestPath,
191
- homeRoute,
192
- initialRoute,
193
- history,
194
- theme
195
- })
196
- ).catch((err) => {
197
- const message = `Application load failed. Unable to load one or more resources for ${options.id} with instance id ${instanceId}. ${err.message}`;
198
- this.#logger.error({
199
- message,
200
- appId: id,
201
- exception: err
202
- });
203
- throw new Error(message);
204
- });
205
- };
206
169
  #initApplication = async ({
207
170
  id,
208
171
  instanceId,
@@ -249,30 +212,49 @@ class CAppBridge {
249
212
  });
250
213
  };
251
214
  #loadApp = async (options) => {
252
- const { id, instanceId, files, name, hostUrl, documentEle, isJsModule } = options;
253
- this.#logger.debug(
254
- `Application ${id} with instance id ${instanceId} is loading...`
255
- );
256
- let assets = files;
257
- const manifest = await import_loaders.ManifestLoader.get(options);
258
- assets = import_loaders.ManifestLoader.getFullFileNameofAssets(manifest, files);
259
- let counter = 0;
260
- const requests = assets.map((fileName) => {
261
- counter += 1;
262
- const resourceOptions = {
215
+ try {
216
+ const { id, instanceId, files, name, hostUrl, documentEle, isJsModule } = options;
217
+ this.#logger.debug(
218
+ `Application ${id} with instance id ${instanceId} is loading...`
219
+ );
220
+ let assets = files;
221
+ const manifest = await import_loaders.ManifestLoader.get(options);
222
+ assets = import_loaders.ManifestLoader.getFullFileNameofAssets(manifest, files);
223
+ const cssAssets = assets.filter((fileName) => isCss(fileName));
224
+ const jsAssets = assets.filter((fileName) => !isCss(fileName));
225
+ const loadOptions = {
263
226
  name,
264
227
  hostUrl,
265
228
  documentEle,
266
- fileName,
267
- index: counter,
268
- isJsModule
229
+ isESMModule: isJsModule
269
230
  };
270
- return !isCss(fileName) ? this.#scriptLoader.add(resourceOptions) : this.#styleLoader.add(resourceOptions);
271
- });
272
- await this.#waitAndInitApplication(options, requests);
273
- this.#logger.audit(
274
- `Application ${id} with instance id ${instanceId} loaded`
275
- );
231
+ const styleElementIds = await this.#styleLoader.load(
232
+ cssAssets,
233
+ loadOptions
234
+ );
235
+ const scriptElementIds = await this.#scriptLoader.load(
236
+ jsAssets,
237
+ loadOptions
238
+ );
239
+ this.#addAppToActiveAppList({
240
+ id,
241
+ instanceId,
242
+ documentEle,
243
+ elementIds: [...styleElementIds, ...scriptElementIds]
244
+ });
245
+ await this.#initApplication(options);
246
+ this.#logger.audit(
247
+ `Application ${id} with instance id ${instanceId} loaded`
248
+ );
249
+ } catch (err) {
250
+ const message = `Application load failed. Unable to load one or more resources for ${options.id} with instance id ${options.instanceId}. ${err.message}`;
251
+ this.#logger.error({
252
+ message,
253
+ appId: options.id,
254
+ exception: err
255
+ });
256
+ throw new Error(message);
257
+ }
276
258
  };
277
259
  #unloadApp = ({ id, instanceId, hostUrl, documentEle }) => {
278
260
  this.#logger.debug(
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Host</title>
8
8
  <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
9
- <script src="https://cdn.qa1.ice.com/pui-diagnostics@3" ></script>
9
+ <script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3" ></script>
10
10
  </head>
11
11
  <body>
12
12
  <header class="bg-indigo-300 h-10 flex place-items-center">
package/dist/cjs/index.js CHANGED
@@ -20,10 +20,14 @@ var index_exports = {};
20
20
  __export(index_exports, {
21
21
  CAppBridge: () => import_appBridge.CAppBridge,
22
22
  Event: () => import_microfe_common.Event,
23
+ ScriptLoader: () => import_script.ScriptLoader,
23
24
  ScriptingObject: () => import_microfe_common.ScriptingObject,
24
- SecurityContext: () => import_common.SecurityContext
25
+ SecurityContext: () => import_common.SecurityContext,
26
+ StyleLoader: () => import_style.StyleLoader
25
27
  });
26
28
  module.exports = __toCommonJS(index_exports);
27
29
  var import_common = require("./typings/common.js");
28
30
  var import_microfe_common = require("@elliemae/microfe-common");
29
31
  var import_appBridge = require("./appBridge.js");
32
+ var import_script = require("./loaders/script.js");
33
+ var import_style = require("./loaders/style.js");
@@ -29,23 +29,77 @@ class ScriptLoader {
29
29
  constructor(logger) {
30
30
  this.#logger = logger;
31
31
  }
32
- add = ({
33
- name,
34
- hostUrl,
32
+ /**
33
+ * Creates a modulepreload link element for performance optimization.
34
+ * @param href.href
35
+ * @param href - The URL of the script to preload
36
+ * @param href.isESMModule
37
+ * @param href.documentEle
38
+ * @remarks
39
+ * Modulepreload hints allow the browser to fetch and cache the module script
40
+ * earlier in the page load lifecycle, improving performance.
41
+ * The crossorigin attribute is set to 'anonymous' to match the script loading behavior
42
+ * and prevent CORS issues with cross-origin resources.
43
+ * @private
44
+ */
45
+ #preLoadScript = ({
46
+ href,
47
+ isESMModule = true,
48
+ documentEle
49
+ }) => {
50
+ const link = documentEle.createElement("link");
51
+ link.rel = isESMModule ? "modulepreload" : "preload";
52
+ link.href = href;
53
+ link.crossOrigin = "anonymous";
54
+ if (!isESMModule) {
55
+ link.as = "script";
56
+ }
57
+ documentEle.head.appendChild(link);
58
+ };
59
+ /**
60
+ * Dynamically adds a script element to the document.
61
+ * @param options - Configuration options for the script
62
+ * @param options.name - The name of the application or script
63
+ * @param options.hostUrl - The base URL where the script is hosted
64
+ * @param options.documentEle - The document object where the script should be added
65
+ * @param options.fileName - The filename of the script to load
66
+ * @param options.index - An index used to generate a unique script ID
67
+ * @param options.isESMModule - Whether the script should be loaded as an ES module (default: true)
68
+ * @param options.id
69
+ * @param options.href
70
+ * @returns A promise that resolves with the script element's ID when the script loads successfully
71
+ * @throws Will reject with an error if the script fails to load
72
+ * @example
73
+ * ```typescript
74
+ * await scriptLoader.add({
75
+ * name: 'myApp',
76
+ * hostUrl: 'https://cdn.example.com',
77
+ * documentEle: document,
78
+ * fileName: 'main.js',
79
+ * index: 0,
80
+ * isESMModule: true
81
+ * });
82
+ * ```
83
+ */
84
+ #loadScript = ({
85
+ id,
86
+ href,
35
87
  documentEle,
36
- fileName,
37
- index,
38
- isJsModule = true
88
+ isESMModule = true
39
89
  }) => new Promise((resolve, reject) => {
40
- const url = new URL(fileName, hostUrl);
41
90
  const ele = documentEle.createElement("script");
42
- ele.id = `${APP_SCRIPT_ID_PREFIX}${name.toLowerCase()}-${index}`;
43
- ele.src = (0, import_utils.removeDoubleSlash)(url.href);
91
+ ele.id = id;
92
+ ele.src = (0, import_utils.removeDoubleSlash)(href);
93
+ ele.crossOrigin = "anonymous";
44
94
  ele.nonce = "__CSP_NONCE__";
45
- if (!(0, import_utils.isJSDOM)() && isJsModule) ele.type = "module";
95
+ if (!(0, import_utils.isJSDOM)() && isESMModule) ele.type = "module";
46
96
  else ele.async = false;
47
97
  ele.onload = resolve.bind(null, ele.id);
48
98
  ele.onerror = (err) => {
99
+ this.#logger.error({
100
+ message: `Error loading script ${ele.src}`,
101
+ exception: new Error(err)
102
+ });
49
103
  reject(
50
104
  new Error(`Unable to load script ${ele.src}`, {
51
105
  cause: new Error(err)
@@ -54,6 +108,78 @@ class ScriptLoader {
54
108
  };
55
109
  documentEle.head.appendChild(ele);
56
110
  });
111
+ /**
112
+ * Loads multiple script assets sequentially, with CDN scripts loaded before non-CDN scripts.
113
+ * @param assets - Array of script URLs (either full HTTP/HTTPS URLs or relative paths)
114
+ * @param options - Configuration options for loading the scripts
115
+ * @param options.name - The name of the application (used for generating script IDs)
116
+ * @param options.hostUrl - The base URL for resolving relative script paths
117
+ * @param options.documentEle - The document object where scripts should be added
118
+ * @param options.isESMModule - Whether scripts should be loaded as ES modules (default: true)
119
+ * @returns A promise that resolves with an array of script element IDs when all scripts are loaded
120
+ * @remarks
121
+ * This method:
122
+ * - Partitions assets into CDN (HTTP/HTTPS) and non-CDN (relative) arrays in a single pass
123
+ * - Creates modulepreload links for all assets for performance optimization
124
+ * - Loads CDN scripts sequentially first (they may set global variables needed by non-CDN scripts)
125
+ * - Then loads non-CDN scripts sequentially
126
+ * - Sequential execution ensures proper dependency resolution and initialization order
127
+ * @example
128
+ * ```typescript
129
+ * const scriptIds = await scriptLoader.load(
130
+ * ['https://cdn.example.com/lib.js', 'app.js', 'config.js'],
131
+ * {
132
+ * name: 'myApp',
133
+ * hostUrl: 'https://example.com',
134
+ * documentEle: document,
135
+ * isESMModule: true
136
+ * }
137
+ * );
138
+ * ```
139
+ */
140
+ load = async (assets, options) => {
141
+ const { documentEle, isESMModule } = options;
142
+ const httpPattern = /^https?:\/\//i;
143
+ const { cdnAssets, nonCdnAssets } = assets.reduce(
144
+ (acc, fileName, index) => {
145
+ const id = `${APP_SCRIPT_ID_PREFIX}${options.name.toLowerCase()}-${index}`;
146
+ if (httpPattern.test(fileName)) {
147
+ acc.cdnAssets.push({ id, href: fileName });
148
+ this.#preLoadScript({ href: fileName, documentEle, isESMModule });
149
+ } else {
150
+ const url = new URL(fileName, options.hostUrl);
151
+ acc.nonCdnAssets.push({ id, href: url.href });
152
+ this.#preLoadScript({ href: url.href, documentEle, isESMModule });
153
+ }
154
+ return acc;
155
+ },
156
+ {
157
+ cdnAssets: [],
158
+ nonCdnAssets: []
159
+ }
160
+ );
161
+ for (const { href, id } of [...cdnAssets, ...nonCdnAssets]) {
162
+ await this.#loadScript({
163
+ id,
164
+ href,
165
+ documentEle,
166
+ isESMModule
167
+ });
168
+ }
169
+ return [...cdnAssets, ...nonCdnAssets].map((asset) => asset.id);
170
+ };
171
+ /**
172
+ * Removes a script element from the document by its ID.
173
+ * @param elementId - The ID of the script element to remove (default: '')
174
+ * @param documentEle - The document object from which to remove the script (default: document)
175
+ * @returns A promise that resolves when the script element has been removed
176
+ * @remarks
177
+ * If the element with the specified ID is not found, a warning is logged but the promise still resolves.
178
+ * @example
179
+ * ```typescript
180
+ * await scriptLoader.remove('ice-script-myapp-0', document);
181
+ * ```
182
+ */
57
183
  remove = (elementId = "", documentEle = document) => new Promise((resolve) => {
58
184
  const ele = documentEle.getElementById(elementId);
59
185
  if (!ele) {
@@ -63,6 +189,19 @@ class ScriptLoader {
63
189
  ele.remove();
64
190
  resolve();
65
191
  });
192
+ /**
193
+ * Removes all script elements from the document that match a specific host URL pattern.
194
+ * @param hostUrl - The host URL pattern to match against script sources
195
+ * @param documentEle - The document object from which to remove scripts
196
+ * @remarks
197
+ * This method iterates through all script elements in reverse order and removes any script
198
+ * whose `src` attribute matches the provided host URL pattern (case-insensitive).
199
+ * This is useful for cleaning up dynamically imported scripts from a specific host.
200
+ * @example
201
+ * ```typescript
202
+ * scriptLoader.removeDynamicImportedScripts('https://cdn.example.com', document);
203
+ * ```
204
+ */
66
205
  removeDynamicImportedScripts = (hostUrl, documentEle) => {
67
206
  const hostPattern = new RegExp(hostUrl, "i");
68
207
  const scriptElements = documentEle.getElementsByTagName("script");
@@ -72,6 +211,19 @@ class ScriptLoader {
72
211
  if (hostPattern.test(src)) scriptEle.remove();
73
212
  }
74
213
  };
214
+ /**
215
+ * Removes all prefetch link elements from the document that match a specific host URL pattern.
216
+ * @param hostUrl - The host URL pattern to match against link hrefs
217
+ * @param documentEle - The document object from which to remove prefetch links
218
+ * @remarks
219
+ * This method queries all elements with `rel="prefetch"` and removes any link whose `href`
220
+ * attribute matches the provided host URL pattern (case-insensitive). This is useful for
221
+ * cleaning up resource hints that are no longer needed.
222
+ * @example
223
+ * ```typescript
224
+ * scriptLoader.removePrefetchLinks('https://cdn.example.com', document);
225
+ * ```
226
+ */
75
227
  removePrefetchLinks = (hostUrl, documentEle) => {
76
228
  const hostPattern = new RegExp(hostUrl, "i");
77
229
  const prefetchElements = documentEle.querySelectorAll('[rel="prefetch"]');
@@ -29,21 +29,45 @@ class StyleLoader {
29
29
  constructor(logger) {
30
30
  this.#logger = logger;
31
31
  }
32
- add = ({
33
- name,
34
- hostUrl,
35
- documentEle,
36
- fileName,
37
- index
32
+ /**
33
+ * Dynamically adds a stylesheet link element to the document.
34
+ * @param options - Configuration options for the stylesheet
35
+ * @param options.name - The name of the application or stylesheet
36
+ * @param options.hostUrl - The base URL where the stylesheet is hosted
37
+ * @param options.documentEle - The document object where the stylesheet should be added
38
+ * @param options.fileName - The filename of the stylesheet to load
39
+ * @param options.index - An index used to generate a unique stylesheet ID
40
+ * @param options.id
41
+ * @param options.href
42
+ * @returns A promise that resolves with the link element's ID when the stylesheet loads successfully
43
+ * @throws Will reject with an error if the stylesheet fails to load
44
+ * @example
45
+ * ```typescript
46
+ * await styleLoader.add({
47
+ * name: 'myApp',
48
+ * hostUrl: 'https://cdn.example.com',
49
+ * documentEle: document,
50
+ * fileName: 'main.css',
51
+ * index: 0
52
+ * });
53
+ * ```
54
+ */
55
+ #loadStyle = ({
56
+ id,
57
+ href,
58
+ documentEle
38
59
  }) => new Promise((resolve, reject) => {
39
60
  const ele = documentEle.createElement("link");
40
- ele.id = `${APP_STYLE_ID_PREFIX}${name.toLowerCase()}-${index}`;
61
+ ele.id = id;
41
62
  ele.nonce = "__CSP_NONCE__";
42
63
  ele.rel = "stylesheet";
43
- const url = new URL(fileName, hostUrl);
44
- ele.href = (0, import_utils.removeDoubleSlash)(url.href);
64
+ ele.href = (0, import_utils.removeDoubleSlash)(href);
45
65
  ele.onload = resolve.bind(null, ele.id);
46
66
  ele.onerror = (err) => {
67
+ this.#logger.error({
68
+ message: `Error loading stylesheet ${ele.href}`,
69
+ exception: new Error(err)
70
+ });
47
71
  reject(
48
72
  new Error(`Unable to load stylesheet ${ele.href}`, {
49
73
  cause: new Error(err)
@@ -52,6 +76,57 @@ class StyleLoader {
52
76
  };
53
77
  documentEle.head.appendChild(ele);
54
78
  });
79
+ /**
80
+ * Loads multiple stylesheet assets in parallel.
81
+ * @param assets - Array of stylesheet filenames (relative paths)
82
+ * @param options - Configuration options for loading the stylesheets
83
+ * @param options.name - The name of the application (used for generating stylesheet IDs)
84
+ * @param options.hostUrl - The base URL for resolving stylesheet paths
85
+ * @param options.documentEle - The document object where stylesheets should be added
86
+ * @returns A promise that resolves with an array of stylesheet element IDs when all stylesheets are loaded
87
+ * @remarks
88
+ * This method loads all stylesheets in parallel using Promise.all for optimal performance.
89
+ * Each stylesheet is assigned a unique ID based on the application name and its index.
90
+ * Unlike scripts, stylesheets don't typically have execution order dependencies.
91
+ * @example
92
+ * ```typescript
93
+ * const styleIds = await styleLoader.load(
94
+ * ['main.css', 'theme.css', 'components.css'],
95
+ * {
96
+ * name: 'myApp',
97
+ * hostUrl: 'https://example.com',
98
+ * documentEle: document
99
+ * }
100
+ * );
101
+ * ```
102
+ */
103
+ load = async (assets, options) => {
104
+ const elementIds = [];
105
+ const loadPromises = assets.map((fileName, index) => {
106
+ const url = new URL(fileName, options.hostUrl);
107
+ const id = `${APP_STYLE_ID_PREFIX}${options.name.toLowerCase()}-${index}`;
108
+ elementIds.push(id);
109
+ return this.#loadStyle({
110
+ id,
111
+ href: url.href,
112
+ documentEle: options.documentEle
113
+ });
114
+ });
115
+ await Promise.all(loadPromises);
116
+ return elementIds;
117
+ };
118
+ /**
119
+ * Removes a stylesheet link element from the document by its ID.
120
+ * @param elementId - The ID of the stylesheet element to remove (default: '')
121
+ * @param documentEle - The document object from which to remove the stylesheet (default: document)
122
+ * @returns A promise that resolves when the stylesheet element has been removed
123
+ * @remarks
124
+ * If the element with the specified ID is not found, a warning is logged but the promise still resolves.
125
+ * @example
126
+ * ```typescript
127
+ * await styleLoader.remove('ice-style-myapp-0', document);
128
+ * ```
129
+ */
55
130
  remove = (elementId = "", documentEle = document) => new Promise((resolve) => {
56
131
  const ele = documentEle.getElementById(elementId);
57
132
  if (!ele) {
@@ -61,6 +136,20 @@ class StyleLoader {
61
136
  ele.remove();
62
137
  resolve();
63
138
  });
139
+ /**
140
+ * Removes all stylesheet link elements from the document that match a specific host URL pattern.
141
+ * @param hostUrl - The host URL pattern to match against stylesheet hrefs
142
+ * @param documentEle - The document object from which to remove stylesheets
143
+ * @remarks
144
+ * This method queries all elements with `rel="stylesheet"` and removes any link whose `href`
145
+ * attribute matches the provided host URL pattern (case-insensitive). This is useful for
146
+ * cleaning up dynamically imported stylesheets from a specific host.
147
+ * The iteration happens in reverse order to safely remove elements while iterating.
148
+ * @example
149
+ * ```typescript
150
+ * styleLoader.removeDynamicImportedStyles('https://cdn.example.com', document);
151
+ * ```
152
+ */
64
153
  removeDynamicImportedStyles = (hostUrl, documentEle) => {
65
154
  const hostPattern = new RegExp(hostUrl, "i");
66
155
  const prefetchElements = documentEle.querySelectorAll('[rel="stylesheet"]');
@@ -129,12 +129,14 @@ class CAppBridge {
129
129
  * @param param0.instanceId
130
130
  * @param elementIds
131
131
  * @param param0.documentEle
132
+ * @param param0.elementIds
132
133
  */
133
134
  #addAppToActiveAppList = ({
134
135
  id,
135
136
  instanceId,
136
- documentEle
137
- }, elementIds) => {
137
+ documentEle,
138
+ elementIds
139
+ }) => {
138
140
  const app = this.#appRegistry.get({ id, instanceId });
139
141
  if (!app) {
140
142
  throw new Error(this.#getAppNotFoundError(id, instanceId));
@@ -149,45 +151,6 @@ class CAppBridge {
149
151
  }
150
152
  });
151
153
  };
152
- #waitAndInitApplication = (options, requests) => {
153
- const {
154
- id,
155
- instanceId,
156
- containerId,
157
- name,
158
- hostUrl,
159
- manifestPath,
160
- homeRoute,
161
- initialRoute,
162
- history,
163
- theme,
164
- documentEle
165
- } = options;
166
- return Promise.all(requests).then(
167
- this.#addAppToActiveAppList.bind(null, { id, instanceId, documentEle })
168
- ).then(
169
- this.#initApplication.bind(null, {
170
- id,
171
- instanceId,
172
- containerId,
173
- name,
174
- hostUrl,
175
- manifestPath,
176
- homeRoute,
177
- initialRoute,
178
- history,
179
- theme
180
- })
181
- ).catch((err) => {
182
- const message = `Application load failed. Unable to load one or more resources for ${options.id} with instance id ${instanceId}. ${err.message}`;
183
- this.#logger.error({
184
- message,
185
- appId: id,
186
- exception: err
187
- });
188
- throw new Error(message);
189
- });
190
- };
191
154
  #initApplication = async ({
192
155
  id,
193
156
  instanceId,
@@ -234,30 +197,49 @@ class CAppBridge {
234
197
  });
235
198
  };
236
199
  #loadApp = async (options) => {
237
- const { id, instanceId, files, name, hostUrl, documentEle, isJsModule } = options;
238
- this.#logger.debug(
239
- `Application ${id} with instance id ${instanceId} is loading...`
240
- );
241
- let assets = files;
242
- const manifest = await ManifestLoader.get(options);
243
- assets = ManifestLoader.getFullFileNameofAssets(manifest, files);
244
- let counter = 0;
245
- const requests = assets.map((fileName) => {
246
- counter += 1;
247
- const resourceOptions = {
200
+ try {
201
+ const { id, instanceId, files, name, hostUrl, documentEle, isJsModule } = options;
202
+ this.#logger.debug(
203
+ `Application ${id} with instance id ${instanceId} is loading...`
204
+ );
205
+ let assets = files;
206
+ const manifest = await ManifestLoader.get(options);
207
+ assets = ManifestLoader.getFullFileNameofAssets(manifest, files);
208
+ const cssAssets = assets.filter((fileName) => isCss(fileName));
209
+ const jsAssets = assets.filter((fileName) => !isCss(fileName));
210
+ const loadOptions = {
248
211
  name,
249
212
  hostUrl,
250
213
  documentEle,
251
- fileName,
252
- index: counter,
253
- isJsModule
214
+ isESMModule: isJsModule
254
215
  };
255
- return !isCss(fileName) ? this.#scriptLoader.add(resourceOptions) : this.#styleLoader.add(resourceOptions);
256
- });
257
- await this.#waitAndInitApplication(options, requests);
258
- this.#logger.audit(
259
- `Application ${id} with instance id ${instanceId} loaded`
260
- );
216
+ const styleElementIds = await this.#styleLoader.load(
217
+ cssAssets,
218
+ loadOptions
219
+ );
220
+ const scriptElementIds = await this.#scriptLoader.load(
221
+ jsAssets,
222
+ loadOptions
223
+ );
224
+ this.#addAppToActiveAppList({
225
+ id,
226
+ instanceId,
227
+ documentEle,
228
+ elementIds: [...styleElementIds, ...scriptElementIds]
229
+ });
230
+ await this.#initApplication(options);
231
+ this.#logger.audit(
232
+ `Application ${id} with instance id ${instanceId} loaded`
233
+ );
234
+ } catch (err) {
235
+ const message = `Application load failed. Unable to load one or more resources for ${options.id} with instance id ${options.instanceId}. ${err.message}`;
236
+ this.#logger.error({
237
+ message,
238
+ appId: options.id,
239
+ exception: err
240
+ });
241
+ throw new Error(message);
242
+ }
261
243
  };
262
244
  #unloadApp = ({ id, instanceId, hostUrl, documentEle }) => {
263
245
  this.#logger.debug(
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Host</title>
8
8
  <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
9
- <script src="https://cdn.qa1.ice.com/pui-diagnostics@3" ></script>
9
+ <script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3" ></script>
10
10
  </head>
11
11
  <body>
12
12
  <header class="bg-indigo-300 h-10 flex place-items-center">
package/dist/esm/index.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import { SecurityContext } from "./typings/common.js";
2
2
  import { Event, ScriptingObject } from "@elliemae/microfe-common";
3
3
  import { CAppBridge } from "./appBridge.js";
4
+ import { ScriptLoader } from "./loaders/script.js";
5
+ import { StyleLoader } from "./loaders/style.js";
4
6
  export {
5
7
  CAppBridge,
6
8
  Event,
9
+ ScriptLoader,
7
10
  ScriptingObject,
8
- SecurityContext
11
+ SecurityContext,
12
+ StyleLoader
9
13
  };