@elliemae/microfe-common 2.25.0 → 2.26.0-alpha.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 (72) hide show
  1. package/dist/cjs/app.js +33 -0
  2. package/dist/cjs/config.js +65 -0
  3. package/dist/cjs/frame.html +47 -0
  4. package/dist/cjs/frame.js +61 -0
  5. package/dist/cjs/history.js +27 -0
  6. package/dist/cjs/host.js +105 -0
  7. package/dist/cjs/loader.js +158 -0
  8. package/dist/cjs/logRecords.js +57 -0
  9. package/dist/cjs/logger.js +34 -0
  10. package/dist/cjs/manifestLoader.js +66 -0
  11. package/dist/cjs/scriptLoader.js +71 -0
  12. package/dist/cjs/styleLoader.js +57 -0
  13. package/dist/cjs/tests/loan/latest/index.js +80 -0
  14. package/dist/cjs/tests/loan/latest/manifest.json +3 -0
  15. package/dist/cjs/tests/server.js +29 -0
  16. package/dist/cjs/tests/serverHandlers.js +81 -0
  17. package/dist/cjs/tests/task/latest/index.js +58 -0
  18. package/dist/cjs/tests/task/latest/manifest.json +3 -0
  19. package/dist/cjs/typings/guest.js +16 -0
  20. package/dist/cjs/typings/host.js +16 -0
  21. package/dist/cjs/typings/index.js +16 -0
  22. package/dist/cjs/typings/microapp.js +16 -0
  23. package/dist/cjs/utils.js +33 -0
  24. package/dist/cjs/window.js +63 -0
  25. package/dist/esm/app.js +13 -0
  26. package/dist/esm/config.js +35 -0
  27. package/dist/esm/frame.html +47 -0
  28. package/dist/esm/frame.js +41 -0
  29. package/dist/esm/history.js +7 -0
  30. package/dist/esm/host.js +85 -0
  31. package/dist/esm/loader.js +145 -0
  32. package/dist/esm/logRecords.js +37 -0
  33. package/dist/esm/logger.js +17 -0
  34. package/dist/esm/manifestLoader.js +46 -0
  35. package/dist/esm/scriptLoader.js +51 -0
  36. package/dist/esm/styleLoader.js +37 -0
  37. package/dist/esm/tests/loan/latest/index.js +79 -0
  38. package/dist/esm/tests/loan/latest/manifest.json +3 -0
  39. package/dist/esm/tests/server.js +9 -0
  40. package/dist/esm/tests/serverHandlers.js +51 -0
  41. package/dist/esm/tests/task/latest/index.js +57 -0
  42. package/dist/esm/tests/task/latest/manifest.json +3 -0
  43. package/dist/esm/typings/guest.js +0 -0
  44. package/dist/esm/typings/host.js +0 -0
  45. package/dist/esm/typings/index.js +0 -0
  46. package/dist/esm/typings/microapp.js +0 -0
  47. package/dist/esm/utils.js +13 -0
  48. package/dist/esm/window.js +43 -0
  49. package/dist/types/lib/app.d.ts +2 -0
  50. package/dist/types/lib/config.d.ts +25 -0
  51. package/dist/types/lib/frame.d.ts +10 -0
  52. package/dist/types/lib/history.d.ts +2 -0
  53. package/dist/types/lib/host.d.ts +42 -0
  54. package/dist/types/lib/loader.d.ts +9 -0
  55. package/dist/types/lib/logRecords.d.ts +14 -0
  56. package/dist/types/lib/logger.d.ts +1 -0
  57. package/dist/types/lib/manifestLoader.d.ts +7 -0
  58. package/dist/types/lib/scriptLoader.d.ts +6 -0
  59. package/dist/types/lib/styleLoader.d.ts +5 -0
  60. package/dist/types/lib/tests/loader.test.d.ts +1 -0
  61. package/dist/types/lib/tests/loan/latest/index.d.ts +0 -0
  62. package/dist/types/lib/tests/server.d.ts +1 -0
  63. package/dist/types/lib/tests/serverHandlers.d.ts +1 -0
  64. package/dist/types/lib/tests/task/latest/index.d.ts +0 -0
  65. package/dist/types/lib/typings/guest.d.ts +67 -0
  66. package/dist/types/lib/typings/host.d.ts +60 -0
  67. package/dist/types/lib/typings/index.d.ts +3 -0
  68. package/dist/types/lib/typings/microapp.d.ts +17 -0
  69. package/dist/types/lib/utils.d.ts +3 -0
  70. package/dist/types/lib/window.d.ts +27 -0
  71. package/dist/types/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +6 -2
