@elliemae/pui-app-bridge 2.21.4 → 2.22.1
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.
- package/dist/cjs/appBridge.js +43 -61
- package/dist/cjs/index.html +1 -1
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/loaders/script.js +162 -10
- package/dist/cjs/loaders/style.js +98 -9
- package/dist/esm/appBridge.js +43 -61
- package/dist/esm/index.html +1 -1
- package/dist/esm/index.js +5 -1
- package/dist/esm/loaders/script.js +162 -10
- package/dist/esm/loaders/style.js +98 -9
- package/dist/public/creditService/latest/creditService.checksum.js +1 -1
- package/dist/public/creditService/latest/creditService.checksum.js.br +0 -0
- package/dist/public/creditService/latest/creditService.checksum.js.gz +0 -0
- package/dist/public/creditService/latest/creditService.checksum.js.map +1 -1
- package/dist/public/frame.html +1 -1
- package/dist/public/index.html +1 -1
- package/dist/public/init.js +1 -1
- package/dist/public/init.js.br +0 -0
- package/dist/public/init.js.gz +0 -0
- package/dist/public/init.js.map +1 -1
- package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js +51 -0
- package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.br +0 -0
- package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.gz +0 -0
- package/dist/public/js/emuiAppBridge.77676d32f64ebb779bb5.js.map +1 -0
- package/dist/public/latest/app.config.json +6 -6
- package/dist/public/loanValidation/latest/loanValidation.checksum.js +1 -1
- package/dist/public/loanValidation/latest/loanValidation.checksum.js.br +0 -0
- package/dist/public/loanValidation/latest/loanValidation.checksum.js.gz +0 -0
- package/dist/public/loanValidation/latest/loanValidation.checksum.js.map +1 -1
- package/dist/public/pricingService/latest/pricingService.checksum.js +2 -2
- package/dist/public/pricingService/latest/pricingService.checksum.js.br +0 -0
- package/dist/public/pricingService/latest/pricingService.checksum.js.gz +0 -0
- package/dist/public/pricingService/latest/pricingService.checksum.js.map +1 -1
- package/dist/public/utils.js +1 -1
- package/dist/public/utils.js.br +0 -0
- package/dist/public/utils.js.gz +0 -0
- package/dist/public/utils.js.map +1 -1
- package/dist/types/lib/index.d.ts +2 -0
- package/dist/types/lib/loaders/script.d.ts +99 -5
- package/dist/types/lib/loaders/style.d.ts +77 -4
- package/dist/types/lib/tests/loaders/script.test.d.ts +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dist/umd/creditService/latest/creditService.checksum.js +1 -1
- package/dist/umd/creditService/latest/creditService.checksum.js.br +0 -0
- package/dist/umd/creditService/latest/creditService.checksum.js.gz +0 -0
- package/dist/umd/creditService/latest/creditService.checksum.js.map +1 -1
- package/dist/umd/index.html +1 -1
- package/dist/umd/index.js +17 -17
- package/dist/umd/index.js.br +0 -0
- package/dist/umd/index.js.gz +0 -0
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/init.js +1 -1
- package/dist/umd/init.js.br +0 -0
- package/dist/umd/init.js.gz +0 -0
- package/dist/umd/init.js.map +1 -1
- package/dist/umd/latest/app.config.json +6 -6
- package/dist/umd/loanValidation/latest/loanValidation.checksum.js +1 -1
- package/dist/umd/loanValidation/latest/loanValidation.checksum.js.br +0 -0
- package/dist/umd/loanValidation/latest/loanValidation.checksum.js.gz +0 -0
- package/dist/umd/loanValidation/latest/loanValidation.checksum.js.map +1 -1
- package/dist/umd/pricingService/latest/pricingService.checksum.js +2 -2
- package/dist/umd/pricingService/latest/pricingService.checksum.js.br +0 -0
- package/dist/umd/pricingService/latest/pricingService.checksum.js.gz +0 -0
- package/dist/umd/pricingService/latest/pricingService.checksum.js.map +1 -1
- package/dist/umd/utils.js +1 -1
- package/dist/umd/utils.js.br +0 -0
- package/dist/umd/utils.js.gz +0 -0
- package/dist/umd/utils.js.map +1 -1
- package/package.json +21 -22
- package/dist/public/js/emuiAppBridge.4c0a4243086e44e29d0b.js +0 -51
- package/dist/public/js/emuiAppBridge.4c0a4243086e44e29d0b.js.br +0 -0
- package/dist/public/js/emuiAppBridge.4c0a4243086e44e29d0b.js.gz +0 -0
- package/dist/public/js/emuiAppBridge.4c0a4243086e44e29d0b.js.map +0 -1
package/dist/cjs/appBridge.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
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
|
-
|
|
267
|
-
index: counter,
|
|
268
|
-
isJsModule
|
|
229
|
+
isESMModule: isJsModule
|
|
269
230
|
};
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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(
|
package/dist/cjs/index.html
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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 =
|
|
43
|
-
ele.src = (0, import_utils.removeDoubleSlash)(
|
|
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)() &&
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 =
|
|
61
|
+
ele.id = id;
|
|
41
62
|
ele.nonce = "__CSP_NONCE__";
|
|
42
63
|
ele.rel = "stylesheet";
|
|
43
|
-
|
|
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"]');
|
package/dist/esm/appBridge.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const
|
|
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
|
-
|
|
252
|
-
index: counter,
|
|
253
|
-
isJsModule
|
|
214
|
+
isESMModule: isJsModule
|
|
254
215
|
};
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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(
|
package/dist/esm/index.html
CHANGED
|
@@ -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.
|
|
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
|
};
|