@elliemae/ssf-host 2.23.6 → 2.24.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 (91) hide show
  1. package/dist/cjs/callchain-host.html +262 -0
  2. package/dist/cjs/callchain-intermediate.html +92 -0
  3. package/dist/cjs/e2e-host.html +603 -0
  4. package/dist/cjs/e2e-index.html +234 -0
  5. package/dist/cjs/host.js +93 -36
  6. package/dist/cjs/index.html +304 -151
  7. package/dist/cjs/popup-focus-host.html +318 -0
  8. package/dist/cjs/utils.js +14 -1
  9. package/dist/cjs/v2-host-v1-guest.html +3 -0
  10. package/dist/esm/callchain-host.html +262 -0
  11. package/dist/esm/callchain-intermediate.html +92 -0
  12. package/dist/esm/e2e-host.html +603 -0
  13. package/dist/esm/e2e-index.html +234 -0
  14. package/dist/esm/host.js +94 -37
  15. package/dist/esm/index.html +304 -151
  16. package/dist/esm/popup-focus-host.html +318 -0
  17. package/dist/esm/utils.js +14 -1
  18. package/dist/esm/v2-host-v1-guest.html +3 -0
  19. package/dist/public/callchain-host.html +34 -0
  20. package/dist/public/callchain-host.js +3 -0
  21. package/dist/public/callchain-host.js.br +0 -0
  22. package/dist/public/callchain-host.js.gz +0 -0
  23. package/dist/public/callchain-host.js.map +1 -0
  24. package/dist/public/callchain-intermediate.html +1 -0
  25. package/dist/public/e2e-host.html +5 -0
  26. package/dist/public/e2e-host.js +8 -0
  27. package/dist/public/e2e-host.js.br +0 -0
  28. package/dist/public/e2e-host.js.gz +0 -0
  29. package/dist/public/e2e-host.js.map +1 -0
  30. package/dist/public/e2e-index.html +1 -0
  31. package/dist/public/index.html +1 -1
  32. package/dist/public/init.js +1 -1
  33. package/dist/public/init.js.br +0 -0
  34. package/dist/public/init.js.gz +0 -0
  35. package/dist/public/init.js.map +1 -1
  36. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js +3 -0
  37. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.br +0 -0
  38. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.gz +0 -0
  39. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.map +1 -0
  40. package/dist/public/loan-object.js +1 -1
  41. package/dist/public/loan-object.js.br +0 -0
  42. package/dist/public/loan-object.js.gz +0 -0
  43. package/dist/public/loan-object.js.map +1 -1
  44. package/dist/public/popup-focus-host.html +1 -0
  45. package/dist/public/popup-focus-host.js +6 -0
  46. package/dist/public/popup-focus-host.js.br +0 -0
  47. package/dist/public/popup-focus-host.js.gz +0 -0
  48. package/dist/public/popup-focus-host.js.map +1 -0
  49. package/dist/public/v1-guest-v2-host.html +1 -1
  50. package/dist/public/v2-host-v1-guest.html +1 -1
  51. package/dist/types/lib/host.d.ts +1 -0
  52. package/dist/types/lib/ihost.d.ts +15 -0
  53. package/dist/types/lib/tests/timingDedup.test.d.ts +1 -0
  54. package/dist/types/lib/utils.d.ts +1 -0
  55. package/dist/types/tsconfig.tsbuildinfo +1 -1
  56. package/dist/umd/callchain-host.html +34 -0
  57. package/dist/umd/callchain-host.js +3 -0
  58. package/dist/umd/callchain-host.js.br +0 -0
  59. package/dist/umd/callchain-host.js.gz +0 -0
  60. package/dist/umd/callchain-host.js.map +1 -0
  61. package/dist/umd/callchain-intermediate.html +1 -0
  62. package/dist/umd/e2e-host.html +5 -0
  63. package/dist/umd/e2e-host.js +8 -0
  64. package/dist/umd/e2e-host.js.br +0 -0
  65. package/dist/umd/e2e-host.js.gz +0 -0
  66. package/dist/umd/e2e-host.js.map +1 -0
  67. package/dist/umd/e2e-index.html +1 -0
  68. package/dist/umd/index.html +1 -1
  69. package/dist/umd/index.js +1 -1
  70. package/dist/umd/index.js.br +0 -0
  71. package/dist/umd/index.js.gz +0 -0
  72. package/dist/umd/index.js.map +1 -1
  73. package/dist/umd/init.js +1 -1
  74. package/dist/umd/init.js.br +0 -0
  75. package/dist/umd/init.js.gz +0 -0
  76. package/dist/umd/init.js.map +1 -1
  77. package/dist/umd/loan-object.js +1 -1
  78. package/dist/umd/loan-object.js.br +0 -0
  79. package/dist/umd/loan-object.js.gz +0 -0
  80. package/dist/umd/loan-object.js.map +1 -1
  81. package/dist/umd/popup-focus-host.html +1 -0
  82. package/dist/umd/popup-focus-host.js +6 -0
  83. package/dist/umd/popup-focus-host.js.br +0 -0
  84. package/dist/umd/popup-focus-host.js.gz +0 -0
  85. package/dist/umd/popup-focus-host.js.map +1 -0
  86. package/dist/umd/v2-host-v1-guest.html +1 -1
  87. package/package.json +5 -5
  88. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js +0 -3
  89. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js.br +0 -0
  90. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js.gz +0 -0
  91. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js.map +0 -1
