@elliemae/pui-app-sdk 5.44.0 → 5.44.2

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 (200) hide show
  1. package/build/docs/404.html +1 -1
  2. package/build/docs/api/classes/Analytics/index.html +1 -1
  3. package/build/docs/api/classes/CMicroAppGuest/index.html +1 -1
  4. package/build/docs/api/classes/CMicroAppHost/index.html +1 -1
  5. package/build/docs/api/classes/ErrorBoundary/index.html +1 -1
  6. package/build/docs/api/classes/GuestModule/index.html +1 -1
  7. package/build/docs/api/functions/AppRoot/index.html +1 -1
  8. package/build/docs/api/functions/Autocomplete/index.html +1 -1
  9. package/build/docs/api/functions/CheckBox/index.html +1 -1
  10. package/build/docs/api/functions/ComboBox/index.html +1 -1
  11. package/build/docs/api/functions/ComboBoxV3/index.html +1 -1
  12. package/build/docs/api/functions/ConnectForm/index.html +1 -1
  13. package/build/docs/api/functions/DateInput/index.html +1 -1
  14. package/build/docs/api/functions/DatePicker/index.html +1 -1
  15. package/build/docs/api/functions/DateRangePicker/index.html +1 -1
  16. package/build/docs/api/functions/DateTimePicker/index.html +1 -1
  17. package/build/docs/api/functions/Form/index.html +1 -1
  18. package/build/docs/api/functions/FormItemLayout/index.html +1 -1
  19. package/build/docs/api/functions/FormLayoutBlockItem/index.html +1 -1
  20. package/build/docs/api/functions/FormSubmitButton/index.html +1 -1
  21. package/build/docs/api/functions/InputMask/index.html +1 -1
  22. package/build/docs/api/functions/InputText/index.html +1 -1
  23. package/build/docs/api/functions/LargeTextBox/index.html +1 -1
  24. package/build/docs/api/functions/MaskedInputText/index.html +1 -1
  25. package/build/docs/api/functions/Page/index.html +1 -1
  26. package/build/docs/api/functions/Radio/index.html +1 -1
  27. package/build/docs/api/functions/RadioGroup/index.html +1 -1
  28. package/build/docs/api/functions/RegisterService/index.html +1 -1
  29. package/build/docs/api/functions/RenderWithStateAddOns/index.html +1 -1
  30. package/build/docs/api/functions/RequireAuth/index.html +1 -1
  31. package/build/docs/api/functions/SessionTimeout/index.html +1 -1
  32. package/build/docs/api/functions/TextBox/index.html +1 -1
  33. package/build/docs/api/functions/Toggle/index.html +1 -1
  34. package/build/docs/api/functions/authSaga/index.html +1 -1
  35. package/build/docs/api/functions/authorize/index.html +1 -1
  36. package/build/docs/api/functions/buildModuleParams/index.html +1 -1
  37. package/build/docs/api/functions/cleanup/index.html +1 -1
  38. package/build/docs/api/functions/configureStore/index.html +1 -1
  39. package/build/docs/api/functions/createGuestModule/index.html +1 -1
  40. package/build/docs/api/functions/createManager/index.html +1 -1
  41. package/build/docs/api/functions/enableReactAppForHostIntegration/index.html +1 -1
  42. package/build/docs/api/functions/fetchHostAppData/index.html +1 -1
  43. package/build/docs/api/functions/fetchUserSettings/index.html +1 -1
  44. package/build/docs/api/functions/getApiActionCreator/index.html +1 -1
  45. package/build/docs/api/functions/getAppConfig/index.html +1 -1
  46. package/build/docs/api/functions/getAppConfigValue/index.html +1 -1
  47. package/build/docs/api/functions/getAuthHTTPClient/index.html +1 -1
  48. package/build/docs/api/functions/getAuthorizationHeader/index.html +1 -1
  49. package/build/docs/api/functions/getHTTPClient/index.html +1 -1
  50. package/build/docs/api/functions/getHostAppDataByKey/index.html +1 -1
  51. package/build/docs/api/functions/getLogger/index.html +1 -1
  52. package/build/docs/api/functions/getMicroFrontEndAppConfig/index.html +1 -1
  53. package/build/docs/api/functions/getNavigationLinks/index.html +1 -1
  54. package/build/docs/api/functions/getRedirectUrl/index.html +1 -1
  55. package/build/docs/api/functions/getSelectField/index.html +1 -1
  56. package/build/docs/api/functions/getStore/index.html +1 -1
  57. package/build/docs/api/functions/initServiceWorker/index.html +1 -1
  58. package/build/docs/api/functions/isCIBuild/index.html +1 -1
  59. package/build/docs/api/functions/isProd/index.html +1 -1
  60. package/build/docs/api/functions/isUserAuthorized/index.html +1 -1
  61. package/build/docs/api/functions/listenStorageEvents/index.html +1 -1
  62. package/build/docs/api/functions/loadAppConfig/index.html +1 -1
  63. package/build/docs/api/functions/loadable/index.html +1 -1
  64. package/build/docs/api/functions/login/index.html +1 -1
  65. package/build/docs/api/functions/notifyGuestUnloadComplete/index.html +1 -1
  66. package/build/docs/api/functions/onAuthorizationFailure/index.html +1 -1
  67. package/build/docs/api/functions/onGuestUnloadStart/index.html +1 -1
  68. package/build/docs/api/functions/redactPii/index.html +1 -1
  69. package/build/docs/api/functions/removeDoubleSlash/index.html +1 -1
  70. package/build/docs/api/functions/removeStorageEvents/index.html +1 -1
  71. package/build/docs/api/functions/render/index.html +1 -1
  72. package/build/docs/api/functions/renderHook/index.html +1 -1
  73. package/build/docs/api/functions/renderWithHostData/index.html +1 -1
  74. package/build/docs/api/functions/renderWithRedux/index.html +1 -1
  75. package/build/docs/api/functions/renderWithRouter/index.html +1 -1
  76. package/build/docs/api/functions/renderWithRouterRedux/index.html +1 -1
  77. package/build/docs/api/functions/sdkBaseQuery/index.html +1 -1
  78. package/build/docs/api/functions/sendBAEvent/index.html +1 -1
  79. package/build/docs/api/functions/sendMessageToHost/index.html +1 -1
  80. package/build/docs/api/functions/setAppConfig/index.html +1 -1
  81. package/build/docs/api/functions/setAppConfigValue/index.html +1 -1
  82. package/build/docs/api/functions/setHostAppData/index.html +1 -1
  83. package/build/docs/api/functions/setLoginParams/index.html +1 -1
  84. package/build/docs/api/functions/subscribeToResetSession/index.html +1 -1
  85. package/build/docs/api/functions/subscribeToSessionExpiry/index.html +1 -1
  86. package/build/docs/api/functions/subscribeToSessionExpiryWarning/index.html +1 -1
  87. package/build/docs/api/functions/trackActivity/index.html +1 -1
  88. package/build/docs/api/functions/useAppDispatch/index.html +1 -1
  89. package/build/docs/api/functions/useAppMiddleware/index.html +1 -1
  90. package/build/docs/api/functions/useInjectQuery/index.html +1 -1
  91. package/build/docs/api/functions/useInjectReducer/index.html +1 -1
  92. package/build/docs/api/functions/useInjectSaga/index.html +1 -1
  93. package/build/docs/api/functions/useInjectSideEffect/index.html +1 -1
  94. package/build/docs/api/functions/useMediaQueryList/index.html +1 -1
  95. package/build/docs/api/functions/useStateSelector/index.html +1 -1
  96. package/build/docs/api/functions/useStateSelectorShallow/index.html +1 -1
  97. package/build/docs/api/functions/waitFor/index.html +1 -1
  98. package/build/docs/api/functions/waitForElementToBeRemoved/index.html +1 -1
  99. package/build/docs/api/functions/withAppDecorator/index.html +1 -1
  100. package/build/docs/api/index.html +1 -1
  101. package/build/docs/api/interfaces/AppConfig/index.html +1 -1
  102. package/build/docs/api/interfaces/GuestProps/index.html +1 -1
  103. package/build/docs/api/interfaces/UseStateSelectorOptions/index.html +1 -1
  104. package/build/docs/api/type-aliases/AppDispatch/index.html +1 -1
  105. package/build/docs/api/type-aliases/AppStore/index.html +1 -1
  106. package/build/docs/api/type-aliases/Await/index.html +1 -1
  107. package/build/docs/api/type-aliases/EMUI/index.html +1 -1
  108. package/build/docs/api/type-aliases/HostOptions/index.html +1 -1
  109. package/build/docs/api/type-aliases/HostProvidedParams/index.html +1 -1
  110. package/build/docs/api/type-aliases/ModuleOverrides/index.html +1 -1
  111. package/build/docs/api/type-aliases/OnHostInitCallback/index.html +1 -1
  112. package/build/docs/api/type-aliases/OnInitCallback/index.html +1 -1
  113. package/build/docs/api/type-aliases/OnMountCallback/index.html +1 -1
  114. package/build/docs/api/type-aliases/OnUnMountCallback/index.html +1 -1
  115. package/build/docs/api/type-aliases/RootState/index.html +1 -1
  116. package/build/docs/api/type-aliases/SelectStateFieldFunction/index.html +1 -1
  117. package/build/docs/api/variables/ErrorToast/index.html +1 -1
  118. package/build/docs/api/variables/FormPropsContext/index.html +1 -1
  119. package/build/docs/api/variables/GuestMicroApp/index.html +1 -1
  120. package/build/docs/api/variables/MASK_PIPES/index.html +1 -1
  121. package/build/docs/api/variables/MASK_TYPES/index.html +1 -1
  122. package/build/docs/api/variables/MicroApp/index.html +1 -1
  123. package/build/docs/api/variables/MicroIFrameApp/index.html +1 -1
  124. package/build/docs/api/variables/NavigationPrompt/index.html +1 -1
  125. package/build/docs/api/variables/NavigationPromptActions/index.html +1 -1
  126. package/build/docs/api/variables/Themes/index.html +1 -1
  127. package/build/docs/api/variables/VisuallyHidden/index.html +1 -1
  128. package/build/docs/api/variables/WaitMessage/index.html +1 -1
  129. package/build/docs/api/variables/act/index.html +1 -1
  130. package/build/docs/api/variables/ariaLive/index.html +1 -1
  131. package/build/docs/api/variables/auth/index.html +1 -1
  132. package/build/docs/api/variables/authReducer/index.html +1 -1
  133. package/build/docs/api/variables/clearSideEffects/index.html +1 -1
  134. package/build/docs/api/variables/createSideEffect/index.html +1 -1
  135. package/build/docs/api/variables/decorators/index.html +1 -1
  136. package/build/docs/api/variables/endSession/index.html +1 -1
  137. package/build/docs/api/variables/error/index.html +1 -1
  138. package/build/docs/api/variables/errorMiddleware/index.html +1 -1
  139. package/build/docs/api/variables/fireEvent/index.html +1 -1
  140. package/build/docs/api/variables/globalConstants/index.html +1 -1
  141. package/build/docs/api/variables/history/index.html +1 -1
  142. package/build/docs/api/variables/httpClientProps/index.html +1 -1
  143. package/build/docs/api/variables/logout/index.html +1 -1
  144. package/build/docs/api/variables/memoryHistory/index.html +1 -1
  145. package/build/docs/api/variables/removeSideEffect/index.html +1 -1
  146. package/build/docs/api/variables/resetUserIdleTime/index.html +1 -1
  147. package/build/docs/api/variables/screen/index.html +1 -1
  148. package/build/docs/api/variables/startSideEffect/index.html +1 -1
  149. package/build/docs/api/variables/updateBAEventParameters/index.html +1 -1
  150. package/build/docs/api/variables/useAppSelector/index.html +1 -1
  151. package/build/docs/api/variables/waitMessageAction/index.html +1 -1
  152. package/build/docs/api/variables/within/index.html +1 -1
  153. package/build/docs/assets/js/{main.f6a3322e.js → main.1bebbb71.js} +2 -2
  154. package/build/docs/index.html +1 -1
  155. package/build/docs/usage-guide/index.html +1 -1
  156. package/demo/{docs/1896.1c742108.iframe.bundle.js → 1896.619ce620.iframe.bundle.js} +1 -1
  157. package/demo/1896.619ce620.iframe.bundle.js.br +0 -0
  158. package/demo/{docs/1896.1c742108.iframe.bundle.js.gz → 1896.619ce620.iframe.bundle.js.gz} +0 -0
  159. package/demo/{1896.1c742108.iframe.bundle.js → docs/1896.619ce620.iframe.bundle.js} +1 -1
  160. package/demo/docs/1896.619ce620.iframe.bundle.js.br +0 -0
  161. package/demo/{1896.1c742108.iframe.bundle.js.gz → docs/1896.619ce620.iframe.bundle.js.gz} +0 -0
  162. package/demo/docs/iframe.html +1 -1
  163. package/demo/docs/index.html +1 -1
  164. package/demo/docs/{main.8e67bf2a43b910cc0a1f.manager.bundle.js → main.a6b1ae4b10ba95f2bb8e.manager.bundle.js} +1 -1
  165. package/demo/docs/{main.8e67bf2a43b910cc0a1f.manager.bundle.js.br → main.a6b1ae4b10ba95f2bb8e.manager.bundle.js.br} +0 -0
  166. package/demo/docs/{main.8e67bf2a43b910cc0a1f.manager.bundle.js.gz → main.a6b1ae4b10ba95f2bb8e.manager.bundle.js.gz} +0 -0
  167. package/demo/docs/main.b368484e.iframe.bundle.js +5 -0
  168. package/demo/docs/main.b368484e.iframe.bundle.js.br +0 -0
  169. package/demo/docs/main.b368484e.iframe.bundle.js.gz +0 -0
  170. package/demo/docs/project.json +1 -1
  171. package/demo/iframe.html +1 -1
  172. package/demo/index.html +1 -1
  173. package/demo/{main.8e67bf2a43b910cc0a1f.manager.bundle.js → main.a6b1ae4b10ba95f2bb8e.manager.bundle.js} +1 -1
  174. package/demo/{main.8e67bf2a43b910cc0a1f.manager.bundle.js.br → main.a6b1ae4b10ba95f2bb8e.manager.bundle.js.br} +0 -0
  175. package/demo/{main.8e67bf2a43b910cc0a1f.manager.bundle.js.gz → main.a6b1ae4b10ba95f2bb8e.manager.bundle.js.gz} +0 -0
  176. package/demo/main.b368484e.iframe.bundle.js +5 -0
  177. package/demo/main.b368484e.iframe.bundle.js.br +0 -0
  178. package/demo/main.b368484e.iframe.bundle.js.gz +0 -0
  179. package/demo/project.json +1 -1
  180. package/dist/cjs/utils/micro-frontend/guest.js +39 -36
  181. package/dist/cjs/view/guest-microapp.js +40 -17
  182. package/dist/cjs/view/iframe-teardown-sandbox.js +50 -0
  183. package/dist/cjs/view/micro-app/use-app-will-render.js +24 -3
  184. package/dist/esm/utils/micro-frontend/guest.js +39 -36
  185. package/dist/esm/view/guest-microapp.js +43 -17
  186. package/dist/esm/view/iframe-teardown-sandbox.js +30 -0
  187. package/dist/esm/view/micro-app/use-app-will-render.js +24 -3
  188. package/dist/types/lib/view/iframe-teardown-sandbox.d.ts +20 -0
  189. package/dist/types/lib/view/tests/iframe-teardown-sandbox.test.d.ts +1 -0
  190. package/dist/types/tsconfig.tsbuildinfo +1 -1
  191. package/package.json +1 -1
  192. package/demo/1896.1c742108.iframe.bundle.js.br +0 -0
  193. package/demo/docs/1896.1c742108.iframe.bundle.js.br +0 -0
  194. package/demo/docs/main.643b2148.iframe.bundle.js +0 -5
  195. package/demo/docs/main.643b2148.iframe.bundle.js.br +0 -0
  196. package/demo/docs/main.643b2148.iframe.bundle.js.gz +0 -0
  197. package/demo/main.643b2148.iframe.bundle.js +0 -5
  198. package/demo/main.643b2148.iframe.bundle.js.br +0 -0
  199. package/demo/main.643b2148.iframe.bundle.js.gz +0 -0
  200. /package/build/docs/assets/js/{main.f6a3322e.js.LICENSE.txt → main.1bebbb71.js.LICENSE.txt} +0 -0
