@elliemae/pui-app-sdk 5.40.4 → 5.41.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 (57) hide show
  1. package/demo/{8201.6a8c0542.iframe.bundle.js → 1080.519b1144.iframe.bundle.js} +317 -317
  2. package/demo/1080.519b1144.iframe.bundle.js.br +0 -0
  3. package/demo/1080.519b1144.iframe.bundle.js.gz +0 -0
  4. package/demo/docs/{8201.6a8c0542.iframe.bundle.js → 1080.519b1144.iframe.bundle.js} +317 -317
  5. package/demo/docs/1080.519b1144.iframe.bundle.js.br +0 -0
  6. package/demo/docs/1080.519b1144.iframe.bundle.js.gz +0 -0
  7. package/demo/docs/iframe.html +1 -1
  8. package/demo/docs/index.html +1 -1
  9. package/demo/docs/main.1bf7b965.iframe.bundle.js +5 -0
  10. package/demo/docs/main.1bf7b965.iframe.bundle.js.br +0 -0
  11. package/demo/docs/main.1bf7b965.iframe.bundle.js.gz +0 -0
  12. package/demo/docs/{main.4b26e7c4efd9fa564d80.manager.bundle.js → main.ba76a04bac90176acd00.manager.bundle.js} +1 -1
  13. package/demo/docs/main.ba76a04bac90176acd00.manager.bundle.js.br +0 -0
  14. package/demo/docs/{main.4b26e7c4efd9fa564d80.manager.bundle.js.gz → main.ba76a04bac90176acd00.manager.bundle.js.gz} +0 -0
  15. package/demo/docs/project.json +1 -1
  16. package/demo/iframe.html +1 -1
  17. package/demo/index.html +1 -1
  18. package/demo/main.1bf7b965.iframe.bundle.js +5 -0
  19. package/demo/main.1bf7b965.iframe.bundle.js.br +0 -0
  20. package/demo/main.1bf7b965.iframe.bundle.js.gz +0 -0
  21. package/demo/{main.4b26e7c4efd9fa564d80.manager.bundle.js → main.ba76a04bac90176acd00.manager.bundle.js} +1 -1
  22. package/demo/main.ba76a04bac90176acd00.manager.bundle.js.br +0 -0
  23. package/demo/{main.4b26e7c4efd9fa564d80.manager.bundle.js.gz → main.ba76a04bac90176acd00.manager.bundle.js.gz} +0 -0
  24. package/demo/project.json +1 -1
  25. package/dist/cjs/utils/micro-frontend/guest-host-context.js +47 -0
  26. package/dist/cjs/utils/micro-frontend/guest.js +48 -14
  27. package/dist/cjs/utils/micro-frontend/module-so.js +5 -1
  28. package/dist/cjs/utils/micro-frontend/tests/guest-test-utils.js +27 -0
  29. package/dist/cjs/view/guest-microapp.js +16 -4
  30. package/dist/cjs/view/tests/scriptingObjects/loan.js +4 -0
  31. package/dist/esm/utils/micro-frontend/guest-host-context.js +27 -0
  32. package/dist/esm/utils/micro-frontend/guest.js +48 -14
  33. package/dist/esm/utils/micro-frontend/module-so.js +5 -1
  34. package/dist/esm/utils/micro-frontend/tests/guest-test-utils.js +7 -0
  35. package/dist/esm/view/guest-microapp.js +19 -4
  36. package/dist/esm/view/tests/scriptingObjects/loan.js +4 -0
  37. package/dist/types/lib/utils/micro-frontend/guest-host-context.d.ts +40 -0
  38. package/dist/types/lib/utils/micro-frontend/guest.d.ts +6 -0
  39. package/dist/types/lib/utils/micro-frontend/module-so.d.ts +3 -1
  40. package/dist/types/lib/utils/micro-frontend/tests/guest-host-context.test.d.ts +1 -0
  41. package/dist/types/lib/utils/micro-frontend/tests/guest-test-utils.d.ts +2 -0
  42. package/dist/types/lib/view/guest-microapp.d.ts +4 -0
  43. package/dist/types/lib/view/tests/scriptingObjects/loan.d.ts +1 -0
  44. package/dist/types/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +6 -6
  46. package/demo/8201.6a8c0542.iframe.bundle.js.br +0 -0
  47. package/demo/8201.6a8c0542.iframe.bundle.js.gz +0 -0
  48. package/demo/docs/8201.6a8c0542.iframe.bundle.js.br +0 -0
  49. package/demo/docs/8201.6a8c0542.iframe.bundle.js.gz +0 -0
  50. package/demo/docs/main.15fc50b1.iframe.bundle.js +0 -5
  51. package/demo/docs/main.15fc50b1.iframe.bundle.js.br +0 -0
  52. package/demo/docs/main.15fc50b1.iframe.bundle.js.gz +0 -0
  53. package/demo/docs/main.4b26e7c4efd9fa564d80.manager.bundle.js.br +0 -0
  54. package/demo/main.15fc50b1.iframe.bundle.js +0 -5
  55. package/demo/main.15fc50b1.iframe.bundle.js.br +0 -0
  56. package/demo/main.15fc50b1.iframe.bundle.js.gz +0 -0
  57. package/demo/main.4b26e7c4efd9fa564d80.manager.bundle.js.br +0 -0