@@ -0,0 +1,34 @@
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>CallChain E2E Test - Root Host</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script defer="defer" src="index.js"></script></head><body class="bg-gray-50"><header class="bg-indigo-600 text-white px-4 py-3 flex items-center justify-between"><h1 class="text-lg font-semibold">CallChain E2E Test — Root Host (A)</h1><a href="./index.html" class="text-indigo-200 hover:text-white text-sm">&larr; Back to main</a></header><main class="mx-auto max-w-7xl px-4 py-4"><div class="grid grid-cols-2 gap-4 mb-4"><div class="bg-blue-50 border border-blue-200 rounded-md p-3"><h2 class="text-sm font-bold text-blue-900 mb-2">Architecture</h2><pre class="text-xs text-blue-800 leading-relaxed">
2
+ Root Host (A) [localhost:4000]
3
+ ├─ Exposes: <strong>TestService</strong> scripting object
4
+ ├─ Loads guest <strong>"intermediateHost"</strong>
5
+ │ metadata: { vendor: "Acme Credit Corp", tier: "premium" }
6
+
7
+ └─ Intermediate (B) [localhost:4000, same-origin iframe]
8
+ ├─ Connects as V2 guest to A
9
+ ├─ Loads ice.guest from localhost:4001/index.js
10
+ ├─ Gets TestService proxy, clones it
11
+ ├─ Creates own SSFHost, loads guest <strong>"grandchildGuest"</strong>
12
+ │ metadata: { role: "validator", region: "US-East" }
13
+
14
+ └─ Grandchild (C) [localhost:4001, cross-origin iframe]
15
+ └─ Connects as V2 guest to B
16
+ └─ Gets TestService proxy → calls methods</pre></div><div class="bg-green-50 border border-green-200 rounded-md p-3"><h2 class="text-sm font-bold text-green-900 mb-2">Test Steps</h2><ol class="text-xs text-green-800 space-y-2 list-decimal list-inside"><li><strong>Wait for initialization</strong> — check the Invocation Log shows: <code>"Root Host (A) ready"</code>, Intermediate shows <code>"Intermediate (B) ready"</code>, Grandchild shows <code>"Got TestService proxy"</code>.</li><li><strong>Click any button</strong> inside the Grandchild (C) panel (e.g. "Call getCallerInfo()").</li><li><strong>Verify on the left</strong> — the "callContext.guest" and "callContext.callChain" panels update with the expected values shown below.</li><li><strong>Verify on grandchild</strong> — the return value appears in the grandchild's result area (proves round-trip works).</li></ol></div></div><div class="bg-amber-50 border border-amber-200 rounded-md p-3 mb-4"><h2 class="text-sm font-bold text-amber-900 mb-2">Expected Values (when grandchild C calls any method)</h2><div class="grid grid-cols-2 gap-4 text-xs"><div><h3 class="font-semibold text-amber-800 mb-1">callContext.guest (direct caller = B)</h3><pre data-testid="expected-guest" class="bg-white rounded p-2 text-amber-900">
17
+ {
18
+ "id": "intermediateHost",
19
+ "title": "Intermediate Guest+Host (B)",
20
+ "url": "...callchain-intermediate.html"
21
+ }</pre></div><div><h3 class="font-semibold text-amber-800 mb-1">callContext.callChain (originator chain)</h3><pre data-testid="expected-callchain" class="bg-white rounded p-2 text-amber-900">
22
+ [
23
+ {
24
+ "id": "grandchildGuest",
25
+ "metadata": { "role": "validator", "region": "US-East" }
26
+ },
27
+ {
28
+ "id": "intermediateHost",
29
+ "metadata": { "vendor": "Acme Credit Corp", "tier": "premium" }
30
+ }
31
+ ]</pre></div></div></div><div class="grid grid-cols-2 gap-4"><div><h2 class="text-md font-semibold mb-2">Actual Results (from Root Host A)</h2><div data-testid="direct-caller-panel" class="bg-white rounded-lg shadow p-3 mb-3"><h3 class="text-sm font-medium text-gray-500 mb-1">callContext.guest (direct caller)</h3><pre data-testid="direct-caller-result" id="directCallerResult" class="text-xs bg-gray-100 rounded p-2 min-h-[40px] whitespace-pre-wrap">
32
+ Waiting — click a button in Grandchild (C)...</pre></div><div data-testid="callchain-panel" class="bg-white rounded-lg shadow p-3 mb-3"><h3 class="text-sm font-medium text-gray-500 mb-1">callContext.callChain (original callers)</h3><pre data-testid="callchain-result" id="callChainResult" class="text-xs bg-gray-100 rounded p-2 min-h-[60px] whitespace-pre-wrap">
33
+ Waiting — click a button in Grandchild (C)...</pre></div><div data-testid="method-result-panel" class="bg-white rounded-lg shadow p-3 mb-3"><h3 class="text-sm font-medium text-gray-500 mb-1">Method return value (returned to grandchild)</h3><pre data-testid="method-return-result" id="methodReturnResult" class="text-xs bg-gray-100 rounded p-2 min-h-[40px] whitespace-pre-wrap">
34
+ Waiting — click a button in Grandchild (C)...</pre></div><div data-testid="invocation-log-panel" class="bg-white rounded-lg shadow p-3"><h3 class="text-sm font-medium text-gray-500 mb-1">Invocation Log</h3><div data-testid="invocation-log" id="invocationLog" class="text-xs bg-gray-100 rounded p-2 min-h-[80px] max-h-[200px] overflow-y-auto"></div></div></div><div><h2 class="text-md font-semibold mb-2">Intermediate Guest (B) → Grandchild Guest (C)</h2><div data-testid="intermediate-guest-container" id="intermediate-container" class="border-2 border-dashed border-indigo-300 rounded-lg h-[400px]"></div></div></div><div class="bg-gray-100 border border-gray-300 rounded-md p-3 mt-4"><h2 class="text-sm font-bold text-gray-800 mb-2">Verification Checklist</h2><div class="grid grid-cols-2 gap-2 text-xs"><label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-01:</strong> All three layers initialized without errors (Log shows "ready" messages, no ERROR lines)</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-02:</strong> <code>callContext.guest.id</code> = <code>"intermediateHost"</code> (B is the direct caller, not C)</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-03:</strong> <code>callChain[0].id</code> = <code>"grandchildGuest"</code> with metadata <code>{ role, region }</code></span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-04:</strong> <code>callChain[1].id</code> = <code>"intermediateHost"</code> with metadata <code>{ vendor, tier }</code></span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-05:</strong> Grandchild (C) receives the method return value (result appears in C's panel)</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-06:</strong> All three methods work: <code>getCallerInfo()</code>, <code>ping()</code>, <code>getLoanDetails()</code></span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-CC-07:</strong> No cross-origin or console errors during invocations</span></label></div></div></main><script src="./callchain-host.js" type="module"></script></body></html>
@@ -0,0 +1,3 @@
1
+ import{Analytics as r}from"./analytics-object-v2.js";import{getGuestBaseUrl as s,getHost as a}from"./utils.js";const o=t=>{const e=document.getElementById("invocationLog"),n=new Date().toLocaleTimeString();e.innerHTML+=`<div>[${n}] ${t}</div>`,e.scrollTop=e.scrollHeight},c=t=>t?{id:t.id,title:t.title,url:t.url}:null,l=t=>{document.getElementById("directCallerResult").textContent=t?.guest?JSON.stringify(c(t.guest),null,2):"(none)",document.getElementById("callChainResult").textContent=t?.callChain?JSON.stringify(t.callChain,null,2):"(empty \u2014 direct call, no forwarding)"};class d extends ice.host.ScriptingObject{constructor(){super("TestService")}getCallerInfo=()=>{const e=this.getCallerInfo.callContext;o("getCallerInfo() invoked"),l(e);const n={status:"ok",timestamp:Date.now()};return document.getElementById("methodReturnResult").textContent=JSON.stringify(n,null,2),n};ping=e=>{const n=this.ping.callContext;o(`ping("${e}") invoked`),l(n);const i={pong:e};return document.getElementById("methodReturnResult").textContent=JSON.stringify(i,null,2),i};getLoanDetails=()=>{const e=this.getLoanDetails.callContext;o("getLoanDetails() invoked"),l(e);const n={id:"LOAN-001",borrower:"Jane Doe",amount:5e5};return document.getElementById("methodReturnResult").textContent=JSON.stringify(n,null,2),n}}const u=async()=>{o("Initializing Root Host (A)...");const t=new r,e=a(t);if(!e){o("ERROR: Failed to create SSFHost");return}const n=new d;e.addScriptingObject(n),o("TestService scripting object registered");const i=await s();o(`Loading intermediate guest from: ${window.location.origin}`),e.loadGuest({id:"intermediateHost",url:new URL("./callchain-intermediate.html",window.location.href).href,title:"Intermediate Guest+Host (B)",targetElement:document.getElementById("intermediate-container"),metadata:{vendor:"Acme Credit Corp",tier:"premium"},options:{allowSameOrigin:!0},onLoad:()=>o("Intermediate guest (B) loaded"),onError:()=>o("ERROR: Intermediate guest (B) failed to load")}),o("Root Host (A) ready \u2014 waiting for calls from grandchild (C)")};window.addEventListener("DOMContentLoaded",u);
2
+
3
+ //# sourceMappingURL=callchain-host.js.map
Binary file
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["webpack://ice.host/callchain-host.js"],"sourcesContent":["import { Analytics } from './analytics-object-v2.js';\nimport { getGuestBaseUrl, getHost } from './utils.js';\n\nconst log = (msg) => {\n const el = document.getElementById('invocationLog');\n const ts = new Date().toLocaleTimeString();\n el.innerHTML += `<div>[${ts}] ${msg}</div>`;\n el.scrollTop = el.scrollHeight;\n};\n\nconst safeGuest = (guest) =>\n guest ? { id: guest.id, title: guest.title, url: guest.url } : null;\n\nconst displayCallContext = (ctx) => {\n document.getElementById('directCallerResult').textContent = ctx?.guest\n ? JSON.stringify(safeGuest(ctx.guest), null, 2)\n : '(none)';\n document.getElementById('callChainResult').textContent = ctx?.callChain\n ? JSON.stringify(ctx.callChain, null, 2)\n : '(empty — direct call, no forwarding)';\n};\n\nclass TestService extends ice.host.ScriptingObject {\n constructor() {\n super('TestService');\n }\n\n getCallerInfo = () => {\n const ctx = this.getCallerInfo.callContext;\n log('getCallerInfo() invoked');\n displayCallContext(ctx);\n const result = { status: 'ok', timestamp: Date.now() };\n document.getElementById('methodReturnResult').textContent = JSON.stringify(\n result,\n null,\n 2,\n );\n return result;\n };\n\n ping = (message) => {\n const ctx = this.ping.callContext;\n log(`ping(\"${message}\") invoked`);\n displayCallContext(ctx);\n const result = { pong: message };\n document.getElementById('methodReturnResult').textContent = JSON.stringify(\n result,\n null,\n 2,\n );\n return result;\n };\n\n getLoanDetails = () => {\n const ctx = this.getLoanDetails.callContext;\n log('getLoanDetails() invoked');\n displayCallContext(ctx);\n const result = {\n id: 'LOAN-001',\n borrower: 'Jane Doe',\n amount: 500000,\n };\n document.getElementById('methodReturnResult').textContent = JSON.stringify(\n result,\n null,\n 2,\n );\n return result;\n };\n}\n\nconst init = async () => {\n log('Initializing Root Host (A)...');\n const analyticsObj = new Analytics();\n const host = getHost(analyticsObj);\n if (!host) {\n log('ERROR: Failed to create SSFHost');\n return;\n }\n\n const testService = new TestService();\n host.addScriptingObject(testService);\n log('TestService scripting object registered');\n\n const guestBaseUrl = await getGuestBaseUrl();\n log(`Loading intermediate guest from: ${window.location.origin}`);\n\n host.loadGuest({\n id: 'intermediateHost',\n url: new URL('./callchain-intermediate.html', window.location.href).href,\n title: 'Intermediate Guest+Host (B)',\n targetElement: document.getElementById('intermediate-container'),\n metadata: { vendor: 'Acme Credit Corp', tier: 'premium' },\n options: { allowSameOrigin: true },\n onLoad: () => log('Intermediate guest (B) loaded'),\n onError: () => log('ERROR: Intermediate guest (B) failed to load'),\n });\n\n log('Root Host (A) ready — waiting for calls from grandchild (C)');\n};\n\nwindow.addEventListener('DOMContentLoaded', init);\n"],"mappings":"AAAA,OAAS,aAAAA,MAAiB,2BAC1B,OAAS,mBAAAC,EAAiB,WAAAC,MAAe,aAEzC,MAAMC,EAAOC,GAAQ,CACnB,MAAMC,EAAK,SAAS,eAAe,eAAe,EAC5CC,EAAK,IAAI,KAAK,EAAE,mBAAmB,EACzCD,EAAG,WAAa,SAASC,CAAE,KAAKF,CAAG,SACnCC,EAAG,UAAYA,EAAG,YACpB,EAEME,EAAaC,GACjBA,EAAQ,CAAE,GAAIA,EAAM,GAAI,MAAOA,EAAM,MAAO,IAAKA,EAAM,GAAI,EAAI,KAE3DC,EAAsBC,GAAQ,CAClC,SAAS,eAAe,oBAAoB,EAAE,YAAcA,GAAK,MAC7D,KAAK,UAAUH,EAAUG,EAAI,KAAK,EAAG,KAAM,CAAC,EAC5C,SACJ,SAAS,eAAe,iBAAiB,EAAE,YAAcA,GAAK,UAC1D,KAAK,UAAUA,EAAI,UAAW,KAAM,CAAC,EACrC,2CACN,EAEA,MAAMC,UAAoB,IAAI,KAAK,eAAgB,CACjD,aAAc,CACZ,MAAM,aAAa,CACrB,CAEA,cAAgB,IAAM,CACpB,MAAMD,EAAM,KAAK,cAAc,YAC/BP,EAAI,yBAAyB,EAC7BM,EAAmBC,CAAG,EACtB,MAAME,EAAS,CAAE,OAAQ,KAAM,UAAW,KAAK,IAAI,CAAE,EACrD,gBAAS,eAAe,oBAAoB,EAAE,YAAc,KAAK,UAC/DA,EACA,KACA,CACF,EACOA,CACT,EAEA,KAAQC,GAAY,CAClB,MAAMH,EAAM,KAAK,KAAK,YACtBP,EAAI,SAASU,CAAO,YAAY,EAChCJ,EAAmBC,CAAG,EACtB,MAAME,EAAS,CAAE,KAAMC,CAAQ,EAC/B,gBAAS,eAAe,oBAAoB,EAAE,YAAc,KAAK,UAC/DD,EACA,KACA,CACF,EACOA,CACT,EAEA,eAAiB,IAAM,CACrB,MAAMF,EAAM,KAAK,eAAe,YAChCP,EAAI,0BAA0B,EAC9BM,EAAmBC,CAAG,EACtB,MAAME,EAAS,CACb,GAAI,WACJ,SAAU,WACV,OAAQ,GACV,EACA,gBAAS,eAAe,oBAAoB,EAAE,YAAc,KAAK,UAC/DA,EACA,KACA,CACF,EACOA,CACT,CACF,CAEA,MAAME,EAAO,SAAY,CACvBX,EAAI,+BAA+B,EACnC,MAAMY,EAAe,IAAIf,EACnBgB,EAAOd,EAAQa,CAAY,EACjC,GAAI,CAACC,EAAM,CACTb,EAAI,iCAAiC,EACrC,MACF,CAEA,MAAMc,EAAc,IAAIN,EACxBK,EAAK,mBAAmBC,CAAW,EACnCd,EAAI,yCAAyC,EAE7C,MAAMe,EAAe,MAAMjB,EAAgB,EAC3CE,EAAI,oCAAoC,OAAO,SAAS,MAAM,EAAE,EAEhEa,EAAK,UAAU,CACb,GAAI,mBACJ,IAAK,IAAI,IAAI,gCAAiC,OAAO,SAAS,IAAI,EAAE,KACpE,MAAO,8BACP,cAAe,SAAS,eAAe,wBAAwB,EAC/D,SAAU,CAAE,OAAQ,mBAAoB,KAAM,SAAU,EACxD,QAAS,CAAE,gBAAiB,EAAK,EACjC,OAAQ,IAAMb,EAAI,+BAA+B,EACjD,QAAS,IAAMA,EAAI,8CAA8C,CACnE,CAAC,EAEDA,EAAI,kEAA6D,CACnE,EAEA,OAAO,iBAAiB,mBAAoBW,CAAI","names":["Analytics","getGuestBaseUrl","getHost","log","msg","el","ts","safeGuest","guest","displayCallContext","ctx","TestService","result","message","init","analyticsObj","host","testService","guestBaseUrl"],"sourceRoot":"","file":"callchain-host.js"}
@@ -0,0 +1 @@
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Intermediate Guest+Host (B)</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script src="http://localhost:4001/index.js"></script><script defer="defer" src="index.js"></script></head><body class="bg-orange-50 p-2"><h2 class="text-sm font-semibold text-orange-800 mb-1">Intermediate (B) — Guest + Host</h2><div data-testid="intermediate-status" id="status" class="text-xs bg-orange-100 rounded p-2 mb-2 min-h-[30px]">Initializing...</div><div data-testid="grandchild-container" id="grandchild-container" class="border border-dashed border-orange-400 rounded h-[250px]"></div><script type="module">import{Analytics}from"./analytics-object-v2.js";import{getGuestBaseUrl,getHost}from"./utils.js";const statusEl=document.getElementById("status"),log=t=>{const e=(new Date).toLocaleTimeString();statusEl.innerHTML+=`<div>[${e}] ${t}</div>`};(async()=>{try{log("Connecting to Root Host (A) as V2 guest...");const t=new ice.guest.SSFGuest({logger:{index:"intermediate-guest",team:"ui platform",appName:"intermediate-b"},keepAlive:!1});await t.connect(),log("Connected to Root Host (A)");const e=await t.getObject("TestService");log("Got TestService proxy from Root Host (A)");const o=new Analytics,a=getHost(o);if(!a)return void log("ERROR: Failed to create intermediate SSFHost");log("Created intermediate SSFHost (B)");const n=a.cloneScriptingObject(e);a.addScriptingObject(n),log("Cloned TestService for grandchild guests");const i=await getGuestBaseUrl();a.loadGuest({id:"grandchildGuest",url:new URL("./callchain-grandchild.html",i).href,title:"Grandchild Guest (C)",targetElement:document.getElementById("grandchild-container"),metadata:{role:"validator",region:"US-East"},onLoad:()=>log("Grandchild guest (C) loaded"),onError:()=>log("ERROR: Grandchild guest (C) failed to load")}),log("Intermediate (B) ready")}catch(t){log(`ERROR: ${t.message}`)}})()</script></body></html>
@@ -0,0 +1,5 @@
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>SSF E2E — Comprehensive Host Test</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script defer="defer" src="index.js"></script></head><body class="bg-gray-50 text-sm"><header class="bg-indigo-700 text-white px-4 py-3 flex items-center justify-between"><h1 class="text-lg font-semibold">Comprehensive E2E Host Test</h1><a href="./e2e-index.html" class="text-indigo-200 hover:text-white text-sm">&larr; Test Suite</a></header><main class="mx-auto max-w-7xl px-4 py-3"><div class="grid grid-cols-2 gap-3 mb-3"><div class="bg-blue-50 border border-blue-200 rounded-md p-3"><h2 class="text-sm font-bold text-blue-900 mb-1">How to Use This Page</h2><ol class="text-xs text-blue-800 space-y-1 list-decimal list-inside"><li>Start the host dev server (port 4000) and guest dev server (port 4001).</li><li>Buttons in <strong>Column 1</strong> (left) execute host-side actions.</li><li><strong>Column 2</strong> (center) shows embedded guest iframes — interact with them to invoke methods, subscribe to events, etc.</li><li><strong>Column 3</strong> (right) shows live results: Event Log, callContext, dispatch results, and the guest list.</li><li>Work through the buttons <strong>top to bottom</strong> for the recommended flow. Each button is labelled with a test case ID (TC-xxx).</li></ol></div><div class="bg-amber-50 border border-amber-200 rounded-md p-3"><h2 class="text-sm font-bold text-amber-900 mb-1">Recommended Flow</h2><ol class="text-xs text-amber-800 space-y-1 list-decimal list-inside"><li><strong>Load guests</strong>: TC-LOAD-01 (embed) → TC-LOAD-02 (popup) → TC-LOAD-04 (with params). Check Event Log for "loaded" messages.</li><li><strong>Scripting objects</strong>: TC-SO-01 (add Inventory). In the guest iframe, call <code>getObject("Inventory")</code> and invoke <code>getStock()</code>. Check return value and callContext.</li><li><strong>Events</strong>: In the guest, subscribe to <code>Loan.onPreSave</code>. Then click TC-EVT-01 on the host. Guest should receive the event.</li><li><strong>Lifecycle</strong>: TC-LIFE-01 (unload), TC-LIFE-03 (close host). Verify guests are removed and host is destroyed.</li></ol></div></div><div class="grid grid-cols-3 gap-3"><div class="space-y-3"><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Guest Loading</h2><div class="space-y-2"><button data-testid="btn-load-embed" id="btnLoadEmbed" class="w-full rounded bg-indigo-600 px-3 py-1.5 text-xs text-white hover:bg-indigo-700">TC-LOAD-01: Load Embedded Guest</button><p class="text-[10px] text-gray-400 -mt-1">Expected: guest iframe appears in center column, log shows "loaded"</p><button data-testid="btn-load-popup" id="btnLoadPopup" class="w-full rounded bg-indigo-600 px-3 py-1.5 text-xs text-white hover:bg-indigo-700">TC-LOAD-02: Load Popup Guest</button><p class="text-[10px] text-gray-400 -mt-1">Expected: new browser popup window opens</p><button data-testid="btn-load-multiple" id="btnLoadMultiple" class="w-full rounded bg-indigo-600 px-3 py-1.5 text-xs text-white hover:bg-indigo-700">TC-LOAD-03: loadGuests (3 instances)</button><p class="text-[10px] text-gray-400 -mt-1">Expected: 3 guest iframes load, "List all guests" shows 3+ entries</p><button data-testid="btn-load-with-params" id="btnLoadWithParams" class="w-full rounded bg-indigo-600 px-3 py-1.5 text-xs text-white hover:bg-indigo-700">TC-LOAD-04: Load with searchParams</button><p class="text-[10px] text-gray-400 -mt-1">Expected: guest shows URL params in its "Params:" line</p><button data-testid="btn-load-with-metadata" id="btnLoadWithMetadata" class="w-full rounded bg-indigo-600 px-3 py-1.5 text-xs text-white hover:bg-indigo-700">TC-META-01: Load with metadata</button><p class="text-[10px] text-gray-400 -mt-1">Expected: when guest invokes a method, callContext.callChain shows metadata</p><button data-testid="btn-load-bad-url" id="btnLoadBadUrl" class="w-full rounded bg-red-600 px-3 py-1.5 text-xs text-white hover:bg-red-700">TC-LOAD-05: Load invalid URL (onError)</button><p class="text-[10px] text-gray-400 -mt-1">Expected: log shows "ERROR" or onError callback fires</p></div></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Scripting Object Management</h2><div class="space-y-2"><button data-testid="btn-add-so" id="btnAddSO" class="w-full rounded bg-green-600 px-3 py-1.5 text-xs text-white hover:bg-green-700">TC-SO-01: Add "Inventory" Object</button><p class="text-[10px] text-gray-400 -mt-1">Expected: guest can now <code>getObject("Inventory")</code> and invoke its methods</p><button data-testid="btn-add-guest-so" id="btnAddGuestSO" class="w-full rounded bg-green-600 px-3 py-1.5 text-xs text-white hover:bg-green-700">TC-SO-04: Add Guest-Scoped Object</button><p class="text-[10px] text-gray-400 -mt-1">Expected: only the target guest can access this object, others get "not available"</p><button data-testid="btn-remove-so" id="btnRemoveSO" class="w-full rounded bg-yellow-600 px-3 py-1.5 text-xs text-white hover:bg-yellow-700">TC-SO-02: Remove "Inventory" Object</button><p class="text-[10px] text-gray-400 -mt-1">Expected: guest's <code>getObject("Inventory")</code> now fails</p><button data-testid="btn-remove-all-so" id="btnRemoveAllSO" class="w-full rounded bg-yellow-600 px-3 py-1.5 text-xs text-white hover:bg-yellow-700">TC-SO-02b: Remove All Objects</button><p class="text-[10px] text-gray-400 -mt-1">Expected: all getObject calls fail until objects are re-added</p></div></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Event Dispatching</h2><div class="space-y-2"><button data-testid="btn-dispatch-all" id="btnDispatchAll" class="w-full rounded bg-purple-600 px-3 py-1.5 text-xs text-white hover:bg-purple-700">TC-EVT-01: Dispatch onPreSave to all</button><p class="text-[10px] text-gray-400 -mt-1">Expected: all guests subscribed to onPreSave receive the event</p><button data-testid="btn-dispatch-targeted" id="btnDispatchTargeted" class="w-full rounded bg-purple-600 px-3 py-1.5 text-xs text-white hover:bg-purple-700">TC-EVT-02: Dispatch to specific guest</button><p class="text-[10px] text-gray-400 -mt-1">Expected: only the targeted guest receives the event</p><button data-testid="btn-dispatch-feedback" id="btnDispatchFeedback" class="w-full rounded bg-purple-600 px-3 py-1.5 text-xs text-white hover:bg-purple-700">TC-EVT-03: Dispatch with timeout (feedback)</button><p class="text-[10px] text-gray-400 -mt-1">Expected: Dispatch Results shows the guest's feedback return value</p><button data-testid="btn-dispatch-amount" id="btnDispatchAmount" class="w-full rounded bg-purple-600 px-3 py-1.5 text-xs text-white hover:bg-purple-700">TC-EVT-04: Dispatch onAmountChanged (criteria)</button><p class="text-[10px] text-gray-400 -mt-1">Expected: only guests subscribed with matching criteria receive it</p></div></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Guest Lifecycle</h2><div class="space-y-2"><button data-testid="btn-unload-guest" id="btnUnloadGuest" class="w-full rounded bg-orange-600 px-3 py-1.5 text-xs text-white hover:bg-orange-700">TC-LIFE-01: Unload first embedded guest</button><p class="text-[10px] text-gray-400 -mt-1">Expected: guest iframe removed, "List all guests" count decreases</p><button data-testid="btn-register-close" id="btnRegisterClose" class="w-full rounded bg-orange-600 px-3 py-1.5 text-xs text-white hover:bg-orange-700">TC-LIFE-02: Register onGuestClose</button><p class="text-[10px] text-gray-400 -mt-1">Expected: log shows callback registered; fires when guest calls close()</p><button data-testid="btn-list-guests" id="btnListGuests" class="w-full rounded bg-gray-600 px-3 py-1.5 text-xs text-white hover:bg-gray-700">List all guests</button><p class="text-[10px] text-gray-400 -mt-1">Expected: Guest List panel shows all currently loaded guests</p><button data-testid="btn-set-loglevel" id="btnSetLogLevel" class="w-full rounded bg-gray-600 px-3 py-1.5 text-xs text-white hover:bg-gray-700">Set Log Level (Debug)</button> <button data-testid="btn-close-host" id="btnCloseHost" class="w-full rounded bg-red-700 px-3 py-1.5 text-xs text-white hover:bg-red-800">TC-LIFE-03: Close host</button><p class="text-[10px] text-gray-400 -mt-1">Expected: all guests unloaded, host destroyed, further actions fail</p></div></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Sandbox Configuration</h2><div class="space-y-2"><button data-testid="btn-sandbox-default" id="btnSandboxDefault" class="w-full rounded bg-teal-600 px-3 py-1.5 text-xs text-white hover:bg-teal-700">TC-SB-01: Default sandbox</button><p class="text-[10px] text-gray-400 -mt-1">Expected: iframe has standard sandbox attributes</p><button data-testid="btn-sandbox-custom" id="btnSandboxCustom" class="w-full rounded bg-teal-600 px-3 py-1.5 text-xs text-white hover:bg-teal-700">TC-SB-02: Custom sandbox values</button><p class="text-[10px] text-gray-400 -mt-1">Expected: iframe sandbox attribute reflects custom permissions</p><button data-testid="btn-sandbox-disabled" id="btnSandboxDisabled" class="w-full rounded bg-teal-600 px-3 py-1.5 text-xs text-white hover:bg-teal-700">TC-SB-03: Sandbox disabled</button><p class="text-[10px] text-gray-400 -mt-1">Expected: iframe has no sandbox attribute (full permissions)</p></div></div></div><div class="space-y-3"><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Embedded Guests</h2><div data-testid="embed-container" id="embedContainer" class="border-2 border-dashed border-indigo-300 rounded min-h-[300px] space-y-2"></div></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Sandbox Test Container</h2><div data-testid="sandbox-container" id="sandboxContainer" class="border-2 border-dashed border-teal-300 rounded min-h-[150px] space-y-2"></div></div></div><div class="space-y-3"><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Event Log</h2><div data-testid="event-log" id="eventLog" class="text-xs bg-gray-100 rounded p-2 min-h-[200px] max-h-[350px] overflow-y-auto font-mono"></div><button data-testid="btn-clear-log" id="btnClearLog" class="mt-1 text-xs text-gray-400 hover:text-gray-600">Clear</button></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">callContext (last invocation)</h2><div data-testid="callcontext-panel"><h3 class="text-xs text-gray-500">callContext.guest</h3><pre data-testid="callcontext-guest" id="callContextGuest" class="text-xs bg-gray-100 rounded p-1 mb-1 whitespace-pre-wrap min-h-[30px]">
2
+ —</pre><h3 class="text-xs text-gray-500">callContext.callChain</h3><pre data-testid="callcontext-chain" id="callContextChain" class="text-xs bg-gray-100 rounded p-1 mb-1 whitespace-pre-wrap min-h-[30px]">
3
+ —</pre></div></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Dispatch Results</h2><pre data-testid="dispatch-result" id="dispatchResult" class="text-xs bg-gray-100 rounded p-2 min-h-[60px] whitespace-pre-wrap">
4
+ —</pre></div><div class="bg-white rounded-lg shadow p-3"><h2 class="font-semibold text-gray-700 mb-2 border-b pb-1">Guest List</h2><pre data-testid="guest-list" id="guestList" class="text-xs bg-gray-100 rounded p-2 min-h-[60px] whitespace-pre-wrap">
5
+ —</pre></div></div></div><div class="bg-gray-100 border border-gray-300 rounded-md p-3 mt-4"><h2 class="text-sm font-bold text-gray-800 mb-2">Verification Checklist</h2><div class="grid grid-cols-3 gap-2 text-xs"><div><p class="font-semibold text-gray-600 mb-1">Guest Loading</p><label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LOAD-01:</strong> Embedded guest loads in iframe</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LOAD-02:</strong> Popup guest opens in new window</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LOAD-03:</strong> loadGuests loads 3 guests at once</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LOAD-04:</strong> searchParams visible in guest</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LOAD-05:</strong> Bad URL triggers onError</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-META-01:</strong> Metadata appears in callChain</span></label></div><div><p class="font-semibold text-gray-600 mb-1">Scripting Objects & Events</p><label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-SO-01:</strong> Guest can getObject after add</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-SO-02:</strong> Guest can't getObject after remove</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-SO-04:</strong> Scoped object only visible to one guest</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-EVT-01:</strong> All subscribed guests receive event</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-EVT-02:</strong> Only targeted guest receives event</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-EVT-03:</strong> Feedback return value in Dispatch Results</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-EVT-04:</strong> Criteria filtering works</span></label></div><div><p class="font-semibold text-gray-600 mb-1">Lifecycle & Sandbox</p><label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LIFE-01:</strong> Unload removes guest iframe</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LIFE-02:</strong> onGuestClose fires on guest disconnect</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-LIFE-03:</strong> Close host destroys all guests</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-SB-01:</strong> Default sandbox attrs present</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-SB-02:</strong> Custom sandbox attrs applied</span></label> <label class="flex items-start gap-1"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-SB-03:</strong> No sandbox attr when disabled</span></label></div></div></div></main><script src="./e2e-host.js" type="module"></script></body></html>
@@ -0,0 +1,8 @@
1
+ import{Analytics as E}from"./analytics-object-v2.js";import{getGuestBaseUrl as v,getHost as y}from"./utils.js";let n=null,l=null,a=0;const m=document.getElementById("eventLog"),e=(d,s="info")=>{const i=new Date().toLocaleTimeString(),c=s==="error"?"text-red-600":s==="success"?"text-green-600":s==="warn"?"text-yellow-600":"text-gray-700";m.innerHTML+=`<div class="${c}">[${i}] ${d}</div>`,m.scrollTop=m.scrollHeight};document.getElementById("btnClearLog").addEventListener("click",()=>{m.innerHTML=""});const g=d=>{document.getElementById("callContextGuest").textContent=d?.guest?JSON.stringify(d.guest,null,2):"(none \u2014 internal call)",document.getElementById("callContextChain").textContent=d?.callChain?JSON.stringify(d.callChain,null,2):"(empty \u2014 direct call)"};class L extends ice.host.ScriptingObject{constructor(){super("Loan"),this.loanId="E2E-001",this.creditScore=750,this.onPreSave=new ice.host.Event({name:"onPreSave",objectId:"Loan"}),this.onAmountChanged=new ice.host.Event({name:"onAmountChanged",objectId:"Loan"})}getLoanDetails=()=>{const s=this.getLoanDetails.callContext;return e("Loan.getLoanDetails() invoked"),g(s),{id:this.loanId,borrower:"Jane Doe",amount:5e5,term:30,creditScore:this.creditScore}};setCreditScore=s=>{const i=this.setCreditScore.callContext;return e(`Loan.setCreditScore(${s}) invoked`),g(i),this.creditScore=s,{updated:!0,score:s}};throwError=()=>{throw e("Loan.throwError() invoked \u2014 will throw"),new Error("Intentional error for TC-SO-08")};asyncMethod=async s=>{const i=this.asyncMethod.callContext;return e(`Loan.asyncMethod(${s}) invoked`),g(i),await new Promise(c=>setTimeout(c,s||100)),{resolved:!0,delay:s}}}class S extends ice.host.ScriptingObject{constructor(){super("Application")}keepSessionAlive=()=>Promise.resolve()}class p extends ice.host.ScriptingObject{constructor(){super("Inventory")}getItems=()=>(e("Inventory.getItems() invoked"),[{id:1,name:"Widget A"},{id:2,name:"Widget B"}])}const h=(d,s={})=>{const i=new URL("./e2e-guest.html",l).href,c=n.loadGuest({id:d,url:i,title:`Guest ${d}`,targetElement:document.getElementById("embedContainer"),options:{fitToContent:!0,...s.options},searchParams:s.searchParams||{},metadata:s.metadata,onLoad:t=>e(`onLoad("${t}")`,"success"),onError:t=>e(`onError("${t}")`,"error")});return e(`loadGuest("${d}") called`),c},u=()=>{const d=n?.getGuests()||[];document.getElementById("guestList").textContent=d.length?d.map(s=>`${s.id} [${s.window?.closed?"closed":"open"}] ready=${s.ready}`).join(`
2
+ `):"(no guests)"},f=async()=>{e("Initializing E2E Host...");const d=new E;if(n=y(d),!n){e("ERROR: Failed to create SSFHost","error");return}l=await v(),e(`Guest base URL: ${l}`);const s=new L,i=new S;n.addScriptingObject(s),n.addScriptingObject(i),e("Registered: Loan, Application"),document.getElementById("btnLoadEmbed").addEventListener("click",()=>{a++,h(`embed-${a}`)}),document.getElementById("btnLoadPopup").addEventListener("click",()=>{const t=new URL("./e2e-guest.html",l);t.searchParams.set("openMode","popup");try{n.loadGuest({id:"popup-guest",url:t.href,title:"Popup Guest",targetElement:document.getElementById("embedContainer"),options:{openMode:ice.host.OpenMode.Popup},onLoad:o=>e(`Popup onLoad("${o}")`,"success"),onError:o=>e(`Popup onError("${o}")`,"error")}),e('loadGuest("popup-guest") as popup')}catch(o){e(`Popup failed: ${o.message}`,"error")}u()}),document.getElementById("btnLoadMultiple").addEventListener("click",()=>{a++;const t=new URL("./e2e-guest.html",l).href;n.loadGuests({id:`multi-${a}`,url:t,title:`Multi Guest ${a}`,targetElement:document.getElementById("embedContainer"),options:{fitToContent:!0},searchParamsList:[{instance:"1",mode:"read"},{instance:"2",mode:"write"},{instance:"3",mode:"admin"}]}),e("loadGuests() with 3 instances"),u()}),document.getElementById("btnLoadWithParams").addEventListener("click",()=>{a++,h(`params-${a}`,{searchParams:{env:"qa",feature:"sandbox-test",debug:!0}})}),document.getElementById("btnLoadWithMetadata").addEventListener("click",()=>{a++,h(`meta-${a}`,{metadata:{vendor:"AcmeCorp",tier:"premium",region:"US-East"}})}),document.getElementById("btnLoadBadUrl").addEventListener("click",()=>{a++,n.loadGuest({id:`bad-${a}`,url:"http://localhost:9999/does-not-exist.html",title:`Bad Guest ${a}`,targetElement:document.getElementById("embedContainer"),onLoad:t=>e(`Unexpected onLoad("${t}") for bad URL`,"warn"),onError:t=>e(`Expected onError("${t}") for bad URL`,"success")}),e("loadGuest() with invalid URL")});let c=!1;document.getElementById("btnAddSO").addEventListener("click",()=>{if(c){e("Inventory already added","warn");return}n.addScriptingObject(new p),c=!0,e('Added "Inventory" scripting object',"success")}),document.getElementById("btnAddGuestSO").addEventListener("click",()=>{const t=n.getGuests();if(!t.length){e("No guests loaded \u2014 load a guest first","warn");return}const o=t[0].id;try{const r=new p;r.id="GuestInventory",n.addScriptingObject(r,{guestId:o}),e(`Added guest-scoped "GuestInventory" for ${o}`,"success")}catch(r){e(`addScriptingObject failed: ${r.message}`,"error")}}),document.getElementById("btnRemoveSO").addEventListener("click",()=>{try{n.removeScriptingObject("Inventory"),c=!1,e('Removed "Inventory" scripting object',"success")}catch(t){e(`removeScriptingObject failed: ${t.message}`,"error")}}),document.getElementById("btnRemoveAllSO").addEventListener("click",()=>{n.removeAllScriptingObjects(),c=!1,e("Removed all scripting objects","success")}),document.getElementById("btnDispatchAll").addEventListener("click",async()=>{e("Dispatching onPreSave to all guests...");try{const t=await n.dispatchEvent({event:s.onPreSave,eventParams:{creditScore:750,amount:5e5}});document.getElementById("dispatchResult").textContent=`onPreSave results:
3
+ ${JSON.stringify(t,null,2)}`,e(`onPreSave dispatched, ${t.length} responses`,"success")}catch(t){e(`Dispatch failed: ${t.message}`,"error")}}),document.getElementById("btnDispatchTargeted").addEventListener("click",async()=>{const t=n.getGuests();if(!t.length){e("No guests \u2014 load a guest first","warn");return}const o=t[0];e(`Dispatching onPreSave to guest "${o.id}" only...`);try{const r=await n.dispatchEvent({event:s.onPreSave,eventParams:{creditScore:800,amount:25e4},eventOptions:{guestId:o.id}});document.getElementById("dispatchResult").textContent=`Targeted results (${o.id}):
4
+ ${JSON.stringify(r,null,2)}`,e("Targeted dispatch complete","success")}catch(r){e(`Dispatch failed: ${r.message}`,"error")}}),document.getElementById("btnDispatchFeedback").addEventListener("click",async()=>{e("Dispatching onPreSave with 3s timeout (feedback)...");try{const t=await n.dispatchEvent({event:s.onPreSave,eventParams:{creditScore:600,amount:1e6},eventOptions:{timeout:3e3}});document.getElementById("dispatchResult").textContent=`Feedback results:
5
+ ${JSON.stringify(t,null,2)}`,e(`Feedback dispatch complete, ${t.length} responses`,"success")}catch(t){e(`Dispatch with feedback failed: ${t.message}`,"error")}}),document.getElementById("btnDispatchAmount").addEventListener("click",async()=>{e("Dispatching onAmountChanged (for criteria-based subscribers)...");try{const t=await n.dispatchEvent({event:s.onAmountChanged,eventParams:{amount:75e4}});document.getElementById("dispatchResult").textContent=`onAmountChanged results:
6
+ ${JSON.stringify(t,null,2)}`,e("onAmountChanged dispatched","success")}catch(t){e(`Dispatch failed: ${t.message}`,"error")}}),document.getElementById("btnUnloadGuest").addEventListener("click",()=>{const t=n.getGuests(),o=t.filter(b=>!b.window||b.window===window.frames[0]?.window);if(!t.length){e("No guests to unload","warn");return}const r=t[0];e(`Unloading guest "${r.id}"...`),n.unloadGuest(r.id),e(`Unloaded "${r.id}"`,"success"),u()}),document.getElementById("btnRegisterClose").addEventListener("click",()=>{const t=n.getGuests();if(!t.length){e("No guests \u2014 load a guest first","warn");return}t.forEach(o=>{n.onGuestClose({id:o.id,guestCloseCallback:({id:r})=>{e(`onGuestClose fired for "${r}"`,"success"),u()}})}),e(`Registered onGuestClose for ${t.length} guest(s)`,"success")}),document.getElementById("btnListGuests").addEventListener("click",()=>{u(),e(`Listed ${n.getGuests().length} guest(s)`)}),document.getElementById("btnSetLogLevel").addEventListener("click",()=>{n.setLogLevel(10),e("Set log level to 10 (debug)","success")}),document.getElementById("btnCloseHost").addEventListener("click",()=>{e("Closing host..."),n.close(),e("Host closed \u2014 all guests should be disconnected","success"),u()}),document.getElementById("btnSandboxDefault").addEventListener("click",()=>{a++;const t=new URL("./e2e-guest.html",l).href;n.loadGuest({id:`sb-default-${a}`,url:t,title:"Sandbox Default",targetElement:document.getElementById("sandboxContainer"),options:{fitToContent:!0},onLoad:()=>{e("SB-01: Default sandbox guest loaded","success");const o=document.querySelector("#sandboxContainer iframe:last-child");e(` sandbox="${o?.getAttribute("sandbox")||"(none)"}"`)}})}),document.getElementById("btnSandboxCustom").addEventListener("click",()=>{a++;const t=new URL("./e2e-guest.html",l).href;n.loadGuest({id:`sb-custom-${a}`,url:t,title:"Sandbox Custom",targetElement:document.getElementById("sandboxContainer"),options:{fitToContent:!0,customSandboxValues:[ice.host.IFrameSandboxValues.AllowScripts,ice.host.IFrameSandboxValues.AllowSameOrigin]},onLoad:()=>{e("SB-02: Custom sandbox guest loaded","success");const o=document.querySelector("#sandboxContainer iframe:last-child");e(` sandbox="${o?.getAttribute("sandbox")||"(none)"}"`)}})}),document.getElementById("btnSandboxDisabled").addEventListener("click",()=>{a++;const t=new URL("./e2e-guest.html",l).href;n.loadGuest({id:`sb-disabled-${a}`,url:t,title:"Sandbox Disabled",targetElement:document.getElementById("sandboxContainer"),options:{fitToContent:!0,disableSandbox:!0},onLoad:()=>{e("SB-03: Disabled sandbox guest loaded","success");const r=document.querySelector("#sandboxContainer iframe:last-child")?.getAttribute("sandbox");e(` sandbox=${r===null?"(not set \u2014 correct)":`"${r}"`}`)}})}),setInterval(u,3e3),e("E2E Host ready","success")};window.addEventListener("DOMContentLoaded",f);
7
+
8
+ //# sourceMappingURL=e2e-host.js.map
Binary file
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["webpack://ice.host/e2e-host.js"],"sourcesContent":["import { Analytics } from './analytics-object-v2.js';\nimport { getGuestBaseUrl, getHost } from './utils.js';\n\nlet host = null;\nlet guestBaseUrl = null;\nlet guestCounter = 0;\n\n// --- Logging ---\nconst logEl = document.getElementById('eventLog');\nconst log = (msg, type = 'info') => {\n const ts = new Date().toLocaleTimeString();\n const color =\n type === 'error'\n ? 'text-red-600'\n : type === 'success'\n ? 'text-green-600'\n : type === 'warn'\n ? 'text-yellow-600'\n : 'text-gray-700';\n logEl.innerHTML += `<div class=\"${color}\">[${ts}] ${msg}</div>`;\n logEl.scrollTop = logEl.scrollHeight;\n};\n\ndocument.getElementById('btnClearLog').addEventListener('click', () => {\n logEl.innerHTML = '';\n});\n\nconst displayCallContext = (ctx) => {\n document.getElementById('callContextGuest').textContent = ctx?.guest\n ? JSON.stringify(ctx.guest, null, 2)\n : '(none — internal call)';\n document.getElementById('callContextChain').textContent = ctx?.callChain\n ? JSON.stringify(ctx.callChain, null, 2)\n : '(empty — direct call)';\n};\n\n// --- Scripting Objects ---\n\nclass Loan extends ice.host.ScriptingObject {\n constructor() {\n super('Loan');\n this.loanId = 'E2E-001';\n this.creditScore = 750;\n this.onPreSave = new ice.host.Event({\n name: 'onPreSave',\n objectId: 'Loan',\n });\n this.onAmountChanged = new ice.host.Event({\n name: 'onAmountChanged',\n objectId: 'Loan',\n });\n }\n\n getLoanDetails = () => {\n const ctx = this.getLoanDetails.callContext;\n log('Loan.getLoanDetails() invoked');\n displayCallContext(ctx);\n return {\n id: this.loanId,\n borrower: 'Jane Doe',\n amount: 500000,\n term: 30,\n creditScore: this.creditScore,\n };\n };\n\n setCreditScore = (score) => {\n const ctx = this.setCreditScore.callContext;\n log(`Loan.setCreditScore(${score}) invoked`);\n displayCallContext(ctx);\n this.creditScore = score;\n return { updated: true, score };\n };\n\n throwError = () => {\n log('Loan.throwError() invoked — will throw');\n throw new Error('Intentional error for TC-SO-08');\n };\n\n asyncMethod = async (delayMs) => {\n const ctx = this.asyncMethod.callContext;\n log(`Loan.asyncMethod(${delayMs}) invoked`);\n displayCallContext(ctx);\n await new Promise((resolve) => setTimeout(resolve, delayMs || 100));\n return { resolved: true, delay: delayMs };\n };\n}\n\nclass Application extends ice.host.ScriptingObject {\n constructor() {\n super('Application');\n }\n keepSessionAlive = () => Promise.resolve();\n}\n\nclass Inventory extends ice.host.ScriptingObject {\n constructor() {\n super('Inventory');\n }\n getItems = () => {\n log('Inventory.getItems() invoked');\n return [\n { id: 1, name: 'Widget A' },\n { id: 2, name: 'Widget B' },\n ];\n };\n}\n\n// --- Helper to load guest ---\nconst loadEmbedGuest = (id, opts = {}) => {\n const url = new URL('./e2e-guest.html', guestBaseUrl).href;\n const guest = host.loadGuest({\n id,\n url,\n title: `Guest ${id}`,\n targetElement: document.getElementById('embedContainer'),\n options: { fitToContent: true, ...opts.options },\n searchParams: opts.searchParams || {},\n metadata: opts.metadata,\n onLoad: (guestId) => log(`onLoad(\"${guestId}\")`, 'success'),\n onError: (guestId) => log(`onError(\"${guestId}\")`, 'error'),\n });\n log(`loadGuest(\"${id}\") called`);\n return guest;\n};\n\nconst refreshGuestList = () => {\n const guests = host?.getGuests() || [];\n document.getElementById('guestList').textContent = guests.length\n ? guests\n .map(\n (g) =>\n `${g.id} [${g.window?.closed ? 'closed' : 'open'}] ready=${\n g.ready\n }`,\n )\n .join('\\n')\n : '(no guests)';\n};\n\n// --- Init ---\nconst init = async () => {\n log('Initializing E2E Host...');\n\n const analyticsObj = new Analytics();\n host = getHost(analyticsObj);\n if (!host) {\n log('ERROR: Failed to create SSFHost', 'error');\n return;\n }\n\n guestBaseUrl = await getGuestBaseUrl();\n log(`Guest base URL: ${guestBaseUrl}`);\n\n // Register core scripting objects\n const loanObj = new Loan();\n const appObj = new Application();\n host.addScriptingObject(loanObj);\n host.addScriptingObject(appObj);\n log('Registered: Loan, Application');\n\n // --- Guest Loading ---\n\n document.getElementById('btnLoadEmbed').addEventListener('click', () => {\n guestCounter++;\n loadEmbedGuest(`embed-${guestCounter}`);\n });\n\n document.getElementById('btnLoadPopup').addEventListener('click', () => {\n const url = new URL('./e2e-guest.html', guestBaseUrl);\n url.searchParams.set('openMode', 'popup');\n try {\n host.loadGuest({\n id: 'popup-guest',\n url: url.href,\n title: 'Popup Guest',\n targetElement: document.getElementById('embedContainer'),\n options: { openMode: ice.host.OpenMode.Popup },\n onLoad: (id) => log(`Popup onLoad(\"${id}\")`, 'success'),\n onError: (id) => log(`Popup onError(\"${id}\")`, 'error'),\n });\n log('loadGuest(\"popup-guest\") as popup');\n } catch (e) {\n log(`Popup failed: ${e.message}`, 'error');\n }\n refreshGuestList();\n });\n\n document.getElementById('btnLoadMultiple').addEventListener('click', () => {\n guestCounter++;\n const url = new URL('./e2e-guest.html', guestBaseUrl).href;\n host.loadGuests({\n id: `multi-${guestCounter}`,\n url,\n title: `Multi Guest ${guestCounter}`,\n targetElement: document.getElementById('embedContainer'),\n options: { fitToContent: true },\n searchParamsList: [\n { instance: '1', mode: 'read' },\n { instance: '2', mode: 'write' },\n { instance: '3', mode: 'admin' },\n ],\n });\n log('loadGuests() with 3 instances');\n refreshGuestList();\n });\n\n document.getElementById('btnLoadWithParams').addEventListener('click', () => {\n guestCounter++;\n loadEmbedGuest(`params-${guestCounter}`, {\n searchParams: { env: 'qa', feature: 'sandbox-test', debug: true },\n });\n });\n\n document\n .getElementById('btnLoadWithMetadata')\n .addEventListener('click', () => {\n guestCounter++;\n loadEmbedGuest(`meta-${guestCounter}`, {\n metadata: { vendor: 'AcmeCorp', tier: 'premium', region: 'US-East' },\n });\n });\n\n document.getElementById('btnLoadBadUrl').addEventListener('click', () => {\n guestCounter++;\n host.loadGuest({\n id: `bad-${guestCounter}`,\n url: 'http://localhost:9999/does-not-exist.html',\n title: `Bad Guest ${guestCounter}`,\n targetElement: document.getElementById('embedContainer'),\n onLoad: (id) => log(`Unexpected onLoad(\"${id}\") for bad URL`, 'warn'),\n onError: (id) => log(`Expected onError(\"${id}\") for bad URL`, 'success'),\n });\n log('loadGuest() with invalid URL');\n });\n\n // --- Scripting Object Management ---\n\n let inventoryAdded = false;\n document.getElementById('btnAddSO').addEventListener('click', () => {\n if (inventoryAdded) {\n log('Inventory already added', 'warn');\n return;\n }\n host.addScriptingObject(new Inventory());\n inventoryAdded = true;\n log('Added \"Inventory\" scripting object', 'success');\n });\n\n document.getElementById('btnAddGuestSO').addEventListener('click', () => {\n const guests = host.getGuests();\n if (!guests.length) {\n log('No guests loaded — load a guest first', 'warn');\n return;\n }\n const guestId = guests[0].id;\n try {\n const scopedObj = new Inventory();\n scopedObj.id = 'GuestInventory';\n host.addScriptingObject(scopedObj, { guestId });\n log(`Added guest-scoped \"GuestInventory\" for ${guestId}`, 'success');\n } catch (e) {\n log(`addScriptingObject failed: ${e.message}`, 'error');\n }\n });\n\n document.getElementById('btnRemoveSO').addEventListener('click', () => {\n try {\n host.removeScriptingObject('Inventory');\n inventoryAdded = false;\n log('Removed \"Inventory\" scripting object', 'success');\n } catch (e) {\n log(`removeScriptingObject failed: ${e.message}`, 'error');\n }\n });\n\n document.getElementById('btnRemoveAllSO').addEventListener('click', () => {\n host.removeAllScriptingObjects();\n inventoryAdded = false;\n log('Removed all scripting objects', 'success');\n });\n\n // --- Event Dispatching ---\n\n document\n .getElementById('btnDispatchAll')\n .addEventListener('click', async () => {\n log('Dispatching onPreSave to all guests...');\n try {\n const results = await host.dispatchEvent({\n event: loanObj.onPreSave,\n eventParams: { creditScore: 750, amount: 500000 },\n });\n document.getElementById(\n 'dispatchResult',\n ).textContent = `onPreSave results:\\n${JSON.stringify(\n results,\n null,\n 2,\n )}`;\n log(`onPreSave dispatched, ${results.length} responses`, 'success');\n } catch (e) {\n log(`Dispatch failed: ${e.message}`, 'error');\n }\n });\n\n document\n .getElementById('btnDispatchTargeted')\n .addEventListener('click', async () => {\n const guests = host.getGuests();\n if (!guests.length) {\n log('No guests — load a guest first', 'warn');\n return;\n }\n const target = guests[0];\n log(`Dispatching onPreSave to guest \"${target.id}\" only...`);\n try {\n const results = await host.dispatchEvent({\n event: loanObj.onPreSave,\n eventParams: { creditScore: 800, amount: 250000 },\n eventOptions: { guestId: target.id },\n });\n document.getElementById(\n 'dispatchResult',\n ).textContent = `Targeted results (${target.id}):\\n${JSON.stringify(\n results,\n null,\n 2,\n )}`;\n log(`Targeted dispatch complete`, 'success');\n } catch (e) {\n log(`Dispatch failed: ${e.message}`, 'error');\n }\n });\n\n document\n .getElementById('btnDispatchFeedback')\n .addEventListener('click', async () => {\n log('Dispatching onPreSave with 3s timeout (feedback)...');\n try {\n const results = await host.dispatchEvent({\n event: loanObj.onPreSave,\n eventParams: { creditScore: 600, amount: 1000000 },\n eventOptions: { timeout: 3000 },\n });\n document.getElementById(\n 'dispatchResult',\n ).textContent = `Feedback results:\\n${JSON.stringify(\n results,\n null,\n 2,\n )}`;\n log(\n `Feedback dispatch complete, ${results.length} responses`,\n 'success',\n );\n } catch (e) {\n log(`Dispatch with feedback failed: ${e.message}`, 'error');\n }\n });\n\n document\n .getElementById('btnDispatchAmount')\n .addEventListener('click', async () => {\n log('Dispatching onAmountChanged (for criteria-based subscribers)...');\n try {\n const results = await host.dispatchEvent({\n event: loanObj.onAmountChanged,\n eventParams: { amount: 750000 },\n });\n document.getElementById(\n 'dispatchResult',\n ).textContent = `onAmountChanged results:\\n${JSON.stringify(\n results,\n null,\n 2,\n )}`;\n log('onAmountChanged dispatched', 'success');\n } catch (e) {\n log(`Dispatch failed: ${e.message}`, 'error');\n }\n });\n\n // --- Guest Lifecycle ---\n\n document.getElementById('btnUnloadGuest').addEventListener('click', () => {\n const guests = host.getGuests();\n const embedGuests = guests.filter(\n (g) => !g.window || g.window === window.frames[0]?.window,\n );\n if (!guests.length) {\n log('No guests to unload', 'warn');\n return;\n }\n const target = guests[0];\n log(`Unloading guest \"${target.id}\"...`);\n host.unloadGuest(target.id);\n log(`Unloaded \"${target.id}\"`, 'success');\n refreshGuestList();\n });\n\n document.getElementById('btnRegisterClose').addEventListener('click', () => {\n const guests = host.getGuests();\n if (!guests.length) {\n log('No guests — load a guest first', 'warn');\n return;\n }\n guests.forEach((g) => {\n host.onGuestClose({\n id: g.id,\n guestCloseCallback: ({ id }) => {\n log(`onGuestClose fired for \"${id}\"`, 'success');\n refreshGuestList();\n },\n });\n });\n log(`Registered onGuestClose for ${guests.length} guest(s)`, 'success');\n });\n\n document.getElementById('btnListGuests').addEventListener('click', () => {\n refreshGuestList();\n log(`Listed ${host.getGuests().length} guest(s)`);\n });\n\n document.getElementById('btnSetLogLevel').addEventListener('click', () => {\n host.setLogLevel(10);\n log('Set log level to 10 (debug)', 'success');\n });\n\n document.getElementById('btnCloseHost').addEventListener('click', () => {\n log('Closing host...');\n host.close();\n log('Host closed — all guests should be disconnected', 'success');\n refreshGuestList();\n });\n\n // --- Sandbox Configuration ---\n\n document.getElementById('btnSandboxDefault').addEventListener('click', () => {\n guestCounter++;\n const url = new URL('./e2e-guest.html', guestBaseUrl).href;\n host.loadGuest({\n id: `sb-default-${guestCounter}`,\n url,\n title: 'Sandbox Default',\n targetElement: document.getElementById('sandboxContainer'),\n options: { fitToContent: true },\n onLoad: () => {\n log('SB-01: Default sandbox guest loaded', 'success');\n const iframe = document.querySelector(\n '#sandboxContainer iframe:last-child',\n );\n log(` sandbox=\"${iframe?.getAttribute('sandbox') || '(none)'}\"`);\n },\n });\n });\n\n document.getElementById('btnSandboxCustom').addEventListener('click', () => {\n guestCounter++;\n const url = new URL('./e2e-guest.html', guestBaseUrl).href;\n host.loadGuest({\n id: `sb-custom-${guestCounter}`,\n url,\n title: 'Sandbox Custom',\n targetElement: document.getElementById('sandboxContainer'),\n options: {\n fitToContent: true,\n customSandboxValues: [\n ice.host.IFrameSandboxValues.AllowScripts,\n ice.host.IFrameSandboxValues.AllowSameOrigin,\n ],\n },\n onLoad: () => {\n log('SB-02: Custom sandbox guest loaded', 'success');\n const iframe = document.querySelector(\n '#sandboxContainer iframe:last-child',\n );\n log(` sandbox=\"${iframe?.getAttribute('sandbox') || '(none)'}\"`);\n },\n });\n });\n\n document\n .getElementById('btnSandboxDisabled')\n .addEventListener('click', () => {\n guestCounter++;\n const url = new URL('./e2e-guest.html', guestBaseUrl).href;\n host.loadGuest({\n id: `sb-disabled-${guestCounter}`,\n url,\n title: 'Sandbox Disabled',\n targetElement: document.getElementById('sandboxContainer'),\n options: { fitToContent: true, disableSandbox: true },\n onLoad: () => {\n log('SB-03: Disabled sandbox guest loaded', 'success');\n const iframe = document.querySelector(\n '#sandboxContainer iframe:last-child',\n );\n const sb = iframe?.getAttribute('sandbox');\n log(` sandbox=${sb === null ? '(not set — correct)' : `\"${sb}\"`}`);\n },\n });\n });\n\n // Periodic guest list refresh\n setInterval(refreshGuestList, 3000);\n\n log('E2E Host ready', 'success');\n};\n\nwindow.addEventListener('DOMContentLoaded', init);\n"],"mappings":"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","names":["Analytics","getGuestBaseUrl","getHost","host","guestBaseUrl","guestCounter","logEl","log","msg","type","ts","color","displayCallContext","ctx","Loan","score","delayMs","resolve","Application","Inventory","loadEmbedGuest","id","opts","url","guest","guestId","refreshGuestList","guests","g","init","analyticsObj","loanObj","appObj","e","inventoryAdded","scopedObj","results","target","embedGuests","iframe","sb"],"sourceRoot":"","file":"e2e-host.js"}
@@ -0,0 +1 @@
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>SSF E2E Test Suite</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script defer="defer" src="index.js"></script></head><body class="bg-gray-50"><header class="bg-gray-900 text-white px-6 py-4"><h1 class="text-xl font-bold">SSF End-to-End Test Suite</h1><p class="text-sm text-gray-400 mt-1">Comprehensive blackbox tests for SSF Host &amp; Guest. Each page includes test steps, expected values, and a verification checklist.</p></header><main class="mx-auto max-w-5xl px-6 py-6"><section class="mb-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4"><h2 class="text-sm font-semibold text-yellow-800 mb-2">Setup Instructions</h2><ol class="text-xs text-yellow-700 list-decimal ml-4 space-y-1"><li>Install dependencies: <code class="bg-yellow-100 px-1 rounded">pnpm install</code></li><li>Start SSF Host dev server: <code class="bg-yellow-100 px-1 rounded">pnpm nx run ssf-host:start-server</code> (port 4000)</li><li>Start SSF Guest dev server: <code class="bg-yellow-100 px-1 rounded">pnpm nx run ssf-guest:start</code> (port 4001)</li><li>Open <code class="bg-yellow-100 px-1 rounded">http://localhost:4000/e2e-index.html</code> in the browser</li><li>Each test page has <code class="bg-yellow-100 px-1 rounded">data-testid</code> attributes for Playwright/Cypress selectors</li></ol></section><section class="mb-6"><h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">1. Core Host-Guest Communication</h2><div class="grid grid-cols-1 md:grid-cols-2 gap-3"><a href="./e2e-host.html" data-testid="link-e2e-host" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">Comprehensive E2E Host</h3><p class="text-xs text-gray-500 mt-1">All host-guest scenarios: guest loading, scripting objects, events, lifecycle, metadata, sandbox, guest-scoped objects.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> Guests load in iframes/popups. Scripting objects can be added/removed/invoked. Events reach subscribed guests. Unload/close cleans up properly. Sandbox attrs applied.</p><div class="mt-2 text-xs text-gray-400"><strong>Test Cases:</strong> TC-LOAD-01..05 &bull; TC-SO-01..08 &bull; TC-EVT-01..04 &bull; TC-LIFE-01..03 &bull; TC-META-01 &bull; TC-SB-01..03</div></a><a href="./index.html" data-testid="link-main-demo" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">Main Demo App</h3><p class="text-xs text-gray-500 mt-1">Original Loan Application demo with embedded and popup guests, events, scripting objects.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> Loan form data flows to guests. Events (onLoanAmountChanged, etc.) update guest UIs. Popup guests open and interact with the Loan object.</p></a></div></section><section class="mb-6"><h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">2. Call Chain &amp; Metadata Propagation</h2><div class="grid grid-cols-1 md:grid-cols-2 gap-3"><a href="./callchain-host.html" data-testid="link-callchain" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">Nested CallChain Test (A → B → C)</h3><p class="text-xs text-gray-500 mt-1">Root Host (A) → Intermediate Guest+Host (B) → Grandchild Guest (C). Verifies callContext.callChain and metadata propagation.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> Click buttons in Grandchild C. Host A should show <code>callContext.guest</code> = B and <code>callChain</code> containing C and B with their metadata. Return values should flow back to C.</p><div class="mt-2 text-xs text-gray-400"><strong>Test Cases:</strong> TC-CC-01..07</div></a></div></section><section class="mb-6"><h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">3. Popup Guest Behavior</h2><div class="grid grid-cols-1 md:grid-cols-2 gap-3"><a href="./popup-focus-host.html" data-testid="link-popup-focus" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">Popup Focus &amp; Lifecycle</h3><p class="text-xs text-gray-500 mt-1">Open, close, and refocus popup guests. Tests trusted vs untrusted domain behavior.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> Trusted popups (localhost): re-open brings existing popup to front. Untrusted popups: opener is nulled, focus falls back to WindowProxy. Closed popup detection updates guest status.</p><div class="mt-2 text-xs text-gray-400"><strong>Test Cases:</strong> TC-POP-01..11</div></a></div></section><section class="mb-6"><h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">4. V1 / V2 Interoperability</h2><div class="grid grid-cols-1 md:grid-cols-2 gap-3"><a href="./v2-host-v1-guest.html" data-testid="link-v2-host-v1-guest" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">V2 Host → V1 Guest</h3><p class="text-xs text-gray-500 mt-1">V2 host loads V1 guest. Loan object, save flow, event feedback.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> V1 guest connects, gets objects, subscribes to events, and returns feedback correctly.</p><div class="mt-2 text-xs text-gray-400"><strong>Test Cases:</strong> TC-INTEROP-01..04</div></a><a href="./v2-host-v1-guest.html?nestV1GuestV2Host=true" data-testid="link-v2-v1-v2-chain" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">V2 Host → V1 Guest → V2 Host → V2 Guest</h3><p class="text-xs text-gray-500 mt-1">Full mixed-version chain. V1 guest acts as intermediate host for a V2 grandchild.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> Scripting objects are cloned across V1/V2 boundary. Events flow through all layers.</p><div class="mt-2 text-xs text-gray-400"><strong>Test Cases:</strong> TC-INTEROP-05..07</div></a></div><div class="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3"><a href="./v1-host.html" data-testid="link-v1-host" class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"><h3 class="font-medium text-indigo-700">V1 Host → V1 Guest → V2 Host → V2 Guest</h3><p class="text-xs text-gray-500 mt-1">Starting from V1 host. Exercises the full V1-to-V2 upgrade path.</p><p class="text-xs text-gray-400 mt-2"><strong>What to verify:</strong> V1 host initializes. V1 guest connects. V2 host/guest chain works through V1 intermediate.</p></a></div></section></main></body></html>
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Host</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script defer="defer" src="index.js"></script></head><body><header class="bg-indigo-300 h-10 flex place-items-center"><div class="px-2">ICE Mortgage Product</div></header><main class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"><div class="min-w-0 flex-1 mt-4"><h1 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">Loan Application</h1></div><div id="successFeedback" class="hidden rounded-md bg-green-50 p-4"><div class="flex"><div class="flex-shrink-0"><svg class="h-5 w-5 text-green-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd"/></svg></div><div class="ml-3"><p class="text-sm font-medium text-green-800">Loan Saved Successfully</p></div></div></div><div id="errorFeedback" class="hidden rounded-md bg-red-50 p-4"><div class="flex"><div class="flex-shrink-0"><svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd"/></svg></div><div class="ml-3"><h3 class="text-sm font-medium text-red-800">Credit Score is not meeting the requirement</h3></div></div></div><div class="mt-2 sm:grid sm:grid-cols-2 sm:gap-2"><form class="px-2 py-2 space-y-8 divide-y divide-gray-200 bg-gray-50"><div class="space-y-8 divide-y divide-gray-200 sm:space-y-5"><div class="space-y-6 sm:space-y-5"><div><h3 class="text-lg font-medium leading-6 text-gray-900">Personal Information</h3></div><div class="space-y-6 sm:space-y-5"><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="firstName" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">First name</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input name="firstName" id="firstName" autocomplete="given-name" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="John" placeholder="John"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="lastName" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Last name</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input name="lastName" id="lastName" autocomplete="family-name" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="Doe" placeholder="Doe"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="ssn" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">SSN</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="ssn" id="ssn" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="123456789" placeholder="123456789"/></div></div></div><div><h3 class="text-lg font-medium leading-6 text-gray-900">Loan Information</h3></div><div class="space-y-6 sm:space-y-5"><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="amount" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Amount</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="amount" id="amount" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="500000" placeholder="500000"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="Term" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Term (years)</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="term" id="term" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="30" placeholder="30"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="downPayment" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Down Payment</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="downPayment" id="downPayment" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="50000" placeholder="50000"/></div></div><div><h3 class="text-lg font-medium leading-6 text-gray-900">Order Services</h3></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><div class="mt-1 sm:mt-0"><button id="title" type="button" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed focus:ring-offset-2"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M7.502 6h7.128A3.375 3.375 0 0118 9.375v9.375a3 3 0 003-3V6.108c0-1.505-1.125-2.811-2.664-2.94a48.972 48.972 0 00-.673-.05A3 3 0 0015 1.5h-1.5a3 3 0 00-2.663 1.618c-.225.015-.45.032-.673.05C8.662 3.295 7.554 4.542 7.502 6zM13.5 3A1.5 1.5 0 0012 4.5h4.5A1.5 1.5 0 0015 3h-1.5z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M3 9.375C3 8.339 3.84 7.5 4.875 7.5h9.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-9.75A1.875 1.875 0 013 20.625V9.375zM6 12a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H6.75a.75.75 0 01-.75-.75V12zm2.25 0a.75.75 0 01.75-.75h3.75a.75.75 0 010 1.5H9a.75.75 0 01-.75-.75zM6 15a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H6.75a.75.75 0 01-.75-.75V15zm2.25 0a.75.75 0 01.75-.75h3.75a.75.75 0 010 1.5H9a.75.75 0 01-.75-.75zM6 18a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H6.75a.75.75 0 01-.75-.75V18zm2.25 0a.75.75 0 01.75-.75h3.75a.75.75 0 010 1.5H9a.75.75 0 01-.75-.75z" clip-rule="evenodd"/></svg> Title</button></div><div class="mt-1 sm:mt-0"><button id="credit" type="button" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed focus:ring-offset-2"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M2.25 13.5a8.25 8.25 0 018.25-8.25.75.75 0 01.75.75v6.75H18a.75.75 0 01.75.75 8.25 8.25 0 01-16.5 0z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M12.75 3a.75.75 0 01.75-.75 8.25 8.25 0 018.25 8.25.75.75 0 01-.75.75h-7.5a.75.75 0 01-.75-.75V3z" clip-rule="evenodd"/></svg> Credit Score</button></div></div></div></div></div><div class="flex flex-col"><button id="saveLoan" type="button" class="rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">Save</button></div></form><div id="aside-container" class="flex flex-col gap-4 items-start mt-4 border-2 p-2 rounded-lg border-dashed border-cyan-300 sm:mt-0"></div></div><div id="bottom-container" class="flex flex-col gap-4 items-start mt-4 p-2 sm:mt-0"></div></main><script src="./init.js" type="module"></script></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Host</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script defer="defer" src="index.js"></script></head><body><header class="bg-indigo-300 h-10 flex place-items-center justify-between"><div class="px-2">ICE Mortgage Product</div><nav class="flex gap-3 px-2 text-xs"><a href="./e2e-index.html" data-testid="nav-e2e-suite" class="text-indigo-800 hover:text-indigo-950 underline font-bold">E2E Test Suite</a> <a href="./e2e-host.html" data-testid="nav-e2e-host" class="text-indigo-800 hover:text-indigo-950 underline">Comprehensive E2E</a> <a href="./callchain-host.html" data-testid="nav-callchain-test" class="text-indigo-800 hover:text-indigo-950 underline">CallChain</a> <a href="./popup-focus-host.html" data-testid="nav-popup-focus-test" class="text-indigo-800 hover:text-indigo-950 underline">Popup Focus</a> <a href="./v2-host-v1-guest.html" data-testid="nav-v1-interop" class="text-indigo-800 hover:text-indigo-950 underline">V1 Interop</a></nav></header><main class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"><div class="min-w-0 flex-1 mt-4"><h1 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">Loan Application</h1></div><div id="successFeedback" class="hidden rounded-md bg-green-50 p-4"><div class="flex"><div class="flex-shrink-0"><svg class="h-5 w-5 text-green-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd"/></svg></div><div class="ml-3"><p class="text-sm font-medium text-green-800">Loan Saved Successfully</p></div></div></div><div id="errorFeedback" class="hidden rounded-md bg-red-50 p-4"><div class="flex"><div class="flex-shrink-0"><svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd"/></svg></div><div class="ml-3"><h3 class="text-sm font-medium text-red-800">Credit Score is not meeting the requirement</h3></div></div></div><div class="mt-2 sm:grid sm:grid-cols-2 sm:gap-2"><form class="px-2 py-2 space-y-8 divide-y divide-gray-200 bg-gray-50"><div class="space-y-8 divide-y divide-gray-200 sm:space-y-5"><div class="space-y-6 sm:space-y-5"><div><h3 class="text-lg font-medium leading-6 text-gray-900">Personal Information</h3></div><div class="space-y-6 sm:space-y-5"><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="firstName" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">First name</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input name="firstName" id="firstName" autocomplete="given-name" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="John" placeholder="John"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="lastName" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Last name</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input name="lastName" id="lastName" autocomplete="family-name" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="Doe" placeholder="Doe"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="ssn" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">SSN</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="ssn" id="ssn" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="123456789" placeholder="123456789"/></div></div></div><div><h3 class="text-lg font-medium leading-6 text-gray-900">Loan Information</h3></div><div class="space-y-6 sm:space-y-5"><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="amount" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Amount</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="amount" id="amount" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="500000" placeholder="500000"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="Term" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Term (years)</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="term" id="term" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="30" placeholder="30"/></div></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><label for="downPayment" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2">Down Payment</label><div class="mt-1 sm:col-span-2 sm:mt-0"><input type="number" name="downPayment" id="downPayment" class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:max-w-xs sm:text-sm" value="50000" placeholder="50000"/></div></div><div><h3 class="text-lg font-medium leading-6 text-gray-900">Order Services</h3></div><div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-gray-200"><div class="mt-1 sm:mt-0"><button id="title" type="button" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed focus:ring-offset-2"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M7.502 6h7.128A3.375 3.375 0 0118 9.375v9.375a3 3 0 003-3V6.108c0-1.505-1.125-2.811-2.664-2.94a48.972 48.972 0 00-.673-.05A3 3 0 0015 1.5h-1.5a3 3 0 00-2.663 1.618c-.225.015-.45.032-.673.05C8.662 3.295 7.554 4.542 7.502 6zM13.5 3A1.5 1.5 0 0012 4.5h4.5A1.5 1.5 0 0015 3h-1.5z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M3 9.375C3 8.339 3.84 7.5 4.875 7.5h9.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-9.75A1.875 1.875 0 013 20.625V9.375zM6 12a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H6.75a.75.75 0 01-.75-.75V12zm2.25 0a.75.75 0 01.75-.75h3.75a.75.75 0 010 1.5H9a.75.75 0 01-.75-.75zM6 15a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H6.75a.75.75 0 01-.75-.75V15zm2.25 0a.75.75 0 01.75-.75h3.75a.75.75 0 010 1.5H9a.75.75 0 01-.75-.75zM6 18a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H6.75a.75.75 0 01-.75-.75V18zm2.25 0a.75.75 0 01.75-.75h3.75a.75.75 0 010 1.5H9a.75.75 0 01-.75-.75z" clip-rule="evenodd"/></svg> Title</button></div><div class="mt-1 sm:mt-0"><button id="credit" type="button" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed focus:ring-offset-2"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M2.25 13.5a8.25 8.25 0 018.25-8.25.75.75 0 01.75.75v6.75H18a.75.75 0 01.75.75 8.25 8.25 0 01-16.5 0z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M12.75 3a.75.75 0 01.75-.75 8.25 8.25 0 018.25 8.25.75.75 0 01-.75.75h-7.5a.75.75 0 01-.75-.75V3z" clip-rule="evenodd"/></svg> Credit Score</button></div></div></div></div></div><div class="flex flex-col"><button id="saveLoan" type="button" class="rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">Save</button></div></form><div id="aside-container" class="flex flex-col gap-4 items-start mt-4 border-2 p-2 rounded-lg border-dashed border-cyan-300 sm:mt-0"></div></div><div id="bottom-container" class="flex flex-col gap-4 items-start mt-4 p-2 sm:mt-0"></div></main><script src="./init.js" type="module"></script></body></html>
package/dist/umd/index.js CHANGED
@@ -1,3 +1,3 @@
1
- (function(I,O){typeof exports=="object"&&typeof module=="object"?module.exports=O():typeof define=="function"&&define.amd?define([],O):typeof exports=="object"?exports.ice=O():(I.ice=I.ice||{},I.ice.host=O())})(globalThis,()=>(()=>{"use strict";var j={};j.d=(n,e)=>{for(var t in e)j.o(e,t)&&!j.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:e[t]})},j.o=(n,e)=>Object.prototype.hasOwnProperty.call(n,e),j.r=n=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})};var I={};j.r(I),j.d(I,{Event:()=>O,Guest:()=>q,IFrameSandboxValues:()=>S,OpenMode:()=>f,SANDBOX_DEFAULT:()=>N,SSFHost:()=>ie,ScriptingObject:()=>U});class O{name;objectId;id;constructor(e){const{name:t,objectId:i}=e;if(!t)throw new Error("Event name is required");if(!i)throw new Error("Scripting object id is required");this.objectId=i,this.name=t,this.id=`${this.objectId}.${this.name}`.toLowerCase()}}class A{static[Symbol.hasInstance](e){return typeof e=="object"&&e!==null&&"getType"in e&&typeof e.getType=="function"&&e.getType()==="ProxyEvent"}#t;objectId;name;id;getType(){return"ProxyEvent"}constructor(e){const{name:t,objectId:i,eventSrc:s}=e;if(!t)throw new Error("Event name is required");if(!i)throw new Error("Scripting object id is required");if(!s)throw new Error("Event source is required");this.objectId=i,this.name=t,this.#t=s,this.id=`${this.objectId}.${this.name}`.toLowerCase()}subscribe=e=>this.#t.subscribe({eventId:this.id,callback:e});unsubscribe=e=>{this.#t.unsubscribe({eventId:this.id,token:e})}}const W=n=>n instanceof O,se=(n,e)=>`${n.toLowerCase()}.${e.toLowerCase()}`,H="function",F=(n,e)=>typeof n===H&&!!e&&!e.startsWith("_");class U{#t;#i="Object";constructor(e,t){this.#t=e,this.#i=t||this.#i}get id(){return this.#t}get objectType(){return this.#i}_toJSON=()=>{const e=[],t=[];return Object.keys(this).forEach(i=>{const s=this[i];W(s)?t.push(i):F(s,i)&&e.push(i)}),{objectId:this.#t,objectType:this.#i,functions:e,events:t}};_dispose=()=>{};dispose=()=>{}}var l=(n=>(n.GuestClose="guest:close",n.GuestEventSubscribe="guest:eventSubscribe",n.GuestEventUnsubscribe="guest:eventUnsubscribe",n.GuestFocus="guest:focus",n.GuestReady="guest:ready",n.GuestReadyComplete="guest:readyComplete",n.GuestResize="guest:resize",n.HandShake="handshake",n.HandShakeAck="handshake:ack",n.HostClose="host:close",n.HostConfig="host:config",n.ListObjects="list:objects",n.ObjectEvent="object:event",n.ObjectGet="object:get",n.ObjectInvoke="object:invoke",n))(l||{}),f=(n=>(n.Popup="popup",n.Embed="embed",n))(f||{}),S=(n=>(n.AllowDownloadsWithoutUserActivation="allow-downloads-without-user-activation",n.AllowDownloads="allow-downloads",n.AllowForms="allow-forms",n.AllowModals="allow-modals",n.AllowOrientationLock="allow-orientation-lock",n.AllowPointerLock="allow-pointer-lock",n.AllowPopups="allow-popups",n.AllowPopupsToEscapeSandbox="allow-popups-to-escape-sandbox",n.AllowPresentation="allow-presentation",n.AllowSameOrigin="allow-same-origin",n.AllowScripts="allow-scripts",n.AllowStorageAccessByUserActivation="allow-storage-access-by-user-activation",n.AllowTopNavigation="allow-top-navigation",n.AllowTopNavigationByUserActivation="allow-top-navigation-by-user-activation",n))(S||{});const J=n=>{if(n==="about:blank")return"*";try{const{origin:e}=new URL(n);return e==="null"||!e?n:e}catch{const{origin:t}=new URL(n,document.baseURI);return t}},Y=n=>n.flat(1/0).filter(e=>e!==void 0);function R(n){return typeof n=="function"}class q{id;title;url;searchParams;domElement;window;openMode;origin;initialized=!1;ready=!1;capabilities;#t;#i;constructor(e){const{guestId:t,domElement:i=null,title:s,url:o,window:r,searchParams:c={},openMode:a=f.Embed,remoting:d,analyticsObj:u}=e;this.id=t,this.title=s,this.url=o,this.origin=J(o),this.searchParams=c,this.domElement=i,this.window=r,this.openMode=a,this.capabilities={},this.#i=u,this.#t=d}dispose=()=>{if(this.openMode===f.Popup&&!this.window.closed)try{this.window.document,this.window.close()}catch{this.#t.send({targetWin:this.window,targetOrigin:this.origin,messageType:l.HostClose,messageBody:{}})}else this.domElement?.remove?.();this.#t.removeSender({origin:this.origin,window:this.window})};getInfo=()=>({guestId:this.id,guestTitle:this.title,guestUrl:this.url});handShake=()=>new Promise(e=>{let t=0;const i=5;let s;const o=()=>{clearInterval(s),this.#t.unlisten({messageType:l.HandShakeAck,callback:o}),e(!0)};s=setInterval(()=>{t>=i?(clearInterval(s),this.#t.unlisten({messageType:l.HandShakeAck,callback:o}),e(!1)):(this.#t.send({targetWin:this.window,targetOrigin:this.origin,messageType:l.HandShake,messageBody:{}}),t+=1)},1e3),this.#t.listen({messageType:l.HandShakeAck,callback:o})});init=()=>{this.#t.addSender({origin:this.origin,window:this.window}),this.openMode===f.Popup&&this.handShake().catch(()=>{})};dispatchEvent=(e,t)=>(this.#i.startTiming(`ScriptingObject.Event.${e.object.objectId}.${e.eventName}`,{appId:this.id,appUrl:this.url}).catch(()=>{}),this.#t.invoke({targetWin:this.window,targetOrigin:this.origin,messageType:l.ObjectEvent,messageBody:e,responseTimeoutMs:t}).finally(()=>{this.#i.endTiming(`ScriptingObject.Event.${e.object.objectId}.${e.eventName}`,{appId:this.id,appUrl:this.url}).catch(()=>{})}));send=e=>{this.#t.send({targetWin:this.window,targetOrigin:this.origin,...e})}}const _={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};let P;const X=new Uint8Array(16);function K(){if(!P&&(P=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!P))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return P(X)}var Q;const h=[];for(let n=0;n<256;++n)h.push((n+256).toString(16).slice(1));function L(n,e=0){return h[n[e+0]]+h[n[e+1]]+h[n[e+2]]+h[n[e+3]]+"-"+h[n[e+4]]+h[n[e+5]]+"-"+h[n[e+6]]+h[n[e+7]]+"-"+h[n[e+8]]+h[n[e+9]]+"-"+h[n[e+10]]+h[n[e+11]]+h[n[e+12]]+h[n[e+13]]+h[n[e+14]]+h[n[e+15]]}function oe(n,e=0){const t=L(n,e);if(!Q(t))throw TypeError("Stringified UUID is invalid");return t}const re=null;function Z(n,e,t){if(_.randomUUID&&!e&&!n)return _.randomUUID();n=n||{};const i=n.random||(n.rng||K)();if(i[6]=i[6]&15|64,i[8]=i[8]&63|128,e){t=t||0;for(let s=0;s<16;++s)e[t+s]=i[s];return e}return L(i)}const M=Z,C="elli:remoting",x="elli:remoting:response",D="elli:remoting:exception",$=({messageType:n,messageBody:e,requestId:t,onewayMsg:i=!1})=>({requestId:t??(i?null:M()),source:C,type:n,body:e}),ce=n=>{const{targetWin:e,targetOrigin:t,messageType:i,messageBody:s}=n,o=$({messageType:i,messageBody:s});e.postMessage(o,t)};class V{#t;#i;#e=new Map;#n=new Map;#s=null;#o=null;#r=new Map;constructor(e,t){if(!e)throw new Error("logger is required");if(!t)throw new Error("correlationId is required");this.#t=t,this.#i=e}#l=()=>{this.#s=null;const e=Date.now(),t=[];let i=null;if(this.#n.forEach((s,o)=>{const{requestId:r,cancelTime:c}=s;c&&c<=e?(this.#i.debug(`Detected response timeout for requestId: ${r}...`),t.push(o),s.resolve(void 0)):c&&(i=i===null?c:Math.min(i,c))}),t.forEach(s=>{this.#n.delete(s)}),i!==null){const s=Math.max(i-Date.now(),0);this.#c(s)}};#c=e=>{this.#s===null&&(this.#s=window.setTimeout(this.#l,e))};#h=()=>{this.#s!==null&&(window.clearTimeout(this.#s),this.#s=null)};#a=e=>{const t=this.#n.get(e);return this.#i.debug(`serving requestId: ${e}`),this.#n.delete(e),t};#g=e=>{const{requestId:t}=e;this.#i.debug(`Response received for invocation requestId: ${t}`);const i=this.#a(t);return i?(i.resolve(e.body),!0):(this.#i.debug(`Received response to stale/invalid request with requestId: ${t}`),!1)};#p=e=>{this.#i.debug(`Exception received for invocation (requestId = ${e.requestId})`);const t=this.#a(e.requestId);return t?(t.reject(new Error(e.body)),!0):(this.#i.warn(`Received exception for stale/invalid request (requestId = ${e.requestId})`),!1)};#m=({sourceWin:e,sourceOrigin:t,message:i})=>{this.#i.debug(`Received message of type "${i.type}"`);const s=this.#e.get(i.type);return s?(s.forEach(o=>{this.#i.debug(`Invoking message handler ${o.name}`),o({sourceWin:e,sourceOrigin:t,requestId:i.requestId,type:i.type,body:i.body})}),!0):!1};#u=e=>{if(this.#r.size===0||!e.source)return!1;const t=this.#r.get(e.source);return!t||e?.data?.source!==C?!1:(this.#i.debug(`Remoting: Received message of type "${e.data.type}"`),e.data.type===x?this.#g(e.data):e.data.type===D?this.#p(e.data):this.#m({sourceWin:e.source,sourceOrigin:t,message:e.data}),!0)};addSender=e=>{const{origin:t,window:i}=e;if(!t)throw new Error("origin is required");if(!i)throw new Error("window is required");this.#r.set(i,t)};initialize=e=>{this.#o&&this.#o.removeEventListener("message",this.#u),e.addEventListener("message",this.#u),this.#o=e,this.#i.debug(`initialized remoting id: ${this.#t}`)};close=()=>{this.#o&&(this.#o.removeEventListener("message",this.#u),this.#o=null),this.#h(),this.#i.debug(`closed remoting id: ${this.#t}`)};invoke=e=>{const{targetWin:t,targetOrigin:i,messageType:s,messageBody:o,responseTimeoutMs:r}=e;return new Promise((c,a)=>{const d=$({messageType:s,messageBody:o});this.#n.set(d.requestId,{requestId:d.requestId,resolve:c,reject:a,cancelTime:r?Date.now()+r:null}),t.postMessage(d,i);const{requestId:u}=d;this.#i.debug(`Posted invocation message of type ${s} requestId: ${u||""}`),r&&(this.#i.debug(`scheduling timeout check for requestId: ${u||""} in ${r} ms`),this.#c(r))})};listen=e=>{const{messageType:t,callback:i}=e,s=this.#e.get(t)||[];s.push(i),this.#e.set(t,s)};unlisten=e=>{const{messageType:t,callback:i}=e,s=this.#e.get(t);if(!s)return;const o=s.indexOf(i);o!==-1&&s.splice(o,1)};send=e=>{const{targetWin:t,targetOrigin:i,messageType:s,messageBody:o}=e,r=$({messageType:s,messageBody:o,onewayMsg:!0});t.postMessage(r,i),this.#i.debug(`Posted one-way message of type "${s}"`)};removeSender=e=>{const{window:t}=e;t&&this.#r.delete(t)};respond=e=>{const{targetWin:t,targetOrigin:i,requestId:s,response:o}=e,r=$({messageType:x,messageBody:o,requestId:s});t.postMessage(r,i),this.#i.debug(`Response sent to caller for invocation requestId: ${s}`)};raiseException=e=>{const{targetWin:t,targetOrigin:i,requestId:s,ex:o}=e,r=o instanceof Error?o.message:o,c=$({messageType:D,messageBody:r,requestId:s});t.postMessage(c,i),this.#i.debug(`Exception sent to caller for invocation. requestId: ${s}`)}}const z="module";var ee=(n=>(n.USER="USER",n.PARTNER="PARTNER",n))(ee||{});class te{#t=e=>e.trim().toLowerCase();#i=new Map;#e=new Map;#n=e=>{const{so:t,guestId:i}=e,s=this.#t(t.id),o=this.#e.get(i);if(!o)this.#e.set(i,new Map([[s,e]]));else{if(o.has(s))throw new Error(`Scripting Object ${t.id} already exists for guest ${i}`);o.set(s,e)}};#s=({so:e})=>{if(e._dispose&&typeof e._dispose=="function")try{e._dispose()}catch{}};#o=({objectId:e,guestId:t})=>{if(e===z&&!t)for(const[,s]of this.#e){const o=s.get(e);if(o)return o}const i=t?this.#e.get(t):null;return i?i.get(e)??null:null};#r=({objectId:e,guestId:t}={})=>{if(t){if(!e){const s=this.#e.get(t);s&&s.forEach(this.#s),this.#e.delete(t);return}const i=this.#e.get(t);if(i){const s=i.get(e);s&&this.#s(s),i.delete(e)}}else e&&this.#e.forEach(i=>{const s=i.get(e);s&&this.#s(s),i.delete(e)})};addScriptingObject=(e,t)=>{const{guestId:i}=t||{};if(!e?.id||!e?._toJSON)throw new Error("Object is not derived from ScriptingObject");const s=this.#t(e.id);if(s===z&&!i)throw new Error("Guest id is required to add Module scripting object");if(i){this.#n({so:e,...t,guestId:i});return}if(this.#i.has(s))throw new Error(`Scripting Object ${e.id} already exists`);this.#i.set(s,{so:e,...t})};getObject=(e,t)=>{const i=this.#t(e);let s=this.#o({objectId:i,guestId:t?.id});s=s??this.#i.get(i)??null;const{so:o}=s||{};return o||null};has=(e,t)=>this.getObject(e,t)!==null;listScriptingObjects=e=>{const t=new Set(this.#i.keys()),i=this.#e.get(e);if(i)for(const s of i.keys())t.add(s);return Array.from(t)};removeScriptingObject=(e,t)=>{const i=this.#t(e);if(t)this.#r({objectId:i,guestId:t});else{this.#r({objectId:i});const s=this.#i.get(i);s&&this.#s(s),this.#i.delete(i)}};removeAllScriptingObjects=e=>{e?this.#r({guestId:e}):(this.#i.forEach(this.#s),this.#i.clear())}}class ae{__TYPE__="Proxy";id;objectType;constructor(e,t){this.id=e,this.objectType=t}}const B=n=>n?.constructor?.name==="Proxy"||n?.constructor?.name==="ScriptingObjectProxy"||n?.__TYPE__==="Proxy",N=[S.AllowScripts,S.AllowPopups,S.AllowModals,S.AllowForms,S.AllowDownloads,S.AllowSameOrigin];class ie{hostId;#t;#i;#e;#n;#s=new Map;#o=new Map;#r=new Map;#l=new Map;#c;#h=null;#a=null;#g=null;#p=null;constructor(e,t){if(this.hostId=e,!t?.logger)throw new Error("Logger is required");if(!t?.analyticsObj)throw new Error("Analytics object is required");if(this.#e=t.logger,this.#n=t.analyticsObj,this.#i=M(),this.#t=new V(this.#e,this.#i),t?.readyStateCallback&&typeof t?.readyStateCallback!="function")throw new Error("readyStateCallback must be a function");this.#h=t?.readyStateCallback||null,this.#g=t?.onGuestEventSubscribe||null,this.#p=t?.onGuestEventUnsubscribe||null,this.#c=new te,this.#t.initialize(window),this.#_(),window.addEventListener("beforeunload",this.#f),this.#e.debug(`host is initialized. hostId: ${this.hostId}, correlationId: ${this.#i}`)}#m=(e,t)=>{const i={event:e,...t};this.#n.sendBAEvent(i).catch(s=>{this.#e.debug(`Analytics sendBAEvent failed: ${s.message}`)})};#u=(e,t)=>{this.#n.startTiming(e,t).catch(i=>{this.#e.debug(`Analytics startTiming failed: ${i.message}`)})};#b=(e,t)=>{this.#n.endTiming(e,t).catch(i=>{this.#e.debug(`Analytics endTiming failed: ${i.message}`)})};#f=()=>{Array.from(this.#s.values()).filter(t=>t.openMode===f.Popup).map(t=>t.id).forEach(t=>this.unloadGuest(t))};#d=e=>this.#o.get(e);#O=e=>this.#r.get(e)??null;#S=e=>typeof e?._toJSON=="function";#v=(e,t)=>this.#S(e)?(this.#c.has(e?.id,t)||this.#c.addScriptingObject(e,t?{guestId:t?.id}:{}),{type:"object",object:e._toJSON()}):{type:"value",value:e};#B=e=>typeof e=="string"?e:e instanceof Error?e.message:"An unexpected error occurred in the host application";#y=e=>{e.ready&&this.#t.send({targetWin:e.window,targetOrigin:e.origin,messageType:l.HostConfig,messageBody:{logLevel:this.#e.getLogLevel(),...e.getInfo()}})};#I=({guest:e,obj:t,functionName:i,functionParams:s})=>{const o=t[i];return R(o)?(this.#e.debug(`Invoking host implementation of ${t.id}.${String(i)}()`),new Promise(r=>{Object.defineProperty(o,"callContext",{value:{guest:e},configurable:!0,enumerable:!0,writable:!0}),r(o(...s))})):(this.#e.warn(`Attempt to call invalid function on object type ${t.objectType}: ${String(i)}`),Promise.reject(new Error(`Method '${i}' not found in Scripting Object '${t.id}'`)))};#E=({sourceWin:e,sourceOrigin:t,requestId:i})=>{const s=this.#d(e);if(!s){this.#e.warn(`Received ready event for unknown guest. requestId: ${i}`);return}if(!s.initialized){this.#e.warn("Guest must be initialized before it is marked as ready"),this.#t.raiseException({targetWin:e,targetOrigin:t,requestId:i,ex:"Guest must be initialized before it is marked as ready"});return}if(!s.ready){s.ready=!0;const o=s.getInfo();this.#b("SSF.Guest.Load",{appId:o.guestId,appUrl:o.guestUrl}),this.#y(s),this.#h?.(s),this.#e.audit({message:"Guest is ready",...o})}};#$=({sourceWin:e,sourceOrigin:t,requestId:i,body:s})=>{const o=this.#d(e);if(!o){this.#e.warn(`Received ready event for unknown guest. requestid = ${i}`);return}o.initialized||(o.initialized=!0,o.capabilities=s||{},this.#e.audit({message:"Guest is initialized",...o.getInfo()})),(!s||!s.onReady)&&this.#E({sourceWin:e,sourceOrigin:t,requestId:i,type:"",body:null})};#k=async({sourceWin:e})=>{if(e?.window){const t=this.#w(e);t&&!await t.handShake()&&this.unloadGuest(e)}};#P=({sourceWin:e,sourceOrigin:t,requestId:i})=>{this.#e.debug(`Processing listObjects request. requestId = ${i}`);const s=this.#d(e);if(!s)return this.#e.warn("Rejected object request from unknown guest window"),!1;const o=this.#c.listScriptingObjects(s.id);return this.#t.respond({targetWin:e,targetOrigin:t,requestId:i,response:o}),this.#e.debug({message:"name of scripting objects returned",requestId:i,objects:o,...s.getInfo()}),!0};#T=({sourceWin:e,sourceOrigin:t,requestId:i,body:s})=>{const{objectId:o}=s;this.#e.debug(`Processing getObject request for object ${o}. requestId = ${i}`);const r=this.#d(e);if(!r)return this.#e.warn("Rejected object request from unknown guest window"),!1;const c=this.getScriptingObject(o,{guest:r});return c?(this.#t.respond({targetWin:e,targetOrigin:t,requestId:i,response:this.#v(c,r)}),this.#e.debug({message:"Scripting Object returned",requestId:i,scriptingObject:o,...r.getInfo()}),!0):(this.#e.warn(`unknown or unauthorized object ${o} from guest ${r.id}`),this.#t.raiseException({targetWin:e,targetOrigin:t,requestId:i,ex:`The requested object (${o}) is not available`}),!1)};#G=({sourceWin:e,requestId:t,body:i})=>{const{eventId:s,criteria:o,token:r}=i;this.#e.debug(`Processing guest event subscribe request for event ${s}. requestId = ${t}`);const c=this.#d(e);if(!c){this.#e.warn("Rejected event subscribe request from unknown guest window");return}setTimeout(()=>{try{this.#g?.({guestId:c.id,eventId:s,criteria:o,token:r})}catch(a){this.#e.warn(`Error in onGuestEventSubscribe callback for event ${s}: ${a.message}`)}},0)};#A=({sourceWin:e,requestId:t,body:i})=>{const{eventId:s,token:o}=i;this.#e.debug(`Processing guest event unsubscribe request for event ${s}. requestId = ${t}`);const r=this.#d(e);if(!r){this.#e.warn("Rejected event unsubscribe request from unknown guest window");return}setTimeout(()=>{try{this.#p?.({guestId:r.id,eventId:s,token:o})}catch(c){this.#e.warn(`Error in onGuestEventUnsubscribe callback for event ${s}: ${c.message}`)}},0)};#U=({sourceWin:e,requestId:t,body:i})=>{const s=this.#d(e);if(!s){this.#e.warn(`Received resize event from unknown guest. requestid = ${t}`);return}s.domElement&&(s.domElement.style.height=`${i.height}px`),this.#e.debug(`Guest ${s.id} resized to ${i.width}x${i.height}`)};#R=({sourceWin:e,sourceOrigin:t,requestId:i,body:s})=>{const{objectId:o}=s,r=this.#d(e);if(!r)return this.#e.warn("Rejected method invocation request from unknown guest window"),!1;this.#e.debug(`Function ${o}.${String(s.functionName)}() called from guest "${r.id}" (requestId = ${i})`);const c=this.getScriptingObject(o,{guest:r});if(!c)return this.#e.warn(`Invocation of unknown or unauthorized object ${o} from guest ${r.id}`),this.#t.raiseException({targetWin:e,targetOrigin:t,requestId:i,ex:`The requested object (${o}) is not available`}),!1;const a=r.getInfo();return this.#u(`ScriptingObject.API.${o}.${s.functionName}`,{appId:a.guestId,appUrl:a.guestUrl}),this.#I({guest:r,obj:c,functionName:s.functionName,functionParams:s.functionParams}).then(d=>{this.#t.respond({targetWin:e,targetOrigin:t,requestId:i,response:this.#v(d,r)}),this.#e.debug({message:"Value returned for Scripting Object method call",requestId:i,scriptingObject:o,scriptingMethod:s.functionName,...a})}).catch(d=>{this.#t.raiseException({targetWin:e,targetOrigin:r.origin,requestId:i,ex:d}),this.#e.error({message:"Exception thrown for Scripting Object method call",requestId:i,scriptingObject:o,scriptingMethod:s.functionName,...a})}).finally(()=>{this.#b(`ScriptingObject.API.${o}.${s.functionName}`,{appId:a.guestId,appUrl:a.guestUrl})}),!0};#q=()=>{this.#t.listen({messageType:l.GuestResize,callback:this.#U})};#_=()=>{this.#t.listen({messageType:l.GuestReady,callback:this.#$}),this.#t.listen({messageType:l.GuestReadyComplete,callback:this.#E}),this.#t.listen({messageType:l.GuestClose,callback:this.#k}),this.#t.listen({messageType:l.ListObjects,callback:this.#P}),this.#t.listen({messageType:l.ObjectGet,callback:this.#T}),this.#t.listen({messageType:l.ObjectInvoke,callback:this.#R}),this.#t.listen({messageType:l.GuestEventSubscribe,callback:this.#G}),this.#t.listen({messageType:l.GuestEventUnsubscribe,callback:this.#A})};#L=e=>e instanceof A||typeof e?.subscribe=="function";#j=e=>{const t=new q({...e,remoting:this.#t,analyticsObj:this.#n});return t.init(),this.#s.set(e.guestId,t),this.#o.set(t.window,t),this.#r.set(t.url,t),t};#w=e=>{if(typeof e=="string")return this.#s.get(e);const t=this.#o.get(e);return t||Array.from(this.#s.values()).find(i=>i.domElement===e)};#M=()=>{this.#a||(this.#a=setInterval(()=>{const e=[];this.#s.forEach(t=>{t.openMode===f.Popup&&t.window.closed&&e.push(t)}),e.forEach(t=>{const{id:i}=t;this.unloadGuest(i),this.#l.get(i)?.forEach(o=>{Promise.resolve(o({id:i})).catch(()=>{})})})},1e3))};#C=()=>{if(!this.#a)return;Array.from(this.#s.values()).some(t=>t.openMode===f.Popup)||(clearInterval(this.#a),this.#a=null)};#x=e=>{const{url:t,title:i,popupWindowFeatures:s={},searchParams:o,guestId:r,onLoad:c,onError:a}=e,{width:d=800,height:u=600,top:y=100,left:E=100}=s;let g=this.#O(t);if(g)g.window.closed||g.send({messageType:l.GuestFocus,messageBody:{}});else{const w=Object.entries({width:d,height:u,top:y,left:E}).filter(([,b])=>b).map(([b,p])=>`${b}=${p}`).join(","),m=window.open(t,i,`popup, ${w}`);if(m)setTimeout(()=>{try{c?.(r)}catch(b){this.#e.debug(`Error occurred in onLoad for guest with id '${r}': ${b.message}`)}},0);else throw setTimeout(()=>{try{a?.(r)}catch(b){this.#e.debug(`Error occurred in onError for guest with id '${r}': ${b.message}`)}},0),new Error("Failed to open guest application in popup window");m.opener=null,g=this.#j({guestId:r,window:m,title:i,url:t,searchParams:o,openMode:f.Popup}),this.#M()}return g};#D=e=>{const{url:t,title:i,targetElement:s,searchParams:o,guestId:r,onLoad:c,onError:a,options:d={}}=e,u=s.ownerDocument??document,{fitToContent:y=!1,disableSandbox:E=!1,sandboxValues:g=[],customSandboxValues:w=[],style:m="",permissionPolicy:b=""}=d;if(!i)throw new Error("title is required");y&&this.#q();const p=u.createElement("iframe");p.setAttribute("id",r);const T=()=>{setTimeout(()=>{try{c?.(r)}catch(G){this.#e.debug(`Error occurred in onLoad for guest with id '${r}': ${G.message}`)}},0),this.#e.debug(`frame loaded for guest with id '${r}'`),p.removeEventListener("load",T)},v=()=>{setTimeout(()=>{try{a?.(r)}catch(G){this.#e.debug(`Error occurred in onError for guest with id '${r}': ${G.message}`)}},0),this.#e.error(`frame load failed for guest with id '${r}'`),p.removeEventListener("error",v)};p.addEventListener("load",T),p.addEventListener("error",v),p.setAttribute("style",`min-width: 100%; height: 100%; border: 0px; ${m}`),E||p.setAttribute("sandbox",w.length>0?w.join(" "):[...N,...g].join(" ")),p.setAttribute("title",i),p.setAttribute("src",t),b&&p.setAttribute("allow",b),s.appendChild(p);const k=u.getElementById(r);return this.#j({guestId:r,domElement:k,window:k.contentWindow,title:i,url:t,searchParams:o,openMode:f.Embed})};#z=(e,t)=>{let i="";return Object.keys(t).forEach(s=>{i+=`${(i.length?"&":"")+encodeURIComponent(s)}=${encodeURIComponent(t[s])}`}),e+(i?(e.indexOf("?")>=0?"&":"?")+i:"")};addScriptingObject=(e,t)=>{if(B(e)){const i=this.cloneScriptingObject(e);this.#c.addScriptingObject(i,t)}else this.#c.addScriptingObject(e,t)};cloneScriptingObject=e=>{if(!e)throw new Error("proxy is required");const t=new U(e.id,e.objectType);let i=[];return Object.keys(e).forEach(s=>{const o=e[s];if(this.#L(o)){const r=new O({name:o.name||s,objectId:t.id});if(Object.defineProperty(t,s,{value:r,enumerable:!0}),o instanceof A){const c=({eventParams:d,eventOptions:u})=>this.dispatchEvent({event:r,eventParams:d,eventOptions:u}),a=o.subscribe(c);i.push(()=>{o.unsubscribe(a)})}else{const c=o.subscribe?.((a,d,u)=>this.dispatchEvent({event:r,eventParams:d,eventOptions:u}));i.push(()=>{o.unsubscribe?.(c)})}}else if(R(o)&&(Object.defineProperty(t,s,{value:async(...r)=>{const c=await o(...r);return B(c)?this.cloneScriptingObject(c):c},enumerable:!0}),s==="dispose")){const r=t.dispose;Object.defineProperty(t,s,{value:()=>(t._dispose(),r.apply(t)),enumerable:!0})}}),t._dispose=()=>{i.forEach(s=>{s?.()}),i=[]},t};close=()=>{this.#a&&(clearInterval(this.#a),this.#a=null),this.#f(),this.#t.close(),window.removeEventListener("beforeunload",this.#f),this.#e.debug(`host is closed. hostId: ${this.hostId}, correlationId: ${this.#i}`)};dispatchEvent=async e=>{const{event:{id:t,name:i},eventParams:s,eventOptions:o={}}=e,{eventHandler:r=null,timeout:c=null,window:a=null,guestId:d}=o,u=t.split(".")[0],y=d||a,E=y?this.#w(y):null,g=E?this.getScriptingObject(u,{guest:E}):this.getScriptingObject(u);if(!g)return this.#e.warn(`Attempt to dispatch event ${i} on unknown object ${u}`),Promise.resolve([]);const w={object:g._toJSON(),eventName:i,eventParams:s,eventHandler:r,eventOptions:{allowsFeedback:!1}};c&&!Number.isNaN(c)&&(w.eventOptions={allowsFeedback:!0,timeout:Number(c)});const m=[];let b=!1;const p=v=>{const k=v.getInfo();c&&v?.capabilities?.eventFeedback?(m.push(v.dispatchEvent(w,c)),b||(this.#u(`ScriptingObject.Event.${g.id}.${i}`,{appId:this.hostId,appUrl:window.location.href}),b=!0),this.#e.debug({message:"Event dispatched and awaiting feedback",scriptingEventId:t,...k})):(v.send({messageType:l.ObjectEvent,messageBody:w}),this.#e.debug({message:"Event dispatched",scriptingEventId:t,...k}))};return E?p(E):this.#s.forEach(p),await Promise.all(m).then(v=>(this.#e.debug({message:"Event feedback received",scriptingEventId:t}),Y(v))).catch(v=>{throw this.#e.error({message:"Error processing event",eventId:t,exception:v}),v}).finally(()=>{b&&this.#b(`ScriptingObject.Event.${g.id}.${i}`,{appId:this.hostId,appUrl:window.location.href})})};getGuests=()=>Array.from(this.#s.values());getScriptingObject=(e,t)=>this.#c.getObject(e,t?.guest);loadGuest=e=>{const{id:t,url:i,targetElement:s,title:o,searchParams:r={},onLoad:c,onError:a,options:d={}}=e;if(!t)throw new Error("id for guest application is required");let u=t;if(this.#s.has(u)){let m=1;for(;this.#s.has(`${t}-${m}`);)m+=1;u=`${t}-${m}`}const{openMode:y=f.Embed,popupWindowFeatures:E={}}=d,g=this.#z(i,r);let w=null;if(this.#u("SSF.Guest.Load",{appId:u,appUrl:g}),y===f.Popup)w=this.#x({guestId:u,url:g,title:o,searchParams:r,popupWindowFeatures:E,onLoad:c,onError:a});else if(y===f.Embed)w=this.#D({guestId:u,url:g,title:o,targetElement:s,searchParams:r,onLoad:c,onError:a,options:d});else throw new Error(`Invalid openMode: ${y}`);return this.#e.audit({message:"Guest loaded",...w.getInfo()}),w};loadGuests=e=>{const{id:t,url:i,targetElement:s,title:o,searchParamsList:r=[],options:c={}}=e;r.forEach((a,d)=>{this.loadGuest({id:`${t}-${d}`,url:i,title:o,targetElement:s,searchParams:a,options:c})},this)};removeAllScriptingObjects=e=>{this.#c.removeAllScriptingObjects(e)};removeScriptingObject=(e,t)=>{this.#c.removeScriptingObject(e,t)};setLogLevel=e=>{this.#e.setLogLevel(e),this.#s.forEach(this.#y),this.#e.debug("Dispatched config events to all guests")};unloadGuest=e=>{const t=this.#w(e);t&&(t.dispose(),this.#o.delete(t.window),this.#r.delete(t.url),this.#s.delete(t.id),this.#e.audit({message:"Guest is removed from host",...t.getInfo()}),this.#C())};onGuestClose=e=>{const{id:t,guestCloseCallback:i}=e,s=this.#l.get(t)||[];s.push(i),this.#l.set(t,s)}}return I})());
1
+ (function(I,O){typeof exports=="object"&&typeof module=="object"?module.exports=O():typeof define=="function"&&define.amd?define([],O):typeof exports=="object"?exports.ice=O():(I.ice=I.ice||{},I.ice.host=O())})(globalThis,()=>(()=>{"use strict";var j={};j.d=(n,e)=>{for(var t in e)j.o(e,t)&&!j.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:e[t]})},j.o=(n,e)=>Object.prototype.hasOwnProperty.call(n,e),j.r=n=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})};var I={};j.r(I),j.d(I,{Event:()=>O,Guest:()=>q,IFrameSandboxValues:()=>S,OpenMode:()=>m,SANDBOX_DEFAULT:()=>F,SSFHost:()=>G,ScriptingObject:()=>_});class O{name;objectId;id;constructor(e){const{name:t,objectId:i}=e;if(!t)throw new Error("Event name is required");if(!i)throw new Error("Scripting object id is required");this.objectId=i,this.name=t,this.id=`${this.objectId}.${this.name}`.toLowerCase()}}class U{static[Symbol.hasInstance](e){return typeof e=="object"&&e!==null&&"getType"in e&&typeof e.getType=="function"&&e.getType()==="ProxyEvent"}#t;objectId;name;id;getType(){return"ProxyEvent"}constructor(e){const{name:t,objectId:i,eventSrc:s}=e;if(!t)throw new Error("Event name is required");if(!i)throw new Error("Scripting object id is required");if(!s)throw new Error("Event source is required");this.objectId=i,this.name=t,this.#t=s,this.id=`${this.objectId}.${this.name}`.toLowerCase()}subscribe=e=>this.#t.subscribe({eventId:this.id,callback:e});unsubscribe=e=>{this.#t.unsubscribe({eventId:this.id,token:e})}}const H=n=>n instanceof O,re=(n,e)=>`${n.toLowerCase()}.${e.toLowerCase()}`,J="function",X=(n,e)=>typeof n===J&&!!e&&!e.startsWith("_");class _{#t;#i="Object";constructor(e,t){this.#t=e,this.#i=t||this.#i}get id(){return this.#t}get objectType(){return this.#i}_toJSON=()=>{const e=[],t=[];return Object.keys(this).forEach(i=>{const s=this[i];H(s)?t.push(i):X(s,i)&&e.push(i)}),{objectId:this.#t,objectType:this.#i,functions:e,events:t}};_dispose=()=>{};dispose=()=>{}}var g=(n=>(n.GuestClose="guest:close",n.GuestEventSubscribe="guest:eventSubscribe",n.GuestEventUnsubscribe="guest:eventUnsubscribe",n.GuestFocus="guest:focus",n.GuestReady="guest:ready",n.GuestReadyComplete="guest:readyComplete",n.GuestResize="guest:resize",n.HandShake="handshake",n.HandShakeAck="handshake:ack",n.HostClose="host:close",n.HostConfig="host:config",n.ListObjects="list:objects",n.ObjectEvent="object:event",n.ObjectGet="object:get",n.ObjectInvoke="object:invoke",n))(g||{}),m=(n=>(n.Popup="popup",n.Embed="embed",n))(m||{}),S=(n=>(n.AllowDownloadsWithoutUserActivation="allow-downloads-without-user-activation",n.AllowDownloads="allow-downloads",n.AllowForms="allow-forms",n.AllowModals="allow-modals",n.AllowOrientationLock="allow-orientation-lock",n.AllowPointerLock="allow-pointer-lock",n.AllowPopups="allow-popups",n.AllowPopupsToEscapeSandbox="allow-popups-to-escape-sandbox",n.AllowPresentation="allow-presentation",n.AllowSameOrigin="allow-same-origin",n.AllowScripts="allow-scripts",n.AllowStorageAccessByUserActivation="allow-storage-access-by-user-activation",n.AllowTopNavigation="allow-top-navigation",n.AllowTopNavigationByUserActivation="allow-top-navigation-by-user-activation",n))(S||{});const Y=n=>{if(n==="about:blank")return"*";try{const{origin:e}=new URL(n);return e==="null"||!e?n:e}catch{const{origin:t}=new URL(n,document.baseURI);return t}},K=n=>n.flat(1/0).filter(e=>e!==void 0);function R(n){return typeof n=="function"}const Q=[".ice.com",".elliemae.com",".ellielabs.com"],Z=["localhost","127.0.0.1"],M=n=>{try{const{hostname:e}=new URL(n,document.baseURI);return Z.includes(e)||Q.some(t=>e===t.slice(1)||e.endsWith(t))}catch{return!1}};class q{id;title;url;searchParams;domElement;window;openMode;origin;initialized=!1;ready=!1;capabilities;#t;#i;constructor(e){const{guestId:t,domElement:i=null,title:s,url:o,window:r,searchParams:c={},openMode:d=m.Embed,remoting:a,analyticsObj:l}=e;this.id=t,this.title=s,this.url=o,this.origin=Y(o),this.searchParams=c,this.domElement=i,this.window=r,this.openMode=d,this.capabilities={},this.#i=l,this.#t=a}dispose=()=>{if(this.openMode===m.Popup&&!this.window.closed)try{this.window.document,this.window.close()}catch{this.#t.send({targetWin:this.window,targetOrigin:this.origin,messageType:g.HostClose,messageBody:{}})}else this.domElement?.remove?.();this.#t.removeSender({origin:this.origin,window:this.window})};getInfo=()=>({guestId:this.id,guestTitle:this.title,guestUrl:this.url});handShake=()=>new Promise(e=>{let t=0;const i=5;let s;const o=()=>{clearInterval(s),this.#t.unlisten({messageType:g.HandShakeAck,callback:o}),e(!0)};s=setInterval(()=>{t>=i?(clearInterval(s),this.#t.unlisten({messageType:g.HandShakeAck,callback:o}),e(!1)):(this.#t.send({targetWin:this.window,targetOrigin:this.origin,messageType:g.HandShake,messageBody:{}}),t+=1)},1e3),this.#t.listen({messageType:g.HandShakeAck,callback:o})});init=()=>{this.#t.addSender({origin:this.origin,window:this.window}),this.openMode===m.Popup&&this.handShake().catch(()=>{})};dispatchEvent=(e,t)=>(this.#i.startTiming(`ScriptingObject.Event.${e.object.objectId}.${e.eventName}`,{appId:this.id,appUrl:this.url}).catch(()=>{}),this.#t.invoke({targetWin:this.window,targetOrigin:this.origin,messageType:g.ObjectEvent,messageBody:e,responseTimeoutMs:t}).finally(()=>{this.#i.endTiming(`ScriptingObject.Event.${e.object.objectId}.${e.eventName}`,{appId:this.id,appUrl:this.url}).catch(()=>{})}));send=e=>{this.#t.send({targetWin:this.window,targetOrigin:this.origin,...e})}}const L={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};let P;const V=new Uint8Array(16);function ee(){if(!P&&(P=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!P))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return P(V)}var te;const b=[];for(let n=0;n<256;++n)b.push((n+256).toString(16).slice(1));function C(n,e=0){return b[n[e+0]]+b[n[e+1]]+b[n[e+2]]+b[n[e+3]]+"-"+b[n[e+4]]+b[n[e+5]]+"-"+b[n[e+6]]+b[n[e+7]]+"-"+b[n[e+8]]+b[n[e+9]]+"-"+b[n[e+10]]+b[n[e+11]]+b[n[e+12]]+b[n[e+13]]+b[n[e+14]]+b[n[e+15]]}function ae(n,e=0){const t=C(n,e);if(!te(t))throw TypeError("Stringified UUID is invalid");return t}const le=null;function ie(n,e,t){if(L.randomUUID&&!e&&!n)return L.randomUUID();n=n||{};const i=n.random||(n.rng||ee)();if(i[6]=i[6]&15|64,i[8]=i[8]&63|128,e){t=t||0;for(let s=0;s<16;++s)e[t+s]=i[s];return e}return C(i)}const D=ie,x="elli:remoting",N="elli:remoting:response",W="elli:remoting:exception",$=({messageType:n,messageBody:e,requestId:t,onewayMsg:i=!1})=>({requestId:t??(i?null:D()),source:x,type:n,body:e}),de=n=>{const{targetWin:e,targetOrigin:t,messageType:i,messageBody:s}=n,o=$({messageType:i,messageBody:s});e.postMessage(o,t)};class se{#t;#i;#e=new Map;#n=new Map;#s=null;#o=null;#r=new Map;constructor(e,t){if(!e)throw new Error("logger is required");if(!t)throw new Error("correlationId is required");this.#t=t,this.#i=e}#h=()=>{this.#s=null;const e=Date.now(),t=[];let i=null;if(this.#n.forEach((s,o)=>{const{requestId:r,cancelTime:c}=s;c&&c<=e?(this.#i.debug(`Detected response timeout for requestId: ${r}...`),t.push(o),s.resolve(void 0)):c&&(i=i===null?c:Math.min(i,c))}),t.forEach(s=>{this.#n.delete(s)}),i!==null){const s=Math.max(i-Date.now(),0);this.#c(s)}};#c=e=>{this.#s===null&&(this.#s=window.setTimeout(this.#h,e))};#g=()=>{this.#s!==null&&(window.clearTimeout(this.#s),this.#s=null)};#a=e=>{const t=this.#n.get(e);return this.#i.debug(`serving requestId: ${e}`),this.#n.delete(e),t};#p=e=>{const{requestId:t}=e;this.#i.debug(`Response received for invocation requestId: ${t}`);const i=this.#a(t);return i?(i.resolve(e.body),!0):(this.#i.debug(`Received response to stale/invalid request with requestId: ${t}`),!1)};#b=e=>{this.#i.debug(`Exception received for invocation (requestId = ${e.requestId})`);const t=this.#a(e.requestId);return t?(t.reject(new Error(e.body)),!0):(this.#i.warn(`Received exception for stale/invalid request (requestId = ${e.requestId})`),!1)};#d=({sourceWin:e,sourceOrigin:t,message:i})=>{this.#i.debug(`Received message of type "${i.type}"`);const s=this.#e.get(i.type);return s?(s.forEach(o=>{this.#i.debug(`Invoking message handler ${o.name}`),o({sourceWin:e,sourceOrigin:t,requestId:i.requestId,type:i.type,body:i.body})}),!0):!1};#u=e=>{if(this.#r.size===0||!e.source)return!1;const t=this.#r.get(e.source);return!t||e?.data?.source!==x?!1:(this.#i.debug(`Remoting: Received message of type "${e.data.type}"`),e.data.type===N?this.#p(e.data):e.data.type===W?this.#b(e.data):this.#d({sourceWin:e.source,sourceOrigin:t,message:e.data}),!0)};addSender=e=>{const{origin:t,window:i}=e;if(!t)throw new Error("origin is required");if(!i)throw new Error("window is required");this.#r.set(i,t)};initialize=e=>{this.#o&&this.#o.removeEventListener("message",this.#u),e.addEventListener("message",this.#u),this.#o=e,this.#i.debug(`initialized remoting id: ${this.#t}`)};close=()=>{this.#o&&(this.#o.removeEventListener("message",this.#u),this.#o=null),this.#g(),this.#i.debug(`closed remoting id: ${this.#t}`)};invoke=e=>{const{targetWin:t,targetOrigin:i,messageType:s,messageBody:o,responseTimeoutMs:r}=e;return new Promise((c,d)=>{const a=$({messageType:s,messageBody:o});this.#n.set(a.requestId,{requestId:a.requestId,resolve:c,reject:d,cancelTime:r?Date.now()+r:null}),t.postMessage(a,i);const{requestId:l}=a;this.#i.debug(`Posted invocation message of type ${s} requestId: ${l||""}`),r&&(this.#i.debug(`scheduling timeout check for requestId: ${l||""} in ${r} ms`),this.#c(r))})};listen=e=>{const{messageType:t,callback:i}=e,s=this.#e.get(t)||[];s.push(i),this.#e.set(t,s)};unlisten=e=>{const{messageType:t,callback:i}=e,s=this.#e.get(t);if(!s)return;const o=s.indexOf(i);o!==-1&&s.splice(o,1)};send=e=>{const{targetWin:t,targetOrigin:i,messageType:s,messageBody:o}=e,r=$({messageType:s,messageBody:o,onewayMsg:!0});t.postMessage(r,i),this.#i.debug(`Posted one-way message of type "${s}"`)};removeSender=e=>{const{window:t}=e;t&&this.#r.delete(t)};respond=e=>{const{targetWin:t,targetOrigin:i,requestId:s,response:o}=e,r=$({messageType:N,messageBody:o,requestId:s});t.postMessage(r,i),this.#i.debug(`Response sent to caller for invocation requestId: ${s}`)};raiseException=e=>{const{targetWin:t,targetOrigin:i,requestId:s,ex:o}=e,r=o instanceof Error?o.message:o,c=$({messageType:W,messageBody:r,requestId:s});t.postMessage(c,i),this.#i.debug(`Exception sent to caller for invocation. requestId: ${s}`)}}const z="module";var ne=(n=>(n.USER="USER",n.PARTNER="PARTNER",n))(ne||{});class oe{#t=e=>e.trim().toLowerCase();#i=new Map;#e=new Map;#n=e=>{const{so:t,guestId:i}=e,s=this.#t(t.id),o=this.#e.get(i);if(!o)this.#e.set(i,new Map([[s,e]]));else{if(o.has(s))throw new Error(`Scripting Object ${t.id} already exists for guest ${i}`);o.set(s,e)}};#s=({so:e})=>{if(e._dispose&&typeof e._dispose=="function")try{e._dispose()}catch{}};#o=({objectId:e,guestId:t})=>{if(e===z&&!t)for(const[,s]of this.#e){const o=s.get(e);if(o)return o}const i=t?this.#e.get(t):null;return i?i.get(e)??null:null};#r=({objectId:e,guestId:t}={})=>{if(t){if(!e){const s=this.#e.get(t);s&&s.forEach(this.#s),this.#e.delete(t);return}const i=this.#e.get(t);if(i){const s=i.get(e);s&&this.#s(s),i.delete(e)}}else e&&this.#e.forEach(i=>{const s=i.get(e);s&&this.#s(s),i.delete(e)})};addScriptingObject=(e,t)=>{const{guestId:i}=t||{};if(!e?.id||!e?._toJSON)throw new Error("Object is not derived from ScriptingObject");const s=this.#t(e.id);if(s===z&&!i)throw new Error("Guest id is required to add Module scripting object");if(i){this.#n({so:e,...t,guestId:i});return}if(this.#i.has(s))throw new Error(`Scripting Object ${e.id} already exists`);this.#i.set(s,{so:e,...t})};getObject=(e,t)=>{const i=this.#t(e);let s=this.#o({objectId:i,guestId:t?.id});s=s??this.#i.get(i)??null;const{so:o}=s||{};return o||null};has=(e,t)=>this.getObject(e,t)!==null;listScriptingObjects=e=>{const t=new Set(this.#i.keys()),i=this.#e.get(e);if(i)for(const s of i.keys())t.add(s);return Array.from(t)};removeScriptingObject=(e,t)=>{const i=this.#t(e);if(t)this.#r({objectId:i,guestId:t});else{this.#r({objectId:i});const s=this.#i.get(i);s&&this.#s(s),this.#i.delete(i)}};removeAllScriptingObjects=e=>{e?this.#r({guestId:e}):(this.#i.forEach(this.#s),this.#i.clear())}}class ue{__TYPE__="Proxy";id;objectType;constructor(e,t){this.id=e,this.objectType=t}}const B=n=>n?.constructor?.name==="Proxy"||n?.constructor?.name==="ScriptingObjectProxy"||n?.__TYPE__==="Proxy",F=[S.AllowScripts,S.AllowPopups,S.AllowModals,S.AllowForms,S.AllowDownloads,S.AllowSameOrigin];class G{hostId;#t;#i;#e;#n;#s=new Map;#o=new Map;#r=new Map;#h=new Map;#c;#g=null;#a=null;#p=null;#b=null;#d=new Map;#u=new Map;#f;static DEFAULT_TIMING_DEDUP_WINDOW_MS=1e4;constructor(e,t){if(this.hostId=e,!t?.logger)throw new Error("Logger is required");if(!t?.analyticsObj)throw new Error("Analytics object is required");if(this.#e=t.logger,this.#n=t.analyticsObj,this.#f=t?.timingDedupWindowMs??G.DEFAULT_TIMING_DEDUP_WINDOW_MS,this.#i=D(),this.#t=new se(this.#e,this.#i),t?.readyStateCallback&&typeof t?.readyStateCallback!="function")throw new Error("readyStateCallback must be a function");this.#g=t?.readyStateCallback||null,this.#p=t?.onGuestEventSubscribe||null,this.#b=t?.onGuestEventUnsubscribe||null,this.#c=new oe,this.#t.initialize(window),this.#C(),window.addEventListener("beforeunload",this.#v),this.#e.debug(`host is initialized. hostId: ${this.hostId}, correlationId: ${this.#i}`)}#F=(e,t)=>{const i={event:e,...t};this.#n.sendBAEvent(i).catch(s=>{this.#e.debug(`Analytics sendBAEvent failed: ${s.message}`)})};#m=(e,t)=>{this.#n.startTiming(e,t).catch(i=>{this.#e.debug(`Analytics startTiming failed: ${i.message}`)})};#w=(e,t)=>{this.#n.endTiming(e,t).catch(i=>{this.#e.debug(`Analytics endTiming failed: ${i.message}`)})};#y=e=>{if(this.#f<=0)return!0;const t=performance.now(),i=this.#u.get(e);return i!==void 0&&t-i<this.#f?!1:(this.#u.set(e,t),!0)};#v=()=>{Array.from(this.#s.values()).filter(t=>t.openMode===m.Popup).map(t=>t.id).forEach(t=>this.unloadGuest(t))};#l=e=>this.#o.get(e);#$=e=>this.#r.get(e)??null;#k=e=>typeof e?._toJSON=="function";#j=(e,t)=>this.#k(e)?(this.#c.has(e?.id,t)||this.#c.addScriptingObject(e,t?{guestId:t?.id}:{}),{type:"object",object:e._toJSON()}):{type:"value",value:e};#H=e=>typeof e=="string"?e:e instanceof Error?e.message:"An unexpected error occurred in the host application";#O=e=>{e.ready&&this.#t.send({targetWin:e.window,targetOrigin:e.origin,messageType:g.HostConfig,messageBody:{logLevel:this.#e.getLogLevel(),...e.getInfo()}})};#T=({guest:e,obj:t,functionName:i,functionParams:s,callerChain:o})=>{const r=t[i];return R(r)?(this.#e.debug(`Invoking host implementation of ${t.id}.${String(i)}()`),new Promise(c=>{Object.defineProperty(r,"callContext",{value:{guest:e,...o?.length?{callChain:o}:{}},configurable:!0,enumerable:!0,writable:!0}),c(r(...s))})):(this.#e.warn(`Attempt to call invalid function on object type ${t.objectType}: ${String(i)}`),Promise.reject(new Error(`Method '${i}' not found in Scripting Object '${t.id}'`)))};#S=({sourceWin:e,sourceOrigin:t,requestId:i})=>{const s=this.#l(e);if(!s){this.#e.warn(`Received ready event for unknown guest. requestId: ${i}`);return}if(!s.initialized){this.#e.warn("Guest must be initialized before it is marked as ready"),this.#t.raiseException({targetWin:e,targetOrigin:t,requestId:i,ex:"Guest must be initialized before it is marked as ready"});return}if(!s.ready){s.ready=!0;const o=s.getInfo();this.#w("SSF.Guest.Load",{appId:o.guestId,appUrl:o.guestUrl}),this.#O(s),this.#g?.(s),this.#e.audit({message:"Guest is ready",...o})}};#P=({sourceWin:e,sourceOrigin:t,requestId:i,body:s})=>{const o=this.#l(e);if(!o){this.#e.warn(`Received ready event for unknown guest. requestid = ${i}`);return}o.initialized||(o.initialized=!0,o.capabilities=s||{},this.#e.audit({message:"Guest is initialized",...o.getInfo()})),(!s||!s.onReady)&&this.#S({sourceWin:e,sourceOrigin:t,requestId:i,type:"",body:null})};#G=async({sourceWin:e})=>{if(e?.window){const t=this.#E(e);t&&!await t.handShake()&&this.unloadGuest(e)}};#A=({sourceWin:e,sourceOrigin:t,requestId:i})=>{this.#e.debug(`Processing listObjects request. requestId = ${i}`);const s=this.#l(e);if(!s)return this.#e.warn("Rejected object request from unknown guest window"),!1;const o=this.#c.listScriptingObjects(s.id);return this.#t.respond({targetWin:e,targetOrigin:t,requestId:i,response:o}),this.#e.debug({message:"name of scripting objects returned",requestId:i,objects:o,...s.getInfo()}),!0};#U=({sourceWin:e,sourceOrigin:t,requestId:i,body:s})=>{const{objectId:o}=s;this.#e.debug(`Processing getObject request for object ${o}. requestId = ${i}`);const r=this.#l(e);if(!r)return this.#e.warn("Rejected object request from unknown guest window"),!1;const c=this.getScriptingObject(o,{guest:r});return c?(this.#t.respond({targetWin:e,targetOrigin:t,requestId:i,response:this.#j(c,r)}),this.#e.debug({message:"Scripting Object returned",requestId:i,scriptingObject:o,...r.getInfo()}),!0):(this.#e.warn(`unknown or unauthorized object ${o} from guest ${r.id}`),this.#t.raiseException({targetWin:e,targetOrigin:t,requestId:i,ex:`The requested object (${o}) is not available`}),!1)};#_=({sourceWin:e,requestId:t,body:i})=>{const{eventId:s,criteria:o,token:r}=i;this.#e.debug(`Processing guest event subscribe request for event ${s}. requestId = ${t}`);const c=this.#l(e);if(!c){this.#e.warn("Rejected event subscribe request from unknown guest window");return}setTimeout(()=>{try{this.#p?.({guestId:c.id,eventId:s,criteria:o,token:r})}catch(d){this.#e.warn(`Error in onGuestEventSubscribe callback for event ${s}: ${d.message}`)}},0)};#R=({sourceWin:e,requestId:t,body:i})=>{const{eventId:s,token:o}=i;this.#e.debug(`Processing guest event unsubscribe request for event ${s}. requestId = ${t}`);const r=this.#l(e);if(!r){this.#e.warn("Rejected event unsubscribe request from unknown guest window");return}setTimeout(()=>{try{this.#b?.({guestId:r.id,eventId:s,token:o})}catch(c){this.#e.warn(`Error in onGuestEventUnsubscribe callback for event ${s}: ${c.message}`)}},0)};#M=({sourceWin:e,requestId:t,body:i})=>{const s=this.#l(e);if(!s){this.#e.warn(`Received resize event from unknown guest. requestid = ${t}`);return}s.domElement&&(s.domElement.style.height=`${i.height}px`),this.#e.debug(`Guest ${s.id} resized to ${i.width}x${i.height}`)};#q=({sourceWin:e,sourceOrigin:t,requestId:i,body:s})=>{const{objectId:o,callerChain:r}=s,c=this.#l(e);if(!c)return this.#e.warn("Rejected method invocation request from unknown guest window"),!1;this.#e.debug(`Function ${o}.${String(s.functionName)}() called from guest "${c.id}" (requestId = ${i})`);const d=this.getScriptingObject(o,{guest:c});if(!d)return this.#e.warn(`Invocation of unknown or unauthorized object ${o} from guest ${c.id}`),this.#t.raiseException({targetWin:e,targetOrigin:t,requestId:i,ex:`The requested object (${o}) is not available`}),!1;const a=c.getInfo(),l=`ScriptingObject.API.${o}.${s.functionName}`,u=this.#y(l);return u&&this.#m(l,{appId:a.guestId,appUrl:a.guestUrl}),this.#T({guest:c,obj:d,functionName:s.functionName,functionParams:s.functionParams,callerChain:r}).then(h=>{this.#t.respond({targetWin:e,targetOrigin:t,requestId:i,response:this.#j(h,c)}),this.#e.debug({message:"Value returned for Scripting Object method call",requestId:i,scriptingObject:o,scriptingMethod:s.functionName,...a})}).catch(h=>{this.#t.raiseException({targetWin:e,targetOrigin:c.origin,requestId:i,ex:h}),this.#e.error({message:"Exception thrown for Scripting Object method call",requestId:i,scriptingObject:o,scriptingMethod:s.functionName,...a})}).finally(()=>{u&&this.#w(l,{appId:a.guestId,appUrl:a.guestUrl})}),!0};#L=()=>{this.#t.listen({messageType:g.GuestResize,callback:this.#M})};#C=()=>{this.#t.listen({messageType:g.GuestReady,callback:this.#P}),this.#t.listen({messageType:g.GuestReadyComplete,callback:this.#S}),this.#t.listen({messageType:g.GuestClose,callback:this.#G}),this.#t.listen({messageType:g.ListObjects,callback:this.#A}),this.#t.listen({messageType:g.ObjectGet,callback:this.#U}),this.#t.listen({messageType:g.ObjectInvoke,callback:this.#q}),this.#t.listen({messageType:g.GuestEventSubscribe,callback:this.#_}),this.#t.listen({messageType:g.GuestEventUnsubscribe,callback:this.#R})};#D=e=>e instanceof U||typeof e?.subscribe=="function";#I=e=>{const t=new q({...e,remoting:this.#t,analyticsObj:this.#n});return t.init(),this.#s.set(e.guestId,t),this.#o.set(t.window,t),this.#r.set(t.url,t),t};#E=e=>{if(typeof e=="string")return this.#s.get(e);const t=this.#o.get(e);return t||Array.from(this.#s.values()).find(i=>i.domElement===e)};#x=()=>{this.#a||(this.#a=setInterval(()=>{const e=[];this.#s.forEach(t=>{t.openMode===m.Popup&&t.window.closed&&e.push(t)}),e.forEach(t=>{const{id:i}=t;this.unloadGuest(i),this.#h.get(i)?.forEach(o=>{Promise.resolve(o({id:i})).catch(()=>{})})})},1e3))};#N=()=>{if(!this.#a)return;Array.from(this.#s.values()).some(t=>t.openMode===m.Popup)||(clearInterval(this.#a),this.#a=null)};#W=e=>{const{url:t,title:i,popupWindowFeatures:s={},searchParams:o,guestId:r,onLoad:c,onError:d}=e,{width:a=800,height:l=600,top:u=100,left:h=100}=s;let w=this.#$(t);if(w)w.window.closed||(M(t)?window.open("",i):w.window.focus());else{const E=Object.entries({width:a,height:l,top:u,left:h}).filter(([,p])=>p).map(([p,f])=>`${p}=${f}`).join(","),v=window.open(t,i,`popup, ${E}`);if(v)setTimeout(()=>{try{c?.(r)}catch(p){this.#e.debug(`Error occurred in onLoad for guest with id '${r}': ${p.message}`)}},0);else throw setTimeout(()=>{try{d?.(r)}catch(p){this.#e.debug(`Error occurred in onError for guest with id '${r}': ${p.message}`)}},0),new Error("Failed to open guest application in popup window");M(t)||(v.opener=null),w=this.#I({guestId:r,window:v,title:i,url:t,searchParams:o,openMode:m.Popup}),this.#x()}return w};#z=e=>{const{url:t,title:i,targetElement:s,searchParams:o,guestId:r,onLoad:c,onError:d,options:a={}}=e,l=s.ownerDocument??document,{fitToContent:u=!1,disableSandbox:h=!1,sandboxValues:w=[],customSandboxValues:E=[],style:v="",permissionPolicy:p=""}=a;if(!i)throw new Error("title is required");u&&this.#L();const f=l.createElement("iframe");f.setAttribute("id",r);const k=()=>{setTimeout(()=>{try{c?.(r)}catch(A){this.#e.debug(`Error occurred in onLoad for guest with id '${r}': ${A.message}`)}},0),this.#e.debug(`frame loaded for guest with id '${r}'`),f.removeEventListener("load",k)},y=()=>{setTimeout(()=>{try{d?.(r)}catch(A){this.#e.debug(`Error occurred in onError for guest with id '${r}': ${A.message}`)}},0),this.#e.error(`frame load failed for guest with id '${r}'`),f.removeEventListener("error",y)};f.addEventListener("load",k),f.addEventListener("error",y),f.setAttribute("style",`min-width: 100%; height: 100%; border: 0px; ${v}`),h||f.setAttribute("sandbox",E.length>0?E.join(" "):[...F,...w].join(" ")),f.setAttribute("title",i),f.setAttribute("src",t),p&&f.setAttribute("allow",p),s.appendChild(f);const T=l.getElementById(r);return this.#I({guestId:r,domElement:T,window:T.contentWindow,title:i,url:t,searchParams:o,openMode:m.Embed})};#B=(e,t)=>{let i="";return Object.keys(t).forEach(s=>{i+=`${(i.length?"&":"")+encodeURIComponent(s)}=${encodeURIComponent(t[s])}`}),e+(i?(e.indexOf("?")>=0?"&":"?")+i:"")};addScriptingObject=(e,t)=>{if(B(e)){const i=this.cloneScriptingObject(e);this.#c.addScriptingObject(i,t)}else this.#c.addScriptingObject(e,t)};cloneScriptingObject=e=>{if(!e)throw new Error("proxy is required");const t=new _(e.id,e.objectType);let i=[];return Object.keys(e).forEach(s=>{const o=e[s];if(this.#D(o)){const r=new O({name:o.name||s,objectId:t.id});if(Object.defineProperty(t,s,{value:r,enumerable:!0}),o instanceof U){const c=({eventParams:a,eventOptions:l})=>this.dispatchEvent({event:r,eventParams:a,eventOptions:l}),d=o.subscribe(c);i.push(()=>{o.unsubscribe(d)})}else{const c=o.subscribe?.((d,a,l)=>this.dispatchEvent({event:r,eventParams:a,eventOptions:l}));i.push(()=>{o.unsubscribe?.(c)})}}else if(R(o)&&(Object.defineProperty(t,s,{value:async(...r)=>{const c=t[s]?.callContext;if(c?.guest){const a=c.callChain??[],l=c.guest.id,u=this.#d.get(l),h={id:l};u&&(h.metadata=u),Object.defineProperty(o,"callContext",{value:{callChain:[...a,h]},configurable:!0,enumerable:!0,writable:!0})}const d=await o(...r);return B(d)?this.cloneScriptingObject(d):d},enumerable:!0}),s==="dispose")){const r=t.dispose;Object.defineProperty(t,s,{value:()=>(t._dispose(),r.apply(t)),enumerable:!0})}}),t._dispose=()=>{i.forEach(s=>{s?.()}),i=[]},t};close=()=>{this.#a&&(clearInterval(this.#a),this.#a=null),this.#v(),this.#d.clear(),this.#t.close(),window.removeEventListener("beforeunload",this.#v),this.#e.debug(`host is closed. hostId: ${this.hostId}, correlationId: ${this.#i}`)};dispatchEvent=async e=>{const{event:{id:t,name:i},eventParams:s,eventOptions:o={}}=e,{eventHandler:r=null,timeout:c=null,window:d=null,guestId:a}=o,l=t.split(".")[0],u=a||d,h=u?this.#E(u):null,w=h?this.getScriptingObject(l,{guest:h}):this.getScriptingObject(l);if(!w)return this.#e.warn(`Attempt to dispatch event ${i} on unknown object ${l}`),Promise.resolve([]);const E={object:w._toJSON(),eventName:i,eventParams:s,eventHandler:r,eventOptions:{allowsFeedback:!1}};c&&!Number.isNaN(c)&&(E.eventOptions={allowsFeedback:!0,timeout:Number(c)});const v=[],p=`ScriptingObject.Event.${w.id}.${i}`;let f=!1;const k=y=>{const T=y.getInfo();c&&y?.capabilities?.eventFeedback?(v.push(y.dispatchEvent(E,c)),!f&&this.#y(p)&&(this.#m(p,{appId:this.hostId,appUrl:window.location.href}),f=!0),this.#e.debug({message:"Event dispatched and awaiting feedback",scriptingEventId:t,...T})):(y.send({messageType:g.ObjectEvent,messageBody:E}),this.#e.debug({message:"Event dispatched",scriptingEventId:t,...T}))};return h?k(h):this.#s.forEach(k),Promise.all(v).then(y=>(this.#e.debug({message:"Event feedback received",scriptingEventId:t}),K(y))).catch(y=>{throw this.#e.error({message:"Error processing event",eventId:t,exception:y}),y}).finally(()=>{f&&this.#w(p,{appId:this.hostId,appUrl:window.location.href})})};getGuests=()=>Array.from(this.#s.values());getScriptingObject=(e,t)=>this.#c.getObject(e,t?.guest);loadGuest=e=>{const{id:t,url:i,targetElement:s,title:o,searchParams:r={},onLoad:c,onError:d,options:a={},metadata:l}=e;if(!t)throw new Error("id for guest application is required");let u=t;if(this.#s.has(u)){let p=1;for(;this.#s.has(`${t}-${p}`);)p+=1;u=`${t}-${p}`}const{openMode:h=m.Embed,popupWindowFeatures:w={}}=a,E=this.#B(i,r);let v=null;if(this.#m("SSF.Guest.Load",{appId:u,appUrl:E}),h===m.Popup)v=this.#W({guestId:u,url:E,title:o,searchParams:r,popupWindowFeatures:w,onLoad:c,onError:d});else if(h===m.Embed)v=this.#z({guestId:u,url:E,title:o,targetElement:s,searchParams:r,onLoad:c,onError:d,options:a});else throw new Error(`Invalid openMode: ${h}`);return l&&this.#d.set(u,l),this.#e.audit({message:"Guest loaded",...v.getInfo()}),v};loadGuests=e=>{const{id:t,url:i,targetElement:s,title:o,searchParamsList:r=[],options:c={}}=e;r.forEach((d,a)=>{this.loadGuest({id:`${t}-${a}`,url:i,title:o,targetElement:s,searchParams:d,options:c})},this)};removeAllScriptingObjects=e=>{this.#c.removeAllScriptingObjects(e)};removeScriptingObject=(e,t)=>{this.#c.removeScriptingObject(e,t)};setLogLevel=e=>{this.#e.setLogLevel(e),this.#s.forEach(this.#O),this.#e.debug("Dispatched config events to all guests")};unloadGuest=e=>{const t=this.#E(e);t&&(t.dispose(),this.#o.delete(t.window),this.#r.delete(t.url),this.#d.delete(t.id),this.#s.delete(t.id),this.#e.audit({message:"Guest is removed from host",...t.getInfo()}),this.#N())};onGuestClose=e=>{const{id:t,guestCloseCallback:i}=e,s=this.#h.get(t)||[];s.push(i),this.#h.set(t,s)}}return I})());
2
2
 
3
3
  //# sourceMappingURL=index.js.map
Binary file
Binary file