@@ -16,6 +16,7 @@ import { getSSFHost, setSSFHost } from "./ssf-host.js";
16
16
  import { syncWebpackPublicPathFromAssetPath } from "./webpack-public-path.js";
17
17
  const DEFAULT_CONTAINER_ID = "app-container";
18
18
  const MODULE_SCRIPTING_OBJECT_TIMEOUT_MS = 2e3;
19
+ const SSF_CONNECT_TIMEOUT_MS = 1e3;
19
20
  const isCrossDomain = () => {
20
21
  try {
21
22
  window.parent.document;
@@ -278,22 +279,31 @@ class CMicroAppGuest {
278
279
  }
279
280
  return value;
280
281
  }
281
- async findHost() {
282
- let host = null;
283
- if (!isCrossDomain()) {
284
- host = window.parent?.emui?.MicroAppHost ?? null;
285
- }
286
- if (!host && this.#useV2Guest) {
287
- host = new SSFGuest({ appLogger: this.logger });
288
- if (await abortPromise({
289
- promise: host.connect(),
290
- delay: 1e3,
291
- retValue: false
292
- })) {
293
- host = null;
294
- } else this.#communicationMethod = "ssf" /* SSF */;
282
+ #getLegacyParentHost() {
283
+ if (isCrossDomain()) return null;
284
+ return window.parent?.emui?.MicroAppHost ?? null;
285
+ }
286
+ async #tryConnectSSFGuest() {
287
+ const ssfGuest = new SSFGuest({ appLogger: this.logger });
288
+ try {
289
+ const failed = await abortPromise({
290
+ promise: ssfGuest.connect(),
291
+ delay: SSF_CONNECT_TIMEOUT_MS,
292
+ retValue: true
293
+ });
294
+ if (!failed) {
295
+ this.#communicationMethod = "ssf" /* SSF */;
296
+ return ssfGuest;
297
+ }
298
+ } catch {
295
299
  }
296
- return host;
300
+ return null;
301
+ }
302
+ async findHost() {
303
+ const legacyHost = this.#getLegacyParentHost();
304
+ if (legacyHost) return legacyHost;
305
+ if (!this.#useV2Guest) return null;
306
+ return this.#tryConnectSSFGuest();
297
307
  }
298
308
  get uuid() {
299
309
  return this.#uuid;
@@ -329,23 +339,10 @@ class CMicroAppGuest {
329
339
  if (window.emui?.__host) {
330
340
  return window.emui.__host;
331
341
  }
332
- if (!isCrossDomain()) {
333
- const legacyHost = window.parent?.emui?.MicroAppHost ?? null;
334
- if (legacyHost) return legacyHost;
335
- }
336
- if (this.#useV2Guest) {
337
- const ssfGuest = new SSFGuest({ appLogger: this.logger });
338
- const failed = await abortPromise({
339
- promise: ssfGuest.connect(),
340
- delay: 1e3,
341
- retValue: true
342
- });
343
- if (!failed) {
344
- this.#communicationMethod = "ssf" /* SSF */;
345
- return ssfGuest;
346
- }
347
- }
348
- return null;
342
+ const legacyHost = this.#getLegacyParentHost();
343
+ if (legacyHost) return legacyHost;
344
+ if (!this.#useV2Guest) return null;
345
+ return this.#tryConnectSSFGuest();
349
346
  }
350
347
  #exposeGetHost() {
351
348
  window.emui = window.emui || {};
@@ -424,10 +421,16 @@ class CMicroAppGuest {
424
421
  */
425
422
  #bindModuleUnmountListener() {
426
423
  if (this.#moduleUnmountSubToken || !this.props.host) return;
427
- this.#moduleUnmountSubToken = this.subscribe({
428
- eventId: MODULE_UNMOUNT_EVENT_ID,
429
- callback: this.#handleModuleUnmount
430
- });
424
+ try {
425
+ this.#moduleUnmountSubToken = this.subscribe({
426
+ eventId: MODULE_UNMOUNT_EVENT_ID,
427
+ callback: this.#handleModuleUnmount
428
+ });
429
+ } catch {
430
+ this.logger.warn(
431
+ "Unable to subscribe to module.unmount from host \u2014 continuing without host teardown listener"
432
+ );
433
+ }
431
434
  }
432
435
  #unbindModuleUnmountListener() {
433
436
  if (!this.#moduleUnmountSubToken) return;
@@ -17,6 +17,10 @@ import {
17
17
  resolveGuestOpenHistory,
18
18
  resolveNestedGuestFrameOptions
19
19
  } from "../utils/micro-frontend/guest-host-context.js";
20
+ import {
21
+ getSsfGuestFrame,
22
+ preserveIframeForTeardown
23
+ } from "./iframe-teardown-sandbox.js";
20
24
  const Div = styled.div`
21
25
  display: flex;
22
26
  width: 100%;
@@ -59,11 +63,14 @@ const useAppRenderer = (props) => {
59
63
  [id]
60
64
  );
61
65
  const unloadInProgressRef = useRef(Promise.resolve());
66
+ const hostStateRef = useRef({ appBridge: null, ssfHost: null, instanceId: null });
62
67
  useLayoutEffect(() => {
63
68
  let isMounted = true;
64
- let appBridge = null;
65
- let ssfHost = null;
66
- let instanceId = null;
69
+ hostStateRef.current = {
70
+ appBridge: null,
71
+ ssfHost: null,
72
+ instanceId: null
73
+ };
67
74
  const abortController = new AbortController();
68
75
  const trackUnload = (unloadResult, onSettled) => {
69
76
  unloadInProgressRef.current = unloadInProgressRef.current.then(() => toVoidPromise(unloadResult)).then(() => {
@@ -76,7 +83,7 @@ const useAppRenderer = (props) => {
76
83
  });
77
84
  };
78
85
  const notifyUnloadComplete = () => {
79
- instanceId = null;
86
+ hostStateRef.current.instanceId = null;
80
87
  setTimeout(() => {
81
88
  try {
82
89
  onUnloadComplete?.();
@@ -108,7 +115,8 @@ const useAppRenderer = (props) => {
108
115
  ...stableSsfAppOptions
109
116
  };
110
117
  if (microappManager === MicroAppManager.SSF) {
111
- ssfHost = getSSFHost();
118
+ const ssfHost = getSSFHost();
119
+ hostStateRef.current.ssfHost = ssfHost;
112
120
  if (!ssfHost)
113
121
  throw new Error(`Failed to load ${id}, SSFHost not initialized`);
114
122
  if (!hostUrl)
@@ -139,14 +147,15 @@ const useAppRenderer = (props) => {
139
147
  onLoad: onLoadComplete,
140
148
  options: guestOptions
141
149
  });
142
- instanceId = guest.id;
150
+ hostStateRef.current.instanceId = guest.id;
143
151
  }
144
152
  } else {
145
- appBridge = await getAppBridge();
153
+ const appBridge = await getAppBridge();
154
+ hostStateRef.current.appBridge = appBridge;
146
155
  if (!appBridge)
147
156
  throw new Error(`Failed to load ${id}, AppBridge not initialized`);
148
157
  if (isMounted && !abortController.signal.aborted) {
149
- instanceId = await appBridge.openApp({
158
+ const openedInstanceId = await appBridge.openApp({
150
159
  id,
151
160
  selfInitialize: configSelfInitialize,
152
161
  frameOptions: {
@@ -160,13 +169,14 @@ const useAppRenderer = (props) => {
160
169
  initialRoute
161
170
  });
162
171
  if (!isMounted || abortController.signal.aborted) {
163
- trackUnload(appBridge.closeApp(instanceId));
164
- instanceId = null;
172
+ trackUnload(appBridge.closeApp(openedInstanceId));
173
+ hostStateRef.current.instanceId = null;
165
174
  return;
166
175
  }
176
+ hostStateRef.current.instanceId = openedInstanceId;
167
177
  setTimeout(() => {
168
178
  try {
169
- onLoadComplete?.(instanceId);
179
+ onLoadComplete?.(openedInstanceId);
170
180
  } catch (ex) {
171
181
  getLogger().warn({
172
182
  message: `onLoadComplete callback failed for ${id}`,
@@ -183,6 +193,7 @@ const useAppRenderer = (props) => {
183
193
  exception: ex
184
194
  });
185
195
  isMounted = false;
196
+ const { appBridge, ssfHost, instanceId } = hostStateRef.current;
186
197
  if (appBridge && instanceId)
187
198
  trackUnload(appBridge.closeApp(instanceId));
188
199
  if (ssfHost && instanceId) trackUnload(ssfHost.unloadGuest(instanceId));
@@ -195,12 +206,27 @@ const useAppRenderer = (props) => {
195
206
  return () => {
196
207
  abortController.abort();
197
208
  isMounted = false;
198
- if (ssfHost && instanceId) {
199
- trackUnload(ssfHost.unloadGuest(instanceId), notifyUnloadComplete);
200
- } else if (appBridge && instanceId) {
201
- trackUnload(appBridge.closeApp(instanceId), notifyUnloadComplete);
202
- } else {
203
- notifyUnloadComplete();
209
+ const {
210
+ appBridge,
211
+ ssfHost,
212
+ instanceId: currentInstanceId
213
+ } = hostStateRef.current;
214
+ const startTeardown = (frameEle, unloadResult) => {
215
+ preserveIframeForTeardown(frameEle ?? void 0);
216
+ trackUnload(unloadResult, notifyUnloadComplete);
217
+ };
218
+ if (ssfHost && currentInstanceId) {
219
+ startTeardown(
220
+ getSsfGuestFrame(ssfHost, currentInstanceId),
221
+ ssfHost.unloadGuest(currentInstanceId)
222
+ );
223
+ } else if (appBridge && currentInstanceId) {
224
+ startTeardown(
225
+ document.getElementById(
226
+ currentInstanceId
227
+ ),
228
+ appBridge.closeApp(currentInstanceId)
229
+ );
204
230
  }
205
231
  };
206
232
  }, [
@@ -0,0 +1,30 @@
1
+ const TEARDOWN_SANDBOX_ID = "pui-iframe-teardown-sandbox";
2
+ let disablePreserveForTeardown = false;
3
+ const setDisablePreserveForTeardown = (disabled) => {
4
+ disablePreserveForTeardown = disabled;
5
+ };
6
+ const getTeardownSandbox = () => {
7
+ let sandbox = document.getElementById(TEARDOWN_SANDBOX_ID);
8
+ if (!sandbox) {
9
+ sandbox = document.createElement("div");
10
+ sandbox.id = TEARDOWN_SANDBOX_ID;
11
+ sandbox.setAttribute("data-testid", TEARDOWN_SANDBOX_ID);
12
+ sandbox.style.cssText = "position:fixed;left:-9999px;top:-9999px;width:0;height:0;overflow:hidden;visibility:hidden;";
13
+ document.body.appendChild(sandbox);
14
+ }
15
+ return sandbox;
16
+ };
17
+ const clearTeardownSandbox = () => {
18
+ document.getElementById(TEARDOWN_SANDBOX_ID)?.replaceChildren();
19
+ };
20
+ const preserveIframeForTeardown = (iframe) => {
21
+ if (disablePreserveForTeardown || !iframe?.isConnected) return;
22
+ getTeardownSandbox().appendChild(iframe);
23
+ };
24
+ const getSsfGuestFrame = (ssfHost, instanceId) => ssfHost.getGuests().find(({ id }) => id === instanceId)?.domElement ?? null;
25
+ export {
26
+ clearTeardownSandbox,
27
+ getSsfGuestFrame,
28
+ preserveIframeForTeardown,
29
+ setDisablePreserveForTeardown
30
+ };
@@ -13,6 +13,8 @@ import {
13
13
  getMicroFrontEndAppConfig
14
14
  } from "../../utils/micro-frontend/index.js";
15
15
  import { logRecords } from "../../utils/log-records.js";
16
+ import { preserveIframeForTeardown } from "../iframe-teardown-sandbox.js";
17
+ import { IFRAME_CONTAINER_ID_PREFIX } from "../micro-iframe-app/iframe/const.js";
16
18
  const useAppWillRender = ({
17
19
  id,
18
20
  uuid,
@@ -32,8 +34,12 @@ const useAppWillRender = ({
32
34
  );
33
35
  const unload = useCallback(
34
36
  async (appParams, loadFailed = false) => {
35
- await unmountApp(appParams);
36
- unloadApp(appParams);
37
+ try {
38
+ await unmountApp(appParams);
39
+ unloadApp(appParams);
40
+ } finally {
41
+ document.getElementById(`${IFRAME_CONTAINER_ID_PREFIX}${appParams.id}`)?.remove();
42
+ }
37
43
  if (!loadFailed && onUnloadComplete) onUnloadComplete();
38
44
  },
39
45
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -70,9 +76,24 @@ const useAppWillRender = ({
70
76
  const appParams = { ...appConfig, uuid };
71
77
  load(appParams);
72
78
  return () => {
79
+ preserveIframeForTeardown(
80
+ document.getElementById(
81
+ `${IFRAME_CONTAINER_ID_PREFIX}${id}`
82
+ )
83
+ );
73
84
  ref.current = unload(appParams);
74
85
  };
75
- }, [documentEle, getConfig, history, theme, homeRoute, load, unload, uuid]);
86
+ }, [
87
+ documentEle,
88
+ getConfig,
89
+ history,
90
+ id,
91
+ theme,
92
+ homeRoute,
93
+ load,
94
+ unload,
95
+ uuid
96
+ ]);
76
97
  };
77
98
  export {
78
99
  useAppWillRender
@@ -0,0 +1,20 @@
1
+ export declare const setDisablePreserveForTeardown: (disabled: boolean) => void;
2
+ export declare const clearTeardownSandbox: () => void;
3
+ /**
4
+ * Re-parent a guest iframe so its JS context stays alive while async host
5
+ * teardown (closeApp / unloadGuest) completes.
6
+ * @param iframe
7
+ */
8
+ export declare const preserveIframeForTeardown: (iframe: HTMLElement | null | undefined) => void;
9
+ /**
10
+ * Resolve the SSF guest iframe from the host registry.
11
+ * @param ssfHost
12
+ * @param ssfHost.getGuests
13
+ * @param instanceId
14
+ */
15
+ export declare const getSsfGuestFrame: (ssfHost: {
16
+ getGuests: () => Array<{
17
+ id: string;
18
+ domElement?: HTMLIFrameElement | null;
19
+ }>;
20
+ }, instanceId: string) => HTMLIFrameElement | null;