package/demo/project.json CHANGED
@@ -1 +1 @@
1
- {"generatedAt":1780016084078,"builder":{"name":"webpack5"},"hasCustomBabel":false,"hasCustomWebpack":true,"hasStaticDirs":false,"hasStorybookEslint":false,"refCount":0,"packageManager":{"type":"pnpm","version":"8.15.9"},"typescriptOptions":{"check":false,"checkOptions":{},"reactDocgen":false,"reactDocgenTypescriptOptions":{"shouldExtractLiteralValuesFromEnum":true,"compilerOptions":{"allowSyntheticDefaultImports":false,"esModuleInterop":false}}},"features":{"fastRefresh":true,"storyStoreV7":true},"language":"javascript","storybookPackages":{},"framework":{"options":{"fastRefresh":true,"strictMode":true,"legacyRootApi":true}},"addons":{"@storybook/addon-docs":{"options":{"sourceLoaderOptions":{"injectStoryParameters":false}},"version":"6.5.16"},"@storybook/addon-essentials":{"options":{"docs":false},"version":"6.5.16"},"@storybook/addon-storysource":{"version":"6.5.16"},"@storybook/addon-links":{"version":"6.5.16"},"@storybook/addon-events":{"version":"6.2.9"},"@storybook/addon-interactions":{"version":"6.5.16"},"@storybook/addon-a11y":{"version":"6.5.16"},"storybook-addon-turbo-build":{"options":{"optimizationLevel":2,"esbuildMinifyOptions":{"target":"es2022"}},"version":"1.1.0"}}}
1
+ {"generatedAt":1782148675244,"builder":{"name":"webpack5"},"hasCustomBabel":false,"hasCustomWebpack":true,"hasStaticDirs":false,"hasStorybookEslint":false,"refCount":0,"packageManager":{"type":"pnpm","version":"8.15.9"},"typescriptOptions":{"check":false,"checkOptions":{},"reactDocgen":false,"reactDocgenTypescriptOptions":{"shouldExtractLiteralValuesFromEnum":true,"compilerOptions":{"allowSyntheticDefaultImports":false,"esModuleInterop":false}}},"features":{"fastRefresh":true,"storyStoreV7":true},"language":"javascript","storybookPackages":{},"framework":{"options":{"fastRefresh":true,"strictMode":true,"legacyRootApi":true}},"addons":{"@storybook/addon-docs":{"options":{"sourceLoaderOptions":{"injectStoryParameters":false}},"version":"6.5.16"},"@storybook/addon-essentials":{"options":{"docs":false},"version":"6.5.16"},"@storybook/addon-storysource":{"version":"6.5.16"},"@storybook/addon-links":{"version":"6.5.16"},"@storybook/addon-events":{"version":"6.2.9"},"@storybook/addon-interactions":{"version":"6.5.16"},"@storybook/addon-a11y":{"version":"6.5.16"},"storybook-addon-turbo-build":{"options":{"optimizationLevel":2,"esbuildMinifyOptions":{"target":"es2022"}},"version":"1.1.0"}}}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var guest_host_context_exports = {};
20
+ __export(guest_host_context_exports, {
21
+ resolveGuestOpenHistory: () => resolveGuestOpenHistory,
22
+ resolveNestedGuestFrameOptions: () => resolveNestedGuestFrameOptions
23
+ });
24
+ module.exports = __toCommonJS(guest_host_context_exports);
25
+ var import_config = require("../app-config/config.js");
26
+ var import_history = require("../history.js");
27
+ var import_guest = require("./guest.js");
28
+ const resolveGuestOpenHistory = (explicitHistory) => {
29
+ const hostUsesParentHistory = (0, import_config.getAppConfigValue)(
30
+ "useParentHistory",
31
+ true
32
+ );
33
+ if (hostUsesParentHistory && typeof window !== "undefined" && window.self !== window.top && import_guest.CMicroAppGuest.isInitialized()) {
34
+ return import_guest.CMicroAppGuest.getInstance().getProps().history;
35
+ }
36
+ return explicitHistory ?? import_history.browserHistory;
37
+ };
38
+ const resolveNestedGuestFrameOptions = (userFrameOptions) => {
39
+ if (typeof window === "undefined" || window.self === window.top) {
40
+ return userFrameOptions;
41
+ }
42
+ const versionSegment = window.location.hostname === "localhost" ? "latest" : window.emui?.version?.match(/^(\d+\.\d+)/)?.[1] ?? "latest";
43
+ return {
44
+ src: `${window.location.origin}/${versionSegment}/frame.html`,
45
+ ...userFrameOptions
46
+ };
47
+ };
@@ -83,6 +83,7 @@ class CMicroAppGuest {
83
83
  */
84
84
  #useV2Guest;
85
85
  #selfInitialize;
86
+ #embeddedWithoutHost;
86
87
  /**
87
88
  * AppBridge instance
88
89
  */
@@ -97,7 +98,7 @@ class CMicroAppGuest {
97
98
  onMount;
98
99
  onUnmount;
99
100
  onGetRef;
100
- // eslint-disable-next-line max-statements
101
+ // eslint-disable-next-line max-statements, complexity
101
102
  constructor(params) {
102
103
  window.emui.uuid = window.emui.uuid || (0, import_uuid.v4)();
103
104
  this.appId = window.emui?.appId || (0, import_config.getAppConfigValue)("appId");
@@ -118,10 +119,16 @@ class CMicroAppGuest {
118
119
  this.#ssfHostRef = (0, import_ssf_host2.getSSFHost)();
119
120
  this.#useV2Guest = params?.useV2Guest !== false;
120
121
  this.#selfInitialize = params?.selfInitialize ?? false;
122
+ this.#embeddedWithoutHost = params?.embeddedWithoutHost ?? false;
121
123
  this.onInit = params?.onInit;
122
124
  this.onMount = params?.onMount;
123
125
  this.onUnmount = params?.onUnmount;
124
126
  this.onGetRef = params?.onGetRef;
127
+ if (this.#embeddedWithoutHost && !this.#selfInitialize) {
128
+ this.logger.warn(
129
+ "embeddedWithoutHost requires selfInitialize: true \u2014 the flag only applies to initialize()"
130
+ );
131
+ }
125
132
  this.getHost = this.getHost.bind(this);
126
133
  this.getLogger = this.getLogger.bind(this);
127
134
  this.getProps = this.getProps.bind(this);
@@ -317,6 +324,10 @@ class CMicroAppGuest {
317
324
  if (window.emui?.__host) {
318
325
  return window.emui.__host;
319
326
  }
327
+ if (!isCrossDomain()) {
328
+ const legacyHost = window.parent?.emui?.MicroAppHost ?? null;
329
+ if (legacyHost) return legacyHost;
330
+ }
320
331
  if (this.#useV2Guest) {
321
332
  const ssfGuest = new import_ssf_guest.SSFGuest({ appLogger: this.logger });
322
333
  const failed = await (0, import_helpers.abortPromise)({
@@ -370,6 +381,33 @@ class CMicroAppGuest {
370
381
  const route = typeof params.initialRoute === "string" ? params.initialRoute : params.initialRoute.pathname;
371
382
  if (route) this.props.history.push(route);
372
383
  }
384
+ async #loadConfigAndBridge() {
385
+ await (0, import_app_config.loadAppConfig)();
386
+ await this.#initAppBridge();
387
+ }
388
+ #trackAnalyticsAndOnInit() {
389
+ const instanceId = this.getSessionStorageItem("instanceId") || "";
390
+ const userId = this.getSessionStorageItem("userId") || "";
391
+ (0, import_pui_analytics_so.updateBAEventParameters)({ appId: this.appId, instanceId, userId });
392
+ if (this.onInit) this.onInit(this.props);
393
+ }
394
+ #completeWithoutHost(mode) {
395
+ if (mode === "standalone") {
396
+ this.logger.info("Running in standalone mode \u2014 skipping host discovery");
397
+ this.#exposeGetHost();
398
+ this.#resolveHistory();
399
+ } else if (mode === "embeddedWithoutHost") {
400
+ this.logger.info("embeddedWithoutHost enabled \u2014 skipping host discovery");
401
+ this.#exposeGetHost();
402
+ } else {
403
+ this.logger.warn(
404
+ "Unable to discover host. Proceeding with app.config.json values. Host-provided parameters (history, theme, initialRoute) will not be available."
405
+ );
406
+ this.#exposeGetHost();
407
+ }
408
+ this.#trackAnalyticsAndOnInit();
409
+ return { moduleId: this.appId };
410
+ }
373
411
  /**
374
412
  * Guest-initiated lifecycle: discovers the host and applies module
375
413
  * parameters if available. After this resolves the guest is ready to render.
@@ -385,20 +423,19 @@ class CMicroAppGuest {
385
423
  (0, import_webpack_public_path.syncWebpackPublicPathFromAssetPath)();
386
424
  const isStandalone = window.top === window.self && !window.opener;
387
425
  if (isStandalone) {
388
- await (0, import_app_config.loadAppConfig)();
389
- await this.#initAppBridge();
390
- this.logger.info("Running in standalone mode \u2014 skipping host discovery");
391
- if (this.onInit) this.onInit(this.props);
392
- return { moduleId: this.appId };
426
+ await this.#loadConfigAndBridge();
427
+ return this.#completeWithoutHost("standalone");
428
+ }
429
+ if (this.#embeddedWithoutHost) {
430
+ await this.#loadConfigAndBridge();
431
+ return this.#completeWithoutHost("embeddedWithoutHost");
393
432
  }
394
433
  const [host] = await Promise.all([
395
434
  this.#discoverHost(),
396
- (0, import_app_config.loadAppConfig)().then(() => this.#initAppBridge())
435
+ this.#loadConfigAndBridge()
397
436
  ]);
398
437
  if (!host) {
399
- throw new Error(
400
- "Unable to discover host. Ensure the host uses an updated pui-app-sdk and pui-microfe, or use the host-initiated model (onInit/onMount)."
401
- );
438
+ return this.#completeWithoutHost("discoveryFallback");
402
439
  }
403
440
  this.props.host = host;
404
441
  this.#exposeGetHost();
@@ -418,10 +455,7 @@ class CMicroAppGuest {
418
455
  `Failed to retrieve module parameters for "${this.appId}". Proceeding without host-provided parameters.`
419
456
  );
420
457
  }
421
- const instanceId = this.getSessionStorageItem("instanceId") || "";
422
- const userId = this.getSessionStorageItem("userId") || "";
423
- (0, import_pui_analytics_so.updateBAEventParameters)({ appId: this.appId, instanceId, userId });
424
- if (this.onInit) this.onInit(this.props);
458
+ this.#trackAnalyticsAndOnInit();
425
459
  return params;
426
460
  }
427
461
  async init(options) {
@@ -25,6 +25,7 @@ __export(module_so_exports, {
25
25
  module.exports = __toCommonJS(module_so_exports);
26
26
  var import_ssf_host = require("@elliemae/ssf-host");
27
27
  var import_index = require("./index.js");
28
+ var import_guest_host_context = require("./guest-host-context.js");
28
29
  var import_const = require("../../view/micro-app/const.js");
29
30
  const LOG_LEVEL_METHOD = {
30
31
  DEBUG: "debug",
@@ -84,7 +85,10 @@ class GuestModule extends import_ssf_host.ScriptingObject {
84
85
  };
85
86
  }
86
87
  const createGuestModule = (guestId, params = {}, overrides = {}) => {
87
- const mod = new GuestModule(guestId, params);
88
+ const mod = new GuestModule(guestId, {
89
+ ...params,
90
+ history: (0, import_guest_host_context.resolveGuestOpenHistory)(params.history)
91
+ });
88
92
  Object.assign(mod, overrides);
89
93
  return mod;
90
94
  };
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var guest_test_utils_exports = {};
20
+ __export(guest_test_utils_exports, {
21
+ resetCMicroAppGuestInstance: () => resetCMicroAppGuestInstance
22
+ });
23
+ module.exports = __toCommonJS(guest_test_utils_exports);
24
+ var import_guest = require("../guest.js");
25
+ const resetCMicroAppGuestInstance = () => {
26
+ Reflect.set(import_guest.CMicroAppGuest, "instance", null);
27
+ };
@@ -41,6 +41,7 @@ var import_app_bridge = require("../utils/micro-frontend/app-bridge.js");
41
41
  var import_ssf_host2 = require("../utils/micro-frontend/ssf-host.js");
42
42
  var import_actions = require("../data/wait-message/actions.js");
43
43
  var import_micro_frontend = require("../utils/micro-frontend/index.js");
44
+ var import_guest_host_context = require("../utils/micro-frontend/guest-host-context.js");
44
45
  const Div = import_styled_components.default.div`
45
46
  display: flex;
46
47
  width: 100%;
@@ -59,13 +60,22 @@ const useAppRenderer = (props) => {
59
60
  onUnloadComplete,
60
61
  containerId,
61
62
  ssfAppOptions,
62
- disableWaitMessage = false
63
+ disableWaitMessage = false,
64
+ frameOptions: userFrameOptions
63
65
  } = props;
64
66
  const dispatch = (0, import_react_redux.useDispatch)();
65
67
  const stableSsfAppOptions = (0, import_react.useMemo)(
66
68
  () => ssfAppOptions ?? {},
67
69
  [ssfAppOptions]
68
70
  );
71
+ const guestHistory = (0, import_react.useMemo)(
72
+ () => (0, import_guest_host_context.resolveGuestOpenHistory)(history),
73
+ [history]
74
+ );
75
+ const resolvedFrameOptions = (0, import_react.useMemo)(
76
+ () => (0, import_guest_host_context.resolveNestedGuestFrameOptions)(userFrameOptions),
77
+ [userFrameOptions]
78
+ );
69
79
  const getConfig = (0, import_react.useCallback)(
70
80
  () => (0, import_micro_frontend.getMicroFrontEndAppConfig)({
71
81
  id
@@ -141,11 +151,12 @@ const useAppRenderer = (props) => {
141
151
  id,
142
152
  selfInitialize: configSelfInitialize,
143
153
  frameOptions: {
154
+ ...resolvedFrameOptions,
144
155
  containerId,
145
156
  queryParams: (0, import_pui_logrocket.buildLogRocketQueryParams)(),
146
157
  signal: abortController.signal
147
158
  },
148
- history,
159
+ history: guestHistory,
149
160
  homeRoute,
150
161
  initialRoute
151
162
  });
@@ -213,7 +224,7 @@ const useAppRenderer = (props) => {
213
224
  }, [
214
225
  containerId,
215
226
  dispatch,
216
- history,
227
+ guestHistory,
217
228
  homeRoute,
218
229
  initialRoute,
219
230
  id,
@@ -221,7 +232,8 @@ const useAppRenderer = (props) => {
221
232
  onUnloadComplete,
222
233
  getConfig,
223
234
  stableSsfAppOptions,
224
- disableWaitMessage
235
+ disableWaitMessage,
236
+ resolvedFrameOptions
225
237
  ]);
226
238
  };
227
239
  const GuestMicroApp = (0, import_react.memo)(
@@ -61,6 +61,10 @@ class Loan extends import_pui_app_bridge.ScriptingObject {
61
61
  name: "editmodechange",
62
62
  objectId: "loan"
63
63
  });
64
+ ExternallyModified = new import_pui_app_bridge.Event({
65
+ name: "externallyModified",
66
+ objectId: "loan"
67
+ });
64
68
  FieldChangeSync = new import_pui_app_bridge.Event({
65
69
  name: "fieldChangeSync",
66
70
  objectId: "loan"
@@ -0,0 +1,27 @@
1
+ import { getAppConfigValue } from "../app-config/config.js";
2
+ import { browserHistory } from "../history.js";
3
+ import { CMicroAppGuest } from "./guest.js";
4
+ const resolveGuestOpenHistory = (explicitHistory) => {
5
+ const hostUsesParentHistory = getAppConfigValue(
6
+ "useParentHistory",
7
+ true
8
+ );
9
+ if (hostUsesParentHistory && typeof window !== "undefined" && window.self !== window.top && CMicroAppGuest.isInitialized()) {
10
+ return CMicroAppGuest.getInstance().getProps().history;
11
+ }
12
+ return explicitHistory ?? browserHistory;
13
+ };
14
+ const resolveNestedGuestFrameOptions = (userFrameOptions) => {
15
+ if (typeof window === "undefined" || window.self === window.top) {
16
+ return userFrameOptions;
17
+ }
18
+ const versionSegment = window.location.hostname === "localhost" ? "latest" : window.emui?.version?.match(/^(\d+\.\d+)/)?.[1] ?? "latest";
19
+ return {
20
+ src: `${window.location.origin}/${versionSegment}/frame.html`,
21
+ ...userFrameOptions
22
+ };
23
+ };
24
+ export {
25
+ resolveGuestOpenHistory,
26
+ resolveNestedGuestFrameOptions
27
+ };
@@ -50,6 +50,7 @@ class CMicroAppGuest {
50
50
  */
51
51
  #useV2Guest;
52
52
  #selfInitialize;
53
+ #embeddedWithoutHost;
53
54
  /**
54
55
  * AppBridge instance
55
56
  */
@@ -64,7 +65,7 @@ class CMicroAppGuest {
64
65
  onMount;
65
66
  onUnmount;
66
67
  onGetRef;
67
- // eslint-disable-next-line max-statements
68
+ // eslint-disable-next-line max-statements, complexity
68
69
  constructor(params) {
69
70
  window.emui.uuid = window.emui.uuid || uuidv4();
70
71
  this.appId = window.emui?.appId || getAppConfigValue("appId");
@@ -85,10 +86,16 @@ class CMicroAppGuest {
85
86
  this.#ssfHostRef = getSSFHost();
86
87
  this.#useV2Guest = params?.useV2Guest !== false;
87
88
  this.#selfInitialize = params?.selfInitialize ?? false;
89
+ this.#embeddedWithoutHost = params?.embeddedWithoutHost ?? false;
88
90
  this.onInit = params?.onInit;
89
91
  this.onMount = params?.onMount;
90
92
  this.onUnmount = params?.onUnmount;
91
93
  this.onGetRef = params?.onGetRef;
94
+ if (this.#embeddedWithoutHost && !this.#selfInitialize) {
95
+ this.logger.warn(
96
+ "embeddedWithoutHost requires selfInitialize: true \u2014 the flag only applies to initialize()"
97
+ );
98
+ }
92
99
  this.getHost = this.getHost.bind(this);
93
100
  this.getLogger = this.getLogger.bind(this);
94
101
  this.getProps = this.getProps.bind(this);
@@ -284,6 +291,10 @@ class CMicroAppGuest {
284
291
  if (window.emui?.__host) {
285
292
  return window.emui.__host;
286
293
  }
294
+ if (!isCrossDomain()) {
295
+ const legacyHost = window.parent?.emui?.MicroAppHost ?? null;
296
+ if (legacyHost) return legacyHost;
297
+ }
287
298
  if (this.#useV2Guest) {
288
299
  const ssfGuest = new SSFGuest({ appLogger: this.logger });
289
300
  const failed = await abortPromise({
@@ -337,6 +348,33 @@ class CMicroAppGuest {
337
348
  const route = typeof params.initialRoute === "string" ? params.initialRoute : params.initialRoute.pathname;
338
349
  if (route) this.props.history.push(route);
339
350
  }
351
+ async #loadConfigAndBridge() {
352
+ await loadAppConfig();
353
+ await this.#initAppBridge();
354
+ }
355
+ #trackAnalyticsAndOnInit() {
356
+ const instanceId = this.getSessionStorageItem("instanceId") || "";
357
+ const userId = this.getSessionStorageItem("userId") || "";
358
+ updateBAEventParameters({ appId: this.appId, instanceId, userId });
359
+ if (this.onInit) this.onInit(this.props);
360
+ }
361
+ #completeWithoutHost(mode) {
362
+ if (mode === "standalone") {
363
+ this.logger.info("Running in standalone mode \u2014 skipping host discovery");
364
+ this.#exposeGetHost();
365
+ this.#resolveHistory();
366
+ } else if (mode === "embeddedWithoutHost") {
367
+ this.logger.info("embeddedWithoutHost enabled \u2014 skipping host discovery");
368
+ this.#exposeGetHost();
369
+ } else {
370
+ this.logger.warn(
371
+ "Unable to discover host. Proceeding with app.config.json values. Host-provided parameters (history, theme, initialRoute) will not be available."
372
+ );
373
+ this.#exposeGetHost();
374
+ }
375
+ this.#trackAnalyticsAndOnInit();
376
+ return { moduleId: this.appId };
377
+ }
340
378
  /**
341
379
  * Guest-initiated lifecycle: discovers the host and applies module
342
380
  * parameters if available. After this resolves the guest is ready to render.
@@ -352,20 +390,19 @@ class CMicroAppGuest {
352
390
  syncWebpackPublicPathFromAssetPath();
353
391
  const isStandalone = window.top === window.self && !window.opener;
354
392
  if (isStandalone) {
355
- await loadAppConfig();
356
- await this.#initAppBridge();
357
- this.logger.info("Running in standalone mode \u2014 skipping host discovery");
358
- if (this.onInit) this.onInit(this.props);
359
- return { moduleId: this.appId };
393
+ await this.#loadConfigAndBridge();
394
+ return this.#completeWithoutHost("standalone");
395
+ }
396
+ if (this.#embeddedWithoutHost) {
397
+ await this.#loadConfigAndBridge();
398
+ return this.#completeWithoutHost("embeddedWithoutHost");
360
399
  }
361
400
  const [host] = await Promise.all([
362
401
  this.#discoverHost(),
363
- loadAppConfig().then(() => this.#initAppBridge())
402
+ this.#loadConfigAndBridge()
364
403
  ]);
365
404
  if (!host) {
366
- throw new Error(
367
- "Unable to discover host. Ensure the host uses an updated pui-app-sdk and pui-microfe, or use the host-initiated model (onInit/onMount)."
368
- );
405
+ return this.#completeWithoutHost("discoveryFallback");
369
406
  }
370
407
  this.props.host = host;
371
408
  this.#exposeGetHost();
@@ -385,10 +422,7 @@ class CMicroAppGuest {
385
422
  `Failed to retrieve module parameters for "${this.appId}". Proceeding without host-provided parameters.`
386
423
  );
387
424
  }
388
- const instanceId = this.getSessionStorageItem("instanceId") || "";
389
- const userId = this.getSessionStorageItem("userId") || "";
390
- updateBAEventParameters({ appId: this.appId, instanceId, userId });
391
- if (this.onInit) this.onInit(this.props);
425
+ this.#trackAnalyticsAndOnInit();
392
426
  return params;
393
427
  }
394
428
  async init(options) {
@@ -1,5 +1,6 @@
1
1
  import { ScriptingObject, Event } from "@elliemae/ssf-host";
2
2
  import { getMicroFrontEndAppConfig } from "./index.js";
3
+ import { resolveGuestOpenHistory } from "./guest-host-context.js";
3
4
  import { APP_CONTAINER_ID_PREFIX } from "../../view/micro-app/const.js";
4
5
  const LOG_LEVEL_METHOD = {
5
6
  DEBUG: "debug",
@@ -59,7 +60,10 @@ class GuestModule extends ScriptingObject {
59
60
  };
60
61
  }
61
62
  const createGuestModule = (guestId, params = {}, overrides = {}) => {
62
- const mod = new GuestModule(guestId, params);
63
+ const mod = new GuestModule(guestId, {
64
+ ...params,
65
+ history: resolveGuestOpenHistory(params.history)
66
+ });
63
67
  Object.assign(mod, overrides);
64
68
  return mod;
65
69
  };
@@ -0,0 +1,7 @@
1
+ import { CMicroAppGuest } from "../guest.js";
2
+ const resetCMicroAppGuestInstance = () => {
3
+ Reflect.set(CMicroAppGuest, "instance", null);
4
+ };
5
+ export {
6
+ resetCMicroAppGuestInstance
7
+ };
@@ -13,6 +13,10 @@ import {
13
13
  getLogger,
14
14
  getMicroFrontEndAppConfig
15
15
  } from "../utils/micro-frontend/index.js";
16
+ import {
17
+ resolveGuestOpenHistory,
18
+ resolveNestedGuestFrameOptions
19
+ } from "../utils/micro-frontend/guest-host-context.js";
16
20
  const Div = styled.div`
17
21
  display: flex;
18
22
  width: 100%;
@@ -31,13 +35,22 @@ const useAppRenderer = (props) => {
31
35
  onUnloadComplete,
32
36
  containerId,
33
37
  ssfAppOptions,
34
- disableWaitMessage = false
38
+ disableWaitMessage = false,
39
+ frameOptions: userFrameOptions
35
40
  } = props;
36
41
  const dispatch = useDispatch();
37
42
  const stableSsfAppOptions = useMemo(
38
43
  () => ssfAppOptions ?? {},
39
44
  [ssfAppOptions]
40
45
  );
46
+ const guestHistory = useMemo(
47
+ () => resolveGuestOpenHistory(history),
48
+ [history]
49
+ );
50
+ const resolvedFrameOptions = useMemo(
51
+ () => resolveNestedGuestFrameOptions(userFrameOptions),
52
+ [userFrameOptions]
53
+ );
41
54
  const getConfig = useCallback(
42
55
  () => getMicroFrontEndAppConfig({
43
56
  id
@@ -113,11 +126,12 @@ const useAppRenderer = (props) => {
113
126
  id,
114
127
  selfInitialize: configSelfInitialize,
115
128
  frameOptions: {
129
+ ...resolvedFrameOptions,
116
130
  containerId,
117
131
  queryParams: buildLogRocketQueryParams(),
118
132
  signal: abortController.signal
119
133
  },
120
- history,
134
+ history: guestHistory,
121
135
  homeRoute,
122
136
  initialRoute
123
137
  });
@@ -185,7 +199,7 @@ const useAppRenderer = (props) => {
185
199
  }, [
186
200
  containerId,
187
201
  dispatch,
188
- history,
202
+ guestHistory,
189
203
  homeRoute,
190
204
  initialRoute,
191
205
  id,
@@ -193,7 +207,8 @@ const useAppRenderer = (props) => {
193
207
  onUnloadComplete,
194
208
  getConfig,
195
209
  stableSsfAppOptions,
196
- disableWaitMessage
210
+ disableWaitMessage,
211
+ resolvedFrameOptions
197
212
  ]);
198
213
  };
199
214
  const GuestMicroApp = memo(
@@ -38,6 +38,10 @@ class Loan extends ScriptingObject {
38
38
  name: "editmodechange",
39
39
  objectId: "loan"
40
40
  });
41
+ ExternallyModified = new Event({
42
+ name: "externallyModified",
43
+ objectId: "loan"
44
+ });
41
45
  FieldChangeSync = new Event({
42
46
  name: "fieldChangeSync",
43
47
  objectId: "loan"
@@ -0,0 +1,40 @@
1
+ import type { History } from 'history';
2
+ type NestedGuestFrameOptions = {
3
+ src?: string;
4
+ };
5
+ /**
6
+ * History object to use when **opening** a child guest via {@link GuestMicroApp}.
7
+ * {@link createGuestModule} applies the same resolution for module scripting
8
+ * objects automatically.
9
+ *
10
+ * This only affects what the **host** passes into app-bridge `openApp`. The
11
+ * **child guest** still decides its final router history during
12
+ * {@link CMicroAppGuest.initialize} from **its own** `app.config.json`
13
+ * `useParentHistory` flag:
14
+ *
15
+ * - `useParentHistory: true` (default) — uses the history received from the host
16
+ * - `useParentHistory: false` — uses independent `memoryHistory` inside the
17
+ * iframe and ignores the host-provided history
18
+ *
19
+ * When this host runs inside a parent iframe and shares parent history itself
20
+ * (`useParentHistory: true`, the default), the exported `browserHistory`
21
+ * singleton tracks the iframe shell URL (e.g. `/latest/frame.html`), not the
22
+ * shared grandparent route (e.g. `/prospects/incomplete-apps`). In that case
23
+ * the history bound during {@link CMicroAppGuest.initialize} is returned
24
+ * automatically so shared-history children receive the correct object.
25
+ *
26
+ * Hosts with `useParentHistory: false` are unchanged: callers keep passing
27
+ * `explicitHistory ?? browserHistory`, and guests that want their own URL space
28
+ * opt out via their own config.
29
+ * @param explicitHistory
30
+ */
31
+ export declare const resolveGuestOpenHistory: (explicitHistory?: History) => History;
32
+ /**
33
+ * Frame options for nested hosts whose bundled frame.html URL targets the
34
+ * guest dev-server origin while JS executes in the parent iframe document.
35
+ * App Bridge rewrites cross-origin frame shells; this supplies an explicit
36
+ * same-origin src when callers pass custom frameOptions.
37
+ * @param userFrameOptions
38
+ */
39
+ export declare const resolveNestedGuestFrameOptions: (userFrameOptions?: NestedGuestFrameOptions) => NestedGuestFrameOptions | undefined;
40
+ export {};
@@ -40,6 +40,12 @@ interface ConstructorParams {
40
40
  * the bridge will not call these methods.
41
41
  */
42
42
  selfInitialize?: boolean;
43
+ /**
44
+ * When true, the guest is embedded in an iframe but not hosted by PUI app
45
+ * bridge or SSF. Skips host discovery and initializes from app.config.json
46
+ * with in-memory history. Ignored when running standalone (top-level window).
47
+ */
48
+ embeddedWithoutHost?: boolean;
43
49
  }
44
50
  /**
45
51
  * Micro-frontend guest application class (singleton).
@@ -73,7 +73,9 @@ export type ModuleOverrides = {
73
73
  *
74
74
  * Internally calls {@link buildModuleParams} to merge the app config
75
75
  * values (hostUrl, manifestPath, homeRoute) with host-provided values
76
- * (theme, history, initialRoute).
76
+ * (theme, history, initialRoute). When nested under a parent iframe,
77
+ * {@link resolveGuestOpenHistory} is applied to `params.history` automatically
78
+ * (same behavior as {@link GuestMicroApp}).
77
79
  *
78
80
  * Register the returned object via {@link CMicroApp.addScriptingObject}
79
81
  * (not directly on the app bridge) so it is added to both the App Bridge
@@ -0,0 +1,2 @@
1
+ /** Clears the guest singleton between tests. */
2
+ export declare const resetCMicroAppGuestInstance: () => void;
@@ -37,6 +37,10 @@ export type GuestMicroAppParams = OpenAppParams & {
37
37
  * this component, and remove it on teardown. Host-provided `theme` and
38
38
  * `history` for self-initializing guests belong on that module SO — not on
39
39
  * {@link GuestMicroApp} / {@link OpenAppParams}.
40
+ *
41
+ * When this app is itself a nested guest (running inside a parent iframe),
42
+ * {@link GuestMicroApp} automatically forwards the shared grandparent history
43
+ * to child guests — callers do not need to pass `history` explicitly.
40
44
  * @param props - The props for the MicroApp component.
41
45
  */
42
46
  export declare const GuestMicroApp: import("react").NamedExoticComponent<Omit<GuestMicroAppParams, "containerId">>;
@@ -8,6 +8,7 @@ export declare class Loan extends ScriptingObject implements ILoan {
8
8
  readonly Change: Event<Events>;
9
9
  readonly Committed: Event<Events>;
10
10
  readonly EditModeChange: Event<Events>;
11
+ readonly ExternallyModified: Event<Events>;
11
12
  readonly FieldChangeSync: Event<Events>;
12
13
  readonly MilestoneCompleted: Event<Events>;
13
14
  readonly Open: Event<Events>;