@@ -0,0 +1,41 @@
1
+ const FRAME_CONTAINER_ID_PREFIX = "pui-iframe-container-";
2
+ const FRAME_APP_CONTAINER_ID_PREFIX = "pui-app-container-";
3
+ const createFrame = (options) => new Promise((resolve, reject) => {
4
+ const parentContainer = document.createElement("div");
5
+ parentContainer.setAttribute(
6
+ "style",
7
+ "display: flex;width: 100%;height: 100%;flex-direction: column;overflow: hidden;"
8
+ );
9
+ const frame = document.createElement("iframe");
10
+ frame.setAttribute("id", `${FRAME_CONTAINER_ID_PREFIX}${options.id}`);
11
+ frame.setAttribute("frameborder", "0");
12
+ frame.setAttribute("scrolling", "no");
13
+ frame.setAttribute("allowfullscreen", "true");
14
+ frame.setAttribute("allowtransparency", "true");
15
+ frame.setAttribute("allow", "microphone; camera");
16
+ if (options.sandbox) frame.setAttribute("sandbox", options.sandbox);
17
+ frame.setAttribute(
18
+ "style",
19
+ options.style ?? "flex-grow: 1;border: none;margin: 0;padding: 0;display: block;height: 100%;"
20
+ );
21
+ frame.setAttribute("src", options.src ?? "./frame.html");
22
+ frame.addEventListener("load", () => {
23
+ if (!frame.contentDocument) {
24
+ reject(new Error("Frame content window is null"));
25
+ return;
26
+ }
27
+ const documentEle = frame.contentDocument;
28
+ const ele = documentEle.getElementById(FRAME_APP_CONTAINER_ID_PREFIX);
29
+ if (ele) {
30
+ ele.id = `${ele.id}${options.id}`;
31
+ }
32
+ resolve(frame);
33
+ });
34
+ parentContainer.appendChild(frame);
35
+ document.body.appendChild(parentContainer);
36
+ });
37
+ export {
38
+ FRAME_APP_CONTAINER_ID_PREFIX,
39
+ FRAME_CONTAINER_ID_PREFIX,
40
+ createFrame
41
+ };
@@ -0,0 +1,7 @@
1
+ import { createBrowserHistory, createMemoryHistory } from "history";
2
+ const browserHistory = createBrowserHistory();
3
+ const memoryHistory = createMemoryHistory();
4
+ export {
5
+ browserHistory,
6
+ memoryHistory
7
+ };
@@ -0,0 +1,85 @@
1
+ import { publish, subscribe, unsubscribe } from "pubsub-js";
2
+ import { getDefaultTheme } from "@elliemae/pui-theme";
3
+ import { FRAME_CONTAINER_ID_PREFIX } from "./frame.js";
4
+ import { getAppConfigValue, loadAppConfig } from "./config.js";
5
+ import { browserHistory } from "./history.js";
6
+ import { logger } from "./logger.js";
7
+ class CMicroAppHost {
8
+ static instance;
9
+ logger;
10
+ appId;
11
+ props;
12
+ activeGuests;
13
+ onInit;
14
+ onRenewSessionTimer;
15
+ scriptingObjects;
16
+ constructor(params) {
17
+ this.appId = getAppConfigValue("appId");
18
+ this.onInit = params?.onInit;
19
+ this.logger = params?.logger || logger;
20
+ this.onRenewSessionTimer = params?.onRenewSessionTimer;
21
+ this.props = {
22
+ systemVersion: params?.version ?? "latest",
23
+ history: params?.history ?? browserHistory,
24
+ theme: params?.theme ?? getDefaultTheme()
25
+ };
26
+ this.activeGuests = {};
27
+ this.scriptingObjects = params?.scriptingObjects ?? {};
28
+ this.getProps = this.getProps.bind(this);
29
+ this.getLogger = this.getLogger.bind(this);
30
+ this.getGuests = this.getGuests.bind(this);
31
+ this.getGuest = this.getGuest.bind(this);
32
+ this.getObject = this.getObject.bind(this);
33
+ loadAppConfig().then(() => {
34
+ if (this.onInit) this.onInit(this.props);
35
+ }).catch(() => {
36
+ });
37
+ }
38
+ static getInstance(params) {
39
+ if (!this.instance) {
40
+ this.instance = new this(params);
41
+ }
42
+ return this.instance;
43
+ }
44
+ static isInitialized() {
45
+ return !!this.instance;
46
+ }
47
+ getProps() {
48
+ return this.props;
49
+ }
50
+ getLogger() {
51
+ return this.logger;
52
+ }
53
+ // eslint-disable-next-line @typescript-eslint/require-await
54
+ async getObject(name) {
55
+ return this.scriptingObjects[name];
56
+ }
57
+ getGuests() {
58
+ return this.activeGuests;
59
+ }
60
+ getGuest(id) {
61
+ return this.activeGuests[id] ? this.activeGuests[id] : null;
62
+ }
63
+ setAppWindowSize(appSize) {
64
+ const { appId, size } = appSize;
65
+ const frameEle = document.getElementById(
66
+ `${FRAME_CONTAINER_ID_PREFIX}${appId}`
67
+ );
68
+ if (frameEle) {
69
+ frameEle.style.height = `${size.height}px`;
70
+ }
71
+ }
72
+ publish(eventId, data) {
73
+ return publish(eventId, data);
74
+ }
75
+ // eslint-disable-next-line @typescript-eslint/ban-types
76
+ subscribe(eventId, listener) {
77
+ return subscribe(eventId, listener);
78
+ }
79
+ unsubscribe(token) {
80
+ unsubscribe(token);
81
+ }
82
+ }
83
+ export {
84
+ CMicroAppHost
85
+ };
@@ -0,0 +1,145 @@
1
+ import { CMicroAppHost } from "./host.js";
2
+ import { getAbsoluteUrl } from "./utils.js";
3
+ import { logger } from "./logger.js";
4
+ import { logRecords } from "./logRecords.js";
5
+ import { getCurrentBreakpoint, getViewportSize } from "./window.js";
6
+ import { addStylesToDOM, removeDynamicImportedStyles } from "./styleLoader.js";
7
+ import {
8
+ addScriptToDOM,
9
+ removeDynamicImportedScripts,
10
+ removePrefetchLinks
11
+ } from "./scriptLoader.js";
12
+ import {
13
+ getAppManifest,
14
+ getFullFileNameofAssetsFromManifest
15
+ } from "./manifestLoader.js";
16
+ const APP_CONTAINER_ID_PREFIX = "pui-app-container-";
17
+ const cssType = /\.css$/;
18
+ const isCss = (fileName) => cssType.test(fileName);
19
+ const activeApps = {};
20
+ const getPathName = (url) => url ? new URL(url).pathname : "";
21
+ const initApplication = async ({
22
+ id,
23
+ name,
24
+ hostUrl,
25
+ history,
26
+ theme,
27
+ manifestPath,
28
+ homeRoute
29
+ }) => {
30
+ const app = window.emui?.[id];
31
+ if (!app) {
32
+ throw new Error(
33
+ `Application ${name} with ${id} is not found. Most probably the appId property of app.config.json is not set to ${id}`
34
+ );
35
+ }
36
+ const { init } = app;
37
+ if (!init || typeof init !== "function")
38
+ throw new Error(
39
+ `Application ${name} with id ${id} doesn't expose init method.`
40
+ );
41
+ return init({
42
+ host: CMicroAppHost.getInstance(),
43
+ hostUrl,
44
+ manifestPath,
45
+ homeRoute: homeRoute ?? getPathName(hostUrl),
46
+ prevState: null,
47
+ history,
48
+ theme,
49
+ hostBreakpoint: getCurrentBreakpoint(),
50
+ hostViewportSize: getViewportSize(),
51
+ logger
52
+ });
53
+ };
54
+ const mountApp = async ({ id, name }) => {
55
+ const app = (window.emui || {})[id] || {};
56
+ const { mount } = app;
57
+ if (!mount || typeof mount !== "function")
58
+ throw new Error(
59
+ `Application ${name} with id ${id} doesn't expose mount method.`
60
+ );
61
+ return mount({
62
+ containerId: `${APP_CONTAINER_ID_PREFIX}${id}`,
63
+ hostBreakpoint: getCurrentBreakpoint(),
64
+ hostViewportSize: getViewportSize()
65
+ });
66
+ };
67
+ const unmountApp = async ({ id, name }) => {
68
+ const app = (window.emui || {})[id];
69
+ if (!app) return null;
70
+ const { unmount } = app;
71
+ if (!unmount) return null;
72
+ if (typeof unmount !== "function")
73
+ throw new Error(
74
+ `unmount failed for application ${name} with id ${id}. unmount is not a valid function`
75
+ );
76
+ return Promise.resolve();
77
+ };
78
+ const addAppToActiveAppList = (id, elementIds) => {
79
+ const app = (window.emui || {})[id] || {};
80
+ const { getObject } = app;
81
+ activeApps[id] = { elementIds };
82
+ const host = CMicroAppHost.getInstance();
83
+ if (host) host.activeGuests[id] = getObject && getObject() || {};
84
+ return Promise.resolve();
85
+ };
86
+ const waitAndInitApplication = (appConfig, requests) => (
87
+ // wait for all assets to get downloaded
88
+ Promise.all(requests).then(addAppToActiveAppList.bind(null, appConfig.id)).then(initApplication.bind(null, appConfig)).catch((err) => {
89
+ const logRecord = logRecords.APP_INIT_FAILED(appConfig.id, err.message);
90
+ logger.error({ ...logRecord, exception: err });
91
+ throw new Error(logRecord.message);
92
+ })
93
+ );
94
+ const removeAssetsFromDOM = (id, documentEle = document) => {
95
+ const host = CMicroAppHost.getInstance();
96
+ if (host) delete host.activeGuests[id];
97
+ const { elementIds } = activeApps[id] || {};
98
+ if (elementIds) {
99
+ elementIds.forEach((elementId) => {
100
+ const ele = documentEle.getElementById(elementId);
101
+ if (ele) ele.remove();
102
+ });
103
+ delete activeApps[id];
104
+ }
105
+ };
106
+ const sanitizeAppConfig = (appConfig) => {
107
+ const { hostUrl = "" } = appConfig;
108
+ appConfig.hostUrl = getAbsoluteUrl(hostUrl).replace(/\/?$/, "/");
109
+ };
110
+ const loadApp = async (appConfig) => {
111
+ logger.info(logRecords.APP_LOADING(appConfig.id));
112
+ sanitizeAppConfig(appConfig);
113
+ let assets = appConfig.files;
114
+ const manifest = await getAppManifest(appConfig);
115
+ assets = getFullFileNameofAssetsFromManifest(manifest, appConfig.files);
116
+ let counter = 0;
117
+ const requests = assets.map((fileName) => {
118
+ counter += 1;
119
+ return !isCss(fileName) ? addScriptToDOM(appConfig, fileName, counter) : addStylesToDOM(appConfig, fileName, counter);
120
+ });
121
+ await waitAndInitApplication(appConfig, requests);
122
+ logger.info(logRecords.APP_LOADING_COMPLETE(appConfig.id));
123
+ };
124
+ const unloadApp = ({
125
+ id,
126
+ hostUrl,
127
+ documentEle
128
+ }) => {
129
+ if (!hostUrl) throw new Error("Unable to unload app. hostUrl is required");
130
+ logger.info(logRecords.APP_UNLOADING(id));
131
+ const app = (window.emui || {})[id];
132
+ if (!app) return;
133
+ removeAssetsFromDOM(id, documentEle);
134
+ removeDynamicImportedScripts(hostUrl, documentEle);
135
+ removePrefetchLinks(hostUrl, documentEle);
136
+ removeDynamicImportedStyles(hostUrl, documentEle);
137
+ if (window.emui && window.emui[id]) delete window.emui[id];
138
+ logger.info(logRecords.APP_UNLOADING_COMPLETE(id));
139
+ };
140
+ export {
141
+ loadApp,
142
+ mountApp,
143
+ unloadApp,
144
+ unmountApp
145
+ };
@@ -0,0 +1,37 @@
1
+ const logRecords = {
2
+ APP_CONFIG_LOAD_FAILED: {
3
+ code: "microfe01",
4
+ message: "Unable to load application configuration"
5
+ },
6
+ ASSET_NOT_FOUND_IN_MANIFEST: (assetName) => ({
7
+ code: "microfe02",
8
+ message: `Application load failed. unable to locate ${assetName} in the manifest`
9
+ }),
10
+ APP_INIT_FAILED: (appId, errMsg) => ({
11
+ code: "microfe03",
12
+ message: `Application load failed. Unable to load one or more application resources for appId: ${appId}. ${errMsg}`
13
+ }),
14
+ APP_LOADING: (appId) => ({
15
+ code: "microfe04",
16
+ message: `Application ${appId} is loading...`
17
+ }),
18
+ APP_LOADING_COMPLETE: (appId) => ({
19
+ code: "microfe05",
20
+ message: `Application ${appId} loaded`
21
+ }),
22
+ APP_UNLOADING: (appId) => ({
23
+ code: "microfe06",
24
+ message: `Application ${appId} unloading...`
25
+ }),
26
+ APP_UNLOADING_COMPLETE: (appId) => ({
27
+ code: "microfe07",
28
+ message: `Application ${appId} unloaded`
29
+ }),
30
+ SSF_HOST_OBJECT_NOT_FOUND: (name) => ({
31
+ code: "microfe08",
32
+ message: `Parent window doesn't expose SSF Object named ${name}`
33
+ })
34
+ };
35
+ export {
36
+ logRecords
37
+ };
@@ -0,0 +1,17 @@
1
+ import {
2
+ logger as uiLogger,
3
+ Console
4
+ } from "@elliemae/pui-diagnostics";
5
+ const consoleLogger = (() => uiLogger({
6
+ transport: Console(),
7
+ index: "app",
8
+ environment: "NA",
9
+ team: "app team",
10
+ appName: "ICEMT App",
11
+ appVersion: "latest",
12
+ userId: ""
13
+ }))();
14
+ const logger = window?.emui?.logger || consoleLogger;
15
+ export {
16
+ logger
17
+ };
@@ -0,0 +1,46 @@
1
+ import { removeDoubleSlash } from "./utils.js";
2
+ import { logger } from "./logger.js";
3
+ import { logRecords } from "./logRecords.js";
4
+ const getUnVersionedManifestPath = (path) => path.replace(/\/\d+\.\d+/, "/latest");
5
+ const getAppManifest = async ({
6
+ hostUrl,
7
+ manifestPath
8
+ }) => {
9
+ if (!hostUrl || !manifestPath)
10
+ throw new Error(
11
+ "Unable to get app manifest. hostUrl and manifestPath are required."
12
+ );
13
+ const url = new URL(
14
+ `${manifestPath.replace(/\/?$/, "/")}manifest.json`,
15
+ hostUrl
16
+ );
17
+ const response = await fetch(removeDoubleSlash(url.href));
18
+ const { headers } = response;
19
+ const contentType = headers?.get?.("content-type") ?? "";
20
+ if (contentType.includes("application/json")) {
21
+ const data = await response.json();
22
+ return data;
23
+ }
24
+ const unVersionedManifestPath = getUnVersionedManifestPath(manifestPath);
25
+ if (manifestPath !== unVersionedManifestPath) {
26
+ return getAppManifest({
27
+ hostUrl,
28
+ manifestPath: getUnVersionedManifestPath(manifestPath)
29
+ });
30
+ }
31
+ throw new Error("manifest.json is not available for the application");
32
+ };
33
+ const getFullFileNameofAssetsFromManifest = (manifest, assetNames = []) => assetNames.reduce((assets, assetName) => {
34
+ const fullFileName = manifest[assetName];
35
+ if (fullFileName) assets.push(fullFileName);
36
+ else {
37
+ const logRecord = logRecords.ASSET_NOT_FOUND_IN_MANIFEST(assetName);
38
+ logger.error(logRecord);
39
+ throw new Error(logRecord.message);
40
+ }
41
+ return assets;
42
+ }, []);
43
+ export {
44
+ getAppManifest,
45
+ getFullFileNameofAssetsFromManifest
46
+ };
@@ -0,0 +1,51 @@
1
+ import { removeDoubleSlash } from "./utils.js";
2
+ const APP_SCRIPT_ID_PREFIX = "emui-script-";
3
+ const HEAD_SCRIPTS = /(?:emuiDiagnostics|global|global-prod|emuiUserMonitoring)(?:..*)?.js/;
4
+ const isHeadScript = (scriptSrc) => HEAD_SCRIPTS.test(scriptSrc);
5
+ const addScriptToDOM = ({ name, hostUrl, documentEle }, fileName, index) => {
6
+ if (!hostUrl)
7
+ throw new Error("Unable to add scripts to DOM. hostUrl is required.");
8
+ return new Promise((resolve, reject) => {
9
+ const ele = documentEle.createElement("script");
10
+ if (!ele) reject(new Error("Unable to insert Application scripts."));
11
+ ele.id = `${APP_SCRIPT_ID_PREFIX}${name}-${index}`;
12
+ const url = new URL(fileName, hostUrl);
13
+ ele.src = removeDoubleSlash(url.href);
14
+ ele.onload = resolve.bind(null, ele.id);
15
+ ele.onerror = reject.bind(null, ele.id);
16
+ ele.async = false;
17
+ if (isHeadScript(ele.src)) documentEle.head.appendChild(ele);
18
+ else documentEle.body.appendChild(ele);
19
+ });
20
+ };
21
+ const removeScriptFromDOM = (elementId = "", documentEle = document) => new Promise((resolve) => {
22
+ const ele = documentEle.getElementById(elementId);
23
+ if (!ele) console.warn(new Error(`script with id ${elementId} not found`));
24
+ ele.remove();
25
+ resolve();
26
+ });
27
+ const removeDynamicImportedScripts = (hostUrl, documentEle) => {
28
+ const hostPattern = new RegExp(hostUrl, "i");
29
+ const scriptElements = documentEle.getElementsByTagName("script");
30
+ for (let index = scriptElements.length - 1; index >= 0; index -= 1) {
31
+ const scriptEle = scriptElements[index];
32
+ const { src } = scriptEle;
33
+ if (hostPattern.test(src)) scriptEle.remove();
34
+ }
35
+ };
36
+ const removePrefetchLinks = (hostUrl, documentEle) => {
37
+ const hostPattern = new RegExp(hostUrl, "i");
38
+ const prefetchElements = documentEle.querySelectorAll('[rel="prefetch"]');
39
+ for (let index = prefetchElements.length - 1; index >= 0; index -= 1) {
40
+ const ele = prefetchElements[index];
41
+ const { href } = ele;
42
+ if (hostPattern.test(href)) ele.remove();
43
+ }
44
+ };
45
+ export {
46
+ APP_SCRIPT_ID_PREFIX,
47
+ addScriptToDOM,
48
+ removeDynamicImportedScripts,
49
+ removePrefetchLinks,
50
+ removeScriptFromDOM
51
+ };
@@ -0,0 +1,37 @@
1
+ import { removeDoubleSlash } from "./utils.js";
2
+ const APP_STYLE_ID_PREFIX = "emui-style-";
3
+ const addStylesToDOM = ({ name, hostUrl, documentEle }, fileName, index) => {
4
+ if (!hostUrl)
5
+ throw new Error("Unable to add styles to DOM. hostUrl is required.");
6
+ return new Promise((resolve, reject) => {
7
+ const ele = documentEle.createElement("link");
8
+ if (!ele) reject(new Error("Unable to insert Application styles."));
9
+ ele.id = `${APP_STYLE_ID_PREFIX}${name}-${index}`;
10
+ ele.rel = "stylesheet";
11
+ const url = new URL(fileName, hostUrl);
12
+ ele.href = removeDoubleSlash(url.href);
13
+ ele.onload = resolve.bind(null, ele.id);
14
+ documentEle.head.appendChild(ele);
15
+ });
16
+ };
17
+ const removeStyleFromDOM = (elementId = "", documentEle = document) => new Promise((resolve) => {
18
+ const ele = documentEle.getElementById(elementId);
19
+ if (!ele) console.warn(new Error(`style with id ${elementId} not found`));
20
+ ele.remove();
21
+ resolve();
22
+ });
23
+ const removeDynamicImportedStyles = (hostUrl, documentEle) => {
24
+ const hostPattern = new RegExp(hostUrl, "i");
25
+ const prefetchElements = documentEle.querySelectorAll('[rel="stylesheet"]');
26
+ for (let index = prefetchElements.length - 1; index >= 0; index -= 1) {
27
+ const ele = prefetchElements[index];
28
+ const { href } = ele;
29
+ if (hostPattern.test(href)) ele.remove();
30
+ }
31
+ };
32
+ export {
33
+ APP_STYLE_ID_PREFIX,
34
+ addStylesToDOM,
35
+ removeDynamicImportedStyles,
36
+ removeStyleFromDOM
37
+ };
@@ -0,0 +1,79 @@
1
+ (function() {
2
+ const appId = "loanapp";
3
+ const appName = "Loan App";
4
+ const appElementId = `pui-app-container-${appId}`;
5
+ const pipelinePath = "/pipeline";
6
+ const pipelineLinkText = "Pipeline";
7
+ const getWindow = () => {
8
+ try {
9
+ window.parent.document;
10
+ return window.parent;
11
+ } catch (err) {
12
+ return window;
13
+ }
14
+ };
15
+ let host = null;
16
+ let parentHistory = null;
17
+ let logger = null;
18
+ const browserWindow = getWindow();
19
+ browserWindow.emui = browserWindow.emui || {};
20
+ browserWindow.emui[appId] = browserWindow.emui[appId] || {};
21
+ const setFrameSize = () => {
22
+ const { document: document2 } = window;
23
+ const iframeBody = document2.body;
24
+ const iframeHTML = document2.documentElement;
25
+ const size = {
26
+ height: Math.max(
27
+ iframeBody.scrollHeight,
28
+ iframeBody.offsetHeight,
29
+ iframeHTML.offsetHeight
30
+ ),
31
+ width: Math.max(
32
+ iframeBody.scrollWidth,
33
+ iframeBody.offsetWidth,
34
+ iframeHTML.offsetWidth
35
+ )
36
+ };
37
+ host.setAppWindowSize({
38
+ appId,
39
+ size
40
+ });
41
+ };
42
+ browserWindow.emui[appId].init = async (options) => {
43
+ host = options.host;
44
+ parentHistory = options.history;
45
+ logger = options.logger;
46
+ setFrameSize();
47
+ };
48
+ browserWindow.emui[appId].mount = async () => {
49
+ const appContainer = document.getElementById(appElementId);
50
+ if (appContainer) {
51
+ const mainElement = document.createElement("main");
52
+ appContainer.appendChild(mainElement);
53
+ const pageHeaderEle = document.createElement("h1");
54
+ pageHeaderEle.textContent = appName;
55
+ mainElement.appendChild(pageHeaderEle);
56
+ const contentEle = document.createElement("p");
57
+ contentEle.textContent = "Go to ";
58
+ mainElement.appendChild(contentEle);
59
+ const linkEle = document.createElement("a");
60
+ linkEle.href = "#";
61
+ linkEle.onclick = () => {
62
+ parentHistory?.push?.(pipelinePath);
63
+ };
64
+ linkEle.textContent = pipelineLinkText;
65
+ contentEle.appendChild(linkEle);
66
+ } else {
67
+ throw new Error(
68
+ `App container element with id ${appElementId} not found`
69
+ );
70
+ }
71
+ };
72
+ browserWindow.emui[appId].unmount = () => {
73
+ const appContainer = document.getElementById(appElementId);
74
+ if (appContainer) {
75
+ appContainer.removeChild(appContainer.getElementsByTagName("main")[0]);
76
+ }
77
+ return Promise.resolve();
78
+ };
79
+ })();
@@ -0,0 +1,3 @@
1
+ {
2
+ "index.js": "latest/index.js"
3
+ }
@@ -0,0 +1,9 @@
1
+ import { setupServer } from "msw/node";
2
+ import { serverHandlers } from "./serverHandlers";
3
+ const initServer = () => {
4
+ const server = setupServer(...serverHandlers);
5
+ return server;
6
+ };
7
+ export {
8
+ initServer
9
+ };
@@ -0,0 +1,51 @@
1
+ import { rest } from "msw";
2
+ import { readFile } from "fs/promises";
3
+ import path from "node:path";
4
+ import appConfig from "../app.config.json";
5
+ import loanAppManifest from "./loan/latest/manifest.json";
6
+ const sendJS = async (res, ctx, filePath) => {
7
+ const fileContent = await readFile(path.join(__dirname, filePath), "utf-8");
8
+ return res(
9
+ ctx.status(200),
10
+ ctx.set("Content-Type", "application/javascript"),
11
+ ctx.body(fileContent)
12
+ );
13
+ };
14
+ const serverHandlers = [
15
+ rest.get(
16
+ "https://www.google-analytics.com/analytics.js",
17
+ (req, res, ctx) => res(
18
+ ctx.status(200),
19
+ ctx.set("Content-Type", "application/javascript"),
20
+ ctx.body("")
21
+ )
22
+ ),
23
+ rest.get(
24
+ "/latest/app.config.json",
25
+ (req, res, ctx) => res(ctx.json(appConfig))
26
+ ),
27
+ // frame html
28
+ rest.get("/frame.html", async (req, res, ctx) => {
29
+ const fileContent = await readFile(
30
+ path.join(__dirname, "../frame.html"),
31
+ "utf-8"
32
+ );
33
+ return res(
34
+ ctx.status(200),
35
+ ctx.set("Content-Type", "text/html"),
36
+ ctx.body(fileContent)
37
+ );
38
+ }),
39
+ rest.get("/loan/latest/index.js", async (req, res, ctx) => {
40
+ const newRes = await sendJS(res, ctx, "./loan/latest/index.js");
41
+ return newRes;
42
+ }),
43
+ // loan microapp handler
44
+ rest.get(
45
+ "/loan/latest/manifest.json",
46
+ (req, res, ctx) => res(ctx.json(loanAppManifest))
47
+ )
48
+ ];
49
+ export {
50
+ serverHandlers
51
+ };
@@ -0,0 +1,57 @@
1
+ (function() {
2
+ const appId = "taskapp";
3
+ const appName = "Task App";
4
+ const appElementId = `pui-app-container-${appId}`;
5
+ const pipelinePath = "/pipeline";
6
+ const pipelineLinkText = "Pipeline";
7
+ const getWindow = () => {
8
+ try {
9
+ window.parent.document;
10
+ return window.parent;
11
+ } catch (err) {
12
+ return window;
13
+ }
14
+ };
15
+ let host = null;
16
+ let parentHistory = null;
17
+ let logger = null;
18
+ const browserWindow = getWindow();
19
+ browserWindow.emui = browserWindow.emui || {};
20
+ browserWindow.emui[appId] = browserWindow.emui[appId] || {};
21
+ browserWindow.emui[appId].init = async (options) => {
22
+ host = options.host;
23
+ parentHistory = options.history;
24
+ logger = options.logger;
25
+ };
26
+ browserWindow.emui[appId].mount = async () => {
27
+ const appContainer = document.getElementById(appElementId);
28
+ if (appContainer) {
29
+ const mainElement = document.createElement("main");
30
+ appContainer.appendChild(mainElement);
31
+ const pageHeaderEle = document.createElement("h1");
32
+ pageHeaderEle.textContent = appName;
33
+ mainElement.appendChild(pageHeaderEle);
34
+ const contentEle = document.createElement("p");
35
+ contentEle.textContent = "Go to ";
36
+ mainElement.appendChild(contentEle);
37
+ const linkEle = document.createElement("a");
38
+ linkEle.href = "#";
39
+ linkEle.onclick = () => {
40
+ parentHistory?.push?.(pipelinePath);
41
+ };
42
+ linkEle.textContent = pipelineLinkText;
43
+ contentEle.appendChild(linkEle);
44
+ } else {
45
+ throw new Error(
46
+ `App container element with id ${appElementId} not found`
47
+ );
48
+ }
49
+ };
50
+ browserWindow.emui[appId].unmount = () => {
51
+ const appContainer = document.getElementById(appElementId);
52
+ if (appContainer) {
53
+ appContainer.removeChild(appContainer.getElementsByTagName("main")[0]);
54
+ }
55
+ return Promise.resolve();
56
+ };
57
+ })();
@@ -0,0 +1,3 @@
1
+ {
2
+ "index.js": "latest/index.js"
3
+ }
File without changes
File without changes
File without changes