@elliemae/ssf-host 2.23.4 → 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 +100 -43
  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 +101 -44
  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.a4526c5eda64df08190f.js +0 -3
  89. package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.br +0 -0
  90. package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.gz +0 -0
  91. package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["webpack://ice.host/loan-object.js"],"sourcesContent":["import { getHost } from './utils.js';\nimport { Analytics } from './analytics-object-v2.js';\n\nconst getLoanField = (name) => document.getElementById(name);\n\nconst host = getHost(new Analytics());\n\nclass Loan extends ice.host.ScriptingObject {\n constructor() {\n super('Loan');\n this.loanId = 'ABC123';\n this.creditScore = 0;\n this.serviceOrderStatus = {\n title: false,\n credit: false,\n };\n this.onSaved = new ice.host.Event({\n name: 'onSaved',\n objectId: this.constructor.name,\n });\n this.onPreSave = new ice.host.Event({\n name: 'onPreSave',\n objectId: this.constructor.name,\n });\n this.onLoanAmountChanged = new ice.host.Event({\n name: 'onLoanAmountChanged',\n objectId: this.constructor.name,\n });\n this.onLoanTermChanged = new ice.host.Event({\n name: 'onLoanTermChanged',\n objectId: this.constructor.name,\n });\n this.onDownPaymentChanged = new ice.host.Event({\n name: 'onDownPaymentChanged',\n objectId: this.constructor.name,\n });\n\n getLoanField('amount')?.addEventListener?.('blur', (e) => {\n host.dispatchEvent({\n event: this.onLoanAmountChanged,\n eventParams: {\n amount: e.target.value,\n },\n });\n });\n\n getLoanField('term')?.addEventListener?.('blur', (e) => {\n host.dispatchEvent({\n event: this.onLoanTermChanged,\n eventParams: { term: e.target.value },\n });\n });\n\n getLoanField('downPayment')?.addEventListener?.('blur', (e) => {\n host.dispatchEvent({\n event: this.onDownPaymentChanged,\n eventParams: {\n downPayment: e.target.value,\n },\n });\n });\n }\n\n getLoanDetails = () => ({\n id: this.loanId,\n firstName: getLoanField('firstName')?.value,\n lastName: getLoanField('lastName')?.value,\n ssn: getLoanField('ssn')?.value,\n amount: getLoanField('amount')?.value,\n term: getLoanField('term')?.value,\n downPayment: getLoanField('downPayment')?.value,\n creditScore: this.creditScore,\n });\n\n setCreditScore = (creditScore) => {\n this.serviceOrderStatus.credit = true;\n this.creditScore = creditScore;\n const creditBtn = document.getElementById('credit');\n if (creditBtn) {\n creditBtn.disabled = true;\n creditBtn.textContent = `${creditBtn.textContent} : ${creditScore}`;\n }\n };\n\n setServiceOrderStatus = (service, completed) => {\n this.serviceOrderStatus[service] = completed;\n const btn = document.getElementById(service);\n if (btn) {\n btn.disabled = completed;\n btn.textContent = `${btn.textContent} : Done`;\n }\n };\n\n saveLoan = async () => {\n host.dispatchEvent({\n event: this.onSaved,\n eventParams: { id: this.loanId },\n });\n };\n}\n\nexport { Loan };\n"],"mappings":"AAAA,OAAS,WAAAA,MAAe,aACxB,OAAS,aAAAC,MAAiB,2BAE1B,MAAMC,EAAgBC,GAAS,SAAS,eAAeA,CAAI,EAErDC,EAAOJ,EAAQ,IAAIC,CAAW,EAEpC,MAAMI,UAAa,IAAI,KAAK,eAAgB,CAC1C,aAAc,CACZ,MAAM,MAAM,EACZ,KAAK,OAAS,SACd,KAAK,YAAc,EACnB,KAAK,mBAAqB,CACxB,MAAO,GACP,OAAQ,EACV,EACA,KAAK,QAAU,IAAI,IAAI,KAAK,MAAM,CAChC,KAAM,UACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,UAAY,IAAI,IAAI,KAAK,MAAM,CAClC,KAAM,YACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,oBAAsB,IAAI,IAAI,KAAK,MAAM,CAC5C,KAAM,sBACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,kBAAoB,IAAI,IAAI,KAAK,MAAM,CAC1C,KAAM,oBACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,qBAAuB,IAAI,IAAI,KAAK,MAAM,CAC7C,KAAM,uBACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EAEDH,EAAa,QAAQ,GAAG,mBAAmB,OAAS,GAAM,CACxDE,EAAK,cAAc,CACjB,MAAO,KAAK,oBACZ,YAAa,CACX,OAAQ,EAAE,OAAO,KACnB,CACF,CAAC,CACH,CAAC,EAEDF,EAAa,MAAM,GAAG,mBAAmB,OAAS,GAAM,CACtDE,EAAK,cAAc,CACjB,MAAO,KAAK,kBACZ,YAAa,CAAE,KAAM,EAAE,OAAO,KAAM,CACtC,CAAC,CACH,CAAC,EAEDF,EAAa,aAAa,GAAG,mBAAmB,OAAS,GAAM,CAC7DE,EAAK,cAAc,CACjB,MAAO,KAAK,qBACZ,YAAa,CACX,YAAa,EAAE,OAAO,KACxB,CACF,CAAC,CACH,CAAC,CACH,CAEA,eAAiB,KAAO,CACtB,GAAI,KAAK,OACT,UAAWF,EAAa,WAAW,GAAG,MACtC,SAAUA,EAAa,UAAU,GAAG,MACpC,IAAKA,EAAa,KAAK,GAAG,MAC1B,OAAQA,EAAa,QAAQ,GAAG,MAChC,KAAMA,EAAa,MAAM,GAAG,MAC5B,YAAaA,EAAa,aAAa,GAAG,MAC1C,YAAa,KAAK,WACpB,GAEA,eAAkBI,GAAgB,CAChC,KAAK,mBAAmB,OAAS,GACjC,KAAK,YAAcA,EACnB,MAAMC,EAAY,SAAS,eAAe,QAAQ,EAC9CA,IACFA,EAAU,SAAW,GACrBA,EAAU,YAAc,GAAGA,EAAU,WAAW,MAAMD,CAAW,GAErE,EAEA,sBAAwB,CAACE,EAASC,IAAc,CAC9C,KAAK,mBAAmBD,CAAO,EAAIC,EACnC,MAAMC,EAAM,SAAS,eAAeF,CAAO,EACvCE,IACFA,EAAI,SAAWD,EACfC,EAAI,YAAc,GAAGA,EAAI,WAAW,UAExC,EAEA,SAAW,SAAY,CACrBN,EAAK,cAAc,CACjB,MAAO,KAAK,QACZ,YAAa,CAAE,GAAI,KAAK,MAAO,CACjC,CAAC,CACH,CACF,CAEA,OAASC,KAAA","names":["getHost","Analytics","getLoanField","name","host","Loan","creditScore","creditBtn","service","completed","btn"],"sourceRoot":"","file":"loan-object.js"}
1
+ {"version":3,"sources":["webpack://ice.host/loan-object.js"],"sourcesContent":["import { getHost } from './utils.js';\nimport { Analytics } from './analytics-object-v2.js';\n\nconst getLoanField = (name) => document.getElementById(name);\n\nconst host = getHost(new Analytics());\n\nclass Loan extends ice.host.ScriptingObject {\n constructor() {\n super('Loan');\n this.loanId = 'ABC123';\n this.creditScore = 0;\n this.serviceOrderStatus = {\n title: false,\n credit: false,\n };\n this.onSaved = new ice.host.Event({\n name: 'onSaved',\n objectId: this.constructor.name,\n });\n this.onPreSave = new ice.host.Event({\n name: 'onPreSave',\n objectId: this.constructor.name,\n });\n this.onLoanAmountChanged = new ice.host.Event({\n name: 'onLoanAmountChanged',\n objectId: this.constructor.name,\n });\n this.onLoanTermChanged = new ice.host.Event({\n name: 'onLoanTermChanged',\n objectId: this.constructor.name,\n });\n this.onDownPaymentChanged = new ice.host.Event({\n name: 'onDownPaymentChanged',\n objectId: this.constructor.name,\n });\n\n getLoanField('amount')?.addEventListener?.('blur', (e) => {\n host.dispatchEvent({\n event: this.onLoanAmountChanged,\n eventParams: {\n amount: e.target.value,\n },\n });\n });\n\n getLoanField('term')?.addEventListener?.('blur', (e) => {\n host.dispatchEvent({\n event: this.onLoanTermChanged,\n eventParams: { term: e.target.value },\n });\n });\n\n getLoanField('downPayment')?.addEventListener?.('blur', (e) => {\n host.dispatchEvent({\n event: this.onDownPaymentChanged,\n eventParams: {\n downPayment: e.target.value,\n },\n });\n });\n }\n\n getLoanDetails = () => {\n const ctx = this.getLoanDetails.callContext;\n if (ctx?.callChain?.length) {\n console.log(\n '[Loan.getLoanDetails] callChain:',\n JSON.stringify(ctx.callChain),\n );\n }\n return {\n id: this.loanId,\n firstName: getLoanField('firstName')?.value,\n lastName: getLoanField('lastName')?.value,\n ssn: getLoanField('ssn')?.value,\n amount: getLoanField('amount')?.value,\n term: getLoanField('term')?.value,\n downPayment: getLoanField('downPayment')?.value,\n creditScore: this.creditScore,\n };\n };\n\n setCreditScore = (creditScore) => {\n const ctx = this.setCreditScore.callContext;\n if (ctx?.callChain?.length) {\n console.log(\n '[Loan.setCreditScore] callChain:',\n JSON.stringify(ctx.callChain),\n );\n }\n this.serviceOrderStatus.credit = true;\n this.creditScore = creditScore;\n const creditBtn = document.getElementById('credit');\n if (creditBtn) {\n creditBtn.disabled = true;\n creditBtn.textContent = `${creditBtn.textContent} : ${creditScore}`;\n }\n };\n\n setServiceOrderStatus = (service, completed) => {\n const ctx = this.setServiceOrderStatus.callContext;\n if (ctx?.callChain?.length) {\n console.log(\n '[Loan.setServiceOrderStatus] callChain:',\n JSON.stringify(ctx.callChain),\n );\n }\n this.serviceOrderStatus[service] = completed;\n const btn = document.getElementById(service);\n if (btn) {\n btn.disabled = completed;\n btn.textContent = `${btn.textContent} : Done`;\n }\n };\n\n saveLoan = async () => {\n host.dispatchEvent({\n event: this.onSaved,\n eventParams: { id: this.loanId },\n });\n };\n}\n\nexport { Loan };\n"],"mappings":"AAAA,OAAS,WAAAA,MAAe,aACxB,OAAS,aAAAC,MAAiB,2BAE1B,MAAMC,EAAgBC,GAAS,SAAS,eAAeA,CAAI,EAErDC,EAAOJ,EAAQ,IAAIC,CAAW,EAEpC,MAAMI,UAAa,IAAI,KAAK,eAAgB,CAC1C,aAAc,CACZ,MAAM,MAAM,EACZ,KAAK,OAAS,SACd,KAAK,YAAc,EACnB,KAAK,mBAAqB,CACxB,MAAO,GACP,OAAQ,EACV,EACA,KAAK,QAAU,IAAI,IAAI,KAAK,MAAM,CAChC,KAAM,UACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,UAAY,IAAI,IAAI,KAAK,MAAM,CAClC,KAAM,YACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,oBAAsB,IAAI,IAAI,KAAK,MAAM,CAC5C,KAAM,sBACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,kBAAoB,IAAI,IAAI,KAAK,MAAM,CAC1C,KAAM,oBACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EACD,KAAK,qBAAuB,IAAI,IAAI,KAAK,MAAM,CAC7C,KAAM,uBACN,SAAU,KAAK,YAAY,IAC7B,CAAC,EAEDH,EAAa,QAAQ,GAAG,mBAAmB,OAASI,GAAM,CACxDF,EAAK,cAAc,CACjB,MAAO,KAAK,oBACZ,YAAa,CACX,OAAQE,EAAE,OAAO,KACnB,CACF,CAAC,CACH,CAAC,EAEDJ,EAAa,MAAM,GAAG,mBAAmB,OAASI,GAAM,CACtDF,EAAK,cAAc,CACjB,MAAO,KAAK,kBACZ,YAAa,CAAE,KAAME,EAAE,OAAO,KAAM,CACtC,CAAC,CACH,CAAC,EAEDJ,EAAa,aAAa,GAAG,mBAAmB,OAASI,GAAM,CAC7DF,EAAK,cAAc,CACjB,MAAO,KAAK,qBACZ,YAAa,CACX,YAAaE,EAAE,OAAO,KACxB,CACF,CAAC,CACH,CAAC,CACH,CAEA,eAAiB,IAAM,CACrB,MAAMC,EAAM,KAAK,eAAe,YAChC,OAAIA,GAAK,WAAW,QAGhB,KAAK,UAAUA,EAAI,SAAS,EAGzB,CACL,GAAI,KAAK,OACT,UAAWL,EAAa,WAAW,GAAG,MACtC,SAAUA,EAAa,UAAU,GAAG,MACpC,IAAKA,EAAa,KAAK,GAAG,MAC1B,OAAQA,EAAa,QAAQ,GAAG,MAChC,KAAMA,EAAa,MAAM,GAAG,MAC5B,YAAaA,EAAa,aAAa,GAAG,MAC1C,YAAa,KAAK,WACpB,CACF,EAEA,eAAkBM,GAAgB,CAChC,MAAMD,EAAM,KAAK,eAAe,YAC5BA,GAAK,WAAW,QAGhB,KAAK,UAAUA,EAAI,SAAS,EAGhC,KAAK,mBAAmB,OAAS,GACjC,KAAK,YAAcC,EACnB,MAAMC,EAAY,SAAS,eAAe,QAAQ,EAC9CA,IACFA,EAAU,SAAW,GACrBA,EAAU,YAAc,GAAGA,EAAU,WAAW,MAAMD,CAAW,GAErE,EAEA,sBAAwB,CAACE,EAASC,IAAc,CAC9C,MAAMJ,EAAM,KAAK,sBAAsB,YACnCA,GAAK,WAAW,QAGhB,KAAK,UAAUA,EAAI,SAAS,EAGhC,KAAK,mBAAmBG,CAAO,EAAIC,EACnC,MAAMC,EAAM,SAAS,eAAeF,CAAO,EACvCE,IACFA,EAAI,SAAWD,EACfC,EAAI,YAAc,GAAGA,EAAI,WAAW,UAExC,EAEA,SAAW,SAAY,CACrBR,EAAK,cAAc,CACjB,MAAO,KAAK,QACZ,YAAa,CAAE,GAAI,KAAK,MAAO,CACjC,CAAC,CACH,CACF,CAEA,OAASC,KAAA","names":["getHost","Analytics","getLoanField","name","host","Loan","e","ctx","creditScore","creditBtn","service","completed","btn"],"sourceRoot":"","file":"loan-object.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>Popup Focus E2E 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="js/emuiSsfHost.071827d0d7e775690fbb.js"></script></head><body class="bg-gray-50"><header class="bg-purple-600 text-white px-4 py-3 flex items-center justify-between"><h1 class="text-lg font-semibold">Popup Focus E2E Test</h1><a href="./index.html" class="text-purple-200 hover:text-white text-sm">&larr; Back to main</a></header><main class="mx-auto max-w-5xl px-4 py-4"><div class="grid grid-cols-2 gap-4 mb-4"><div class="bg-purple-50 border border-purple-200 rounded-md p-3"><h2 class="text-sm font-bold text-purple-900 mb-2">Behavior Under Test</h2><ul class="text-xs text-purple-800 space-y-1 list-disc list-inside"><li><strong>Trusted domains</strong> (.ice.com, .elliemae.com, .ellielabs.com, localhost): <code>window.opener</code> is kept intact. Re-opening uses <code>window.open('', name)</code> to bring the popup to front.</li><li><strong>Untrusted domains</strong> (everything else): <code>window.opener = null</code> is set for security. Re-opening falls back to <code>guest.window.focus()</code>.</li><li>The host tracks popup windows and detects closure via a periodic monitor.</li></ul></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-1.5 list-decimal list-inside"><li>Wait for <code>"Popup Focus Host ready"</code> in the Event Log.</li><li><strong>Trusted popup test:</strong> Click "Open Title Service" → popup opens. Click "Re-open Title Service" → the <em>existing</em> popup should come to the foreground (no new window).</li><li><strong>Second trusted popup:</strong> Click "Open Credit Service" → second popup opens. Click "Re-open Credit" → same foreground behavior.</li><li><strong>Untrusted popup test:</strong> Click "Open Third-Party" → popup opens to <code>example.com</code>. Click "Re-open Third-Party" → focus may or may not work (opener was nulled). Observe the behavior.</li><li>Verify the Guest Status panel shows the open/closed state of each popup.</li><li>Manually close a popup window, then check Guest Status updates to "closed".</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 Behavior</h2><div class="grid grid-cols-3 gap-3 text-xs"><div><h3 class="font-semibold text-amber-800 mb-1">Title / Credit Service (Trusted)</h3><ul class="text-amber-900 list-disc list-inside space-y-0.5"><li>"Open" → new popup window appears</li><li>"Re-open" → existing popup comes to front, <strong>no new window</strong></li><li>Guest Status shows <span class="text-green-600 font-semibold">open</span></li><li>Embedded Pricing Service renders normally</li></ul></div><div><h3 class="font-semibold text-amber-800 mb-1">Third-Party (Untrusted)</h3><ul class="text-amber-900 list-disc list-inside space-y-0.5"><li>"Open" → popup opens to <code>example.com</code></li><li>"Re-open" → <code>focus()</code> is called on stored reference (browser may or may not honor it)</li><li><code>opener</code> is <code>null</code> inside the popup</li></ul></div><div><h3 class="font-semibold text-amber-800 mb-1">Popup Closure</h3><ul class="text-amber-900 list-disc list-inside space-y-0.5"><li>Close a popup manually (X button)</li><li>Guest Status updates to <span class="text-red-500 font-semibold">closed</span> within 2 seconds</li><li>Re-opening a closed popup should open a new window</li></ul></div></div></div><div class="grid grid-cols-2 gap-4 mb-4"><div class="bg-white rounded-lg shadow p-4"><h2 class="text-sm font-semibold text-gray-700 mb-3">Popup Actions</h2><div class="flex flex-col gap-2"><p class="text-xs text-gray-400 font-medium">Trusted (localhost — opener preserved)</p><button data-testid="btn-open-title-popup" id="btnOpenTitle" class="rounded bg-purple-600 px-4 py-2 text-sm text-white hover:bg-purple-700">Open Title Service (Popup)</button> <button data-testid="btn-reopen-title-popup" id="btnReopenTitle" class="rounded bg-purple-500 px-4 py-2 text-sm text-white hover:bg-purple-600">Re-open Title Service (should focus existing)</button> <button data-testid="btn-open-credit-popup" id="btnOpenCredit" class="rounded bg-indigo-600 px-4 py-2 text-sm text-white hover:bg-indigo-700">Open Credit Service (Popup)</button> <button data-testid="btn-reopen-credit-popup" id="btnReopenCredit" class="rounded bg-indigo-500 px-4 py-2 text-sm text-white hover:bg-indigo-600">Re-open Credit Service (should focus existing)</button><hr class="my-2 border-gray-300"/><p class="text-xs text-gray-400 font-medium">Untrusted (opener nulled)</p><button data-testid="btn-open-thirdparty-popup" id="btnOpenThirdParty" class="rounded bg-red-600 px-4 py-2 text-sm text-white hover:bg-red-700">Open Third-Party (Popup) — untrusted</button> <button data-testid="btn-reopen-thirdparty-popup" id="btnReopenThirdParty" class="rounded bg-red-500 px-4 py-2 text-sm text-white hover:bg-red-600">Re-open Third-Party (focus test — opener nulled)</button></div></div><div class="bg-white rounded-lg shadow p-4"><h2 class="text-sm font-semibold text-gray-700 mb-3">Guest Status</h2><div data-testid="popup-guest-list" id="guestList" class="text-xs space-y-1 min-h-[100px]"><p class="text-gray-400">No popup guests loaded yet</p></div></div></div><div class="bg-white rounded-lg shadow p-4 mb-4"><h2 class="text-sm font-semibold text-gray-700 mb-2">Event Log</h2><div data-testid="popup-event-log" id="eventLog" class="text-xs bg-gray-100 rounded p-2 min-h-[100px] max-h-[250px] overflow-y-auto"></div></div><div class="mb-4"><h2 class="text-sm font-semibold text-gray-700 mb-2">Embedded Guest (for comparison)</h2><div data-testid="embedded-container" id="embedded-container" class="border-2 border-dashed border-purple-300 rounded-lg min-h-[150px]"></div></div><div class="bg-gray-100 border border-gray-300 rounded-md p-3"><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-POP-01:</strong> Host initializes, embedded Pricing Service guest loads without error</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-02:</strong> "Open Title Service" opens a new popup window</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-03:</strong> "Re-open Title Service" brings the existing popup to front — <strong>no new window or about:blank tab</strong></span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-04:</strong> "Open Credit Service" opens a second popup (both coexist)</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-05:</strong> "Re-open Credit Service" brings credit popup to front</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-06:</strong> Guest Status shows each popup as <span class="text-green-600 font-semibold">open</span></span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-07:</strong> Manually close a popup → Guest Status updates to <span class="text-red-500 font-semibold">closed</span> within ~2s</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-08:</strong> Re-opening a closed popup opens a new window (not just focus)</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-09:</strong> "Open Third-Party" opens <code>example.com</code> popup</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-10:</strong> "Re-open Third-Party" attempts <code>focus()</code> (may open about:blank — expected for untrusted, opener was nulled)</span></label> <label class="flex items-start gap-2"><input type="checkbox" class="mt-0.5"/> <span><strong>TC-POP-11:</strong> No console errors during trusted popup operations</span></label></div></div></main><script src="./popup-focus-host.js" type="module"></script></body></html>
@@ -0,0 +1,6 @@
1
+ import{Analytics as v}from"./analytics-object-v2.js";import{getGuestBaseUrl as L,getHost as S}from"./utils.js";const p=document.getElementById("eventLog"),m=document.getElementById("guestList"),e=i=>{const t=new Date().toLocaleTimeString();p.innerHTML+=`<div>[${t}] ${i}</div>`,p.scrollTop=p.scrollHeight},l=i=>{const t=i.getGuests();if(!t.length){m.innerHTML='<p class="text-gray-400">No popup guests loaded</p>';return}m.innerHTML=t.map(n=>`<div class="flex justify-between items-center p-1 bg-gray-50 rounded">
2
+ <span><strong>${n.id}</strong> \u2014 ${n.title??"untitled"}</span>
3
+ <span class="${n.window?.closed?"text-red-500":"text-green-500"}">${n.window?.closed?"closed":"open"}</span>
4
+ </div>`).join("")},y=async()=>{e("Initializing Popup Focus Host...");const i=new v,t=S(i);if(!t){e("ERROR: Failed to create SSFHost");return}class n extends ice.host.ScriptingObject{constructor(){super("Loan"),this.onLoanAmountChanged=new ice.host.Event({name:"onLoanAmountChanged",objectId:this.constructor.name}),this.onLoanTermChanged=new ice.host.Event({name:"onLoanTermChanged",objectId:this.constructor.name}),this.onDownPaymentChanged=new ice.host.Event({name:"onDownPaymentChanged",objectId:this.constructor.name})}getLoanDetails=()=>({id:"POPUP-LOAN",amount:25e4,term:30,downPayment:5e4,creditScore:720});setCreditScore=d=>{e(`setCreditScore(${d}) called by popup guest`)};setServiceOrderStatus=(d,s)=>{e(`setServiceOrderStatus("${d}", ${s}) called by popup guest`)}}const g=new n;t.addScriptingObject(g),e("Loan scripting object registered");const r=await L();t.loadGuest({id:"pricingService",url:new URL("./pricingService.html",r).href,title:"Pricing Service (Embedded)",targetElement:document.getElementById("embedded-container"),options:{fitToContent:!0},onLoad:()=>e("Pricing Service (embedded) loaded")});const o=(c,d,s)=>{e(`loadGuest("${c}") as popup...`);const h=t.loadGuest({id:c,url:d,title:s,targetElement:document.getElementById("embedded-container"),options:{openMode:ice.host.OpenMode.Popup},onLoad:a=>{e(`Popup guest "${a}" loaded`),l(t)},onError:a=>e(`ERROR: Popup guest "${a}" failed`)});return l(t),h};document.getElementById("btnOpenTitle").addEventListener("click",()=>{o("titleService",new URL("./titleService.html",r).href,"Title Service Corp")}),document.getElementById("btnReopenTitle").addEventListener("click",()=>{e("Re-opening Title Service popup (should bring to front)..."),o("titleService",new URL("./titleService.html",r).href,"Title Service Corp"),e("Check if Title Service popup came to front")}),document.getElementById("btnOpenCredit").addEventListener("click",()=>{o("creditService",new URL("./creditService.html",r).href,"Credit Service Corp")}),document.getElementById("btnReopenCredit").addEventListener("click",()=>{e("Re-opening Credit Service popup (should bring to front)..."),o("creditService",new URL("./creditService.html",r).href,"Credit Service Corp"),e("Check if Credit Service popup came to front")});const u="https://example.com";document.getElementById("btnOpenThirdParty").addEventListener("click",()=>{o("thirdPartyGuest",u,"Third Party App")}),document.getElementById("btnReopenThirdParty").addEventListener("click",()=>{e("Re-opening Third Party popup (opener was nulled \u2014 focus may not work)..."),o("thirdPartyGuest",u,"Third Party App"),e("Check if Third Party popup came to front or a new about:blank opened")}),setInterval(()=>l(t),2e3),e("Popup Focus Host ready")};window.addEventListener("DOMContentLoaded",y);
5
+
6
+ //# sourceMappingURL=popup-focus-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["webpack://ice.host/popup-focus-host.js"],"sourcesContent":["import { Analytics } from './analytics-object-v2.js';\nimport { getGuestBaseUrl, getHost } from './utils.js';\n\nconst logEl = document.getElementById('eventLog');\nconst guestListEl = document.getElementById('guestList');\n\nconst log = (msg) => {\n const ts = new Date().toLocaleTimeString();\n logEl.innerHTML += `<div>[${ts}] ${msg}</div>`;\n logEl.scrollTop = logEl.scrollHeight;\n};\n\nconst refreshGuestList = (host) => {\n const guests = host.getGuests();\n if (!guests.length) {\n guestListEl.innerHTML =\n '<p class=\"text-gray-400\">No popup guests loaded</p>';\n return;\n }\n guestListEl.innerHTML = guests\n .map(\n (g) =>\n `<div class=\"flex justify-between items-center p-1 bg-gray-50 rounded\">\n <span><strong>${g.id}</strong> — ${g.title ?? 'untitled'}</span>\n <span class=\"${\n g.window?.closed ? 'text-red-500' : 'text-green-500'\n }\">${g.window?.closed ? 'closed' : 'open'}</span>\n </div>`,\n )\n .join('');\n};\n\nconst init = async () => {\n log('Initializing Popup Focus Host...');\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 // Scripting object compatible with the shared pricingService guest\n class Loan extends ice.host.ScriptingObject {\n constructor() {\n super('Loan');\n this.onLoanAmountChanged = new ice.host.Event({\n name: 'onLoanAmountChanged',\n objectId: this.constructor.name,\n });\n this.onLoanTermChanged = new ice.host.Event({\n name: 'onLoanTermChanged',\n objectId: this.constructor.name,\n });\n this.onDownPaymentChanged = new ice.host.Event({\n name: 'onDownPaymentChanged',\n objectId: this.constructor.name,\n });\n }\n getLoanDetails = () => ({\n id: 'POPUP-LOAN',\n amount: 250000,\n term: 30,\n downPayment: 50000,\n creditScore: 720,\n });\n setCreditScore = (score) => {\n log(`setCreditScore(${score}) called by popup guest`);\n };\n setServiceOrderStatus = (service, done) => {\n log(`setServiceOrderStatus(\"${service}\", ${done}) called by popup guest`);\n };\n }\n\n const loanObj = new Loan();\n host.addScriptingObject(loanObj);\n log('Loan scripting object registered');\n\n const guestBaseUrl = await getGuestBaseUrl();\n\n // Load an embedded guest for comparison\n host.loadGuest({\n id: 'pricingService',\n url: new URL('./pricingService.html', guestBaseUrl).href,\n title: 'Pricing Service (Embedded)',\n targetElement: document.getElementById('embedded-container'),\n options: { fitToContent: true },\n onLoad: () => log('Pricing Service (embedded) loaded'),\n });\n\n const loadPopup = (id, url, title) => {\n log(`loadGuest(\"${id}\") as popup...`);\n const guest = host.loadGuest({\n id,\n url,\n title,\n targetElement: document.getElementById('embedded-container'),\n options: { openMode: ice.host.OpenMode.Popup },\n onLoad: (guestId) => {\n log(`Popup guest \"${guestId}\" loaded`);\n refreshGuestList(host);\n },\n onError: (guestId) => log(`ERROR: Popup guest \"${guestId}\" failed`),\n });\n refreshGuestList(host);\n return guest;\n };\n\n // Title Service popup\n document.getElementById('btnOpenTitle').addEventListener('click', () => {\n loadPopup(\n 'titleService',\n new URL('./titleService.html', guestBaseUrl).href,\n 'Title Service Corp',\n );\n });\n\n document.getElementById('btnReopenTitle').addEventListener('click', () => {\n log('Re-opening Title Service popup (should bring to front)...');\n loadPopup(\n 'titleService',\n new URL('./titleService.html', guestBaseUrl).href,\n 'Title Service Corp',\n );\n log('Check if Title Service popup came to front');\n });\n\n // Credit Service popup\n document.getElementById('btnOpenCredit').addEventListener('click', () => {\n loadPopup(\n 'creditService',\n new URL('./creditService.html', guestBaseUrl).href,\n 'Credit Service Corp',\n );\n });\n\n document.getElementById('btnReopenCredit').addEventListener('click', () => {\n log('Re-opening Credit Service popup (should bring to front)...');\n loadPopup(\n 'creditService',\n new URL('./creditService.html', guestBaseUrl).href,\n 'Credit Service Corp',\n );\n log('Check if Credit Service popup came to front');\n });\n\n // Third-party (untrusted) popup — opener will be nulled\n const THIRD_PARTY_URL = 'https://example.com';\n document.getElementById('btnOpenThirdParty').addEventListener('click', () => {\n loadPopup('thirdPartyGuest', THIRD_PARTY_URL, 'Third Party App');\n });\n\n document\n .getElementById('btnReopenThirdParty')\n .addEventListener('click', () => {\n log(\n 'Re-opening Third Party popup (opener was nulled — focus may not work)...',\n );\n loadPopup('thirdPartyGuest', THIRD_PARTY_URL, 'Third Party App');\n log(\n 'Check if Third Party popup came to front or a new about:blank opened',\n );\n });\n\n // Periodic guest list refresh\n setInterval(() => refreshGuestList(host), 2000);\n\n log('Popup Focus Host ready');\n};\n\nwindow.addEventListener('DOMContentLoaded', init);\n"],"mappings":"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","names":["Analytics","getGuestBaseUrl","getHost","logEl","guestListEl","log","msg","ts","refreshGuestList","host","guests","g","init","analyticsObj","Loan","score","service","done","loanObj","guestBaseUrl","loadPopup","id","url","title","guest","guestId","THIRD_PARTY_URL"],"sourceRoot":"","file":"popup-focus-host.js"}
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Guest V1 -> V2 Host</title><style>body,html,main{height:100%;margin:2px;padding:0}iframe{width:100%;height:100%;border:none}#guest-container{height:100%}</style><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script src="https://cdn.elliemae.io/elliemae/core/ssf/1.0/elli.ssf.guest.js"></script><script defer="defer" src="js/emuiSsfHost.a4526c5eda64df08190f.js"></script></head><body><h2>Guest V1 nesting Host V2</h2><div id="result"><p id="loan-details"></p><p id="loan-pre-save-event-details"></p></div><div id="guest-container"></div><script type="module">import{getGuestBaseUrl,getHost}from"./utils.js";import{Analytics}from"./analytics-object-v2.js";const analyticsObj=new Analytics,hostV2=getHost(analyticsObj),loadV2Guest=async()=>{const t=await getGuestBaseUrl(),{id:e}=hostV2.loadGuest({id:"guestV2",url:new URL("./v2-guest.html",t).href,title:"Guest V2",targetElement:document.getElementById("guest-container")})};window.addEventListener("load",async()=>{await elli.script.connect();const t=await elli.script.getObject("Application"),e=await elli.script.getObject("Loan"),a=await e.getLoanDetails();document.getElementById("loan-details").innerText=`Loan Data: ${JSON.stringify(a)}`,elli.script.subscribe("loan","onPreSave",(t,e)=>(document.getElementById("loan-pre-save-event-details").innerText=`Presave event data: ${JSON.stringify(e)}`,!1)),hostV2.addScriptingObject(hostV2.cloneScriptingObject(t,{})),hostV2.addScriptingObject(hostV2.cloneScriptingObject(e,{})),await loadV2Guest()})</script></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Guest V1 -> V2 Host</title><style>body,html,main{height:100%;margin:2px;padding:0}iframe{width:100%;height:100%;border:none}#guest-container{height:100%}</style><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script src="https://cdn.elliemae.io/elliemae/core/ssf/1.0/elli.ssf.guest.js"></script><script defer="defer" src="js/emuiSsfHost.071827d0d7e775690fbb.js"></script></head><body><h2>Guest V1 nesting Host V2</h2><div id="result"><p id="loan-details"></p><p id="loan-pre-save-event-details"></p></div><div id="guest-container"></div><script type="module">import{getGuestBaseUrl,getHost}from"./utils.js";import{Analytics}from"./analytics-object-v2.js";const analyticsObj=new Analytics,hostV2=getHost(analyticsObj),loadV2Guest=async()=>{const t=await getGuestBaseUrl(),{id:e}=hostV2.loadGuest({id:"guestV2",url:new URL("./v2-guest.html",t).href,title:"Guest V2",targetElement:document.getElementById("guest-container")})};window.addEventListener("load",async()=>{await elli.script.connect();const t=await elli.script.getObject("Application"),e=await elli.script.getObject("Loan"),a=await e.getLoanDetails();document.getElementById("loan-details").innerText=`Loan Data: ${JSON.stringify(a)}`,elli.script.subscribe("loan","onPreSave",(t,e)=>(document.getElementById("loan-pre-save-event-details").innerText=`Presave event data: ${JSON.stringify(e)}`,!1)),hostV2.addScriptingObject(hostV2.cloneScriptingObject(t,{})),hostV2.addScriptingObject(hostV2.cloneScriptingObject(e,{})),await loadV2Guest()})</script></body></html>
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Host V2 -> V1 Guest</title><style>body,html,main{height:100%;margin:4px;padding:0}iframe{width:100%;height:100%;border:none}#guest-container{height:100%}</style><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script defer="defer" src="js/emuiSsfHost.a4526c5eda64df08190f.js"></script></head><body><main><h1>Host V2 nesting V1 Guest</h1><button id="save-btn" type="button" onclick="saveLoan()">Save Loan</button><div id="results"><p id="pre-save-feedback-result"></p></div><div id="guest-container"></div></main><script type="module">import{Loan}from"./loan-object.js";import{getGuestBaseUrl,getHost}from"./utils.js";import{Analytics}from"./analytics-object-v2.js";const analyticsObj=new Analytics,hostV2=getHost(analyticsObj),loanObj=new Loan;hostV2.addScriptingObject(loanObj);const params=new URLSearchParams(document.location.search),{id:id}=hostV2.loadGuest({id:"guestV1",url:new URL("true"!==params.get("nestV1GuestV2Host")?"./v1-guest.html":"./v1-guest-v2-host.html",window.location.href).href,searchParams:{nestHostV1:!0},title:"Guest V1",targetElement:document.getElementById("guest-container")});window.saveLoan=async()=>{const e=loanObj.getLoanDetails();(await hostV2.dispatchEvent({event:loanObj.onPreSave,eventParams:e,eventOptions:{timeout:1e3}})).some(e=>!1===e)?document.getElementById("pre-save-feedback-result").innerText="Pre save failed":document.getElementById("pre-save-feedback-result").innerText="Pre save succeeded"}</script></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Host V2 -> V1 Guest</title><style>body,html,main{height:100%;margin:4px;padding:0}iframe{width:100%;height:100%;border:none}#guest-container{height:100%}</style><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script defer="defer" src="js/emuiSsfHost.071827d0d7e775690fbb.js"></script></head><body><main><h1>Host V2 nesting V1 Guest</h1><button id="save-btn" type="button" onclick="saveLoan()">Save Loan</button><div id="results"><p id="pre-save-feedback-result"></p></div><div id="guest-container"></div></main><script type="module">import{Loan}from"./loan-object.js";import{Application}from"./application-object-v2.js";import{getGuestBaseUrl,getHost}from"./utils.js";import{Analytics}from"./analytics-object-v2.js";const analyticsObj=new Analytics,hostV2=getHost(analyticsObj),loanObj=new Loan,appObj=new Application;hostV2.addScriptingObject(loanObj),hostV2.addScriptingObject(appObj);const params=new URLSearchParams(document.location.search),{id:id}=hostV2.loadGuest({id:"guestV1",url:new URL("true"!==params.get("nestV1GuestV2Host")?"./v1-guest.html":"./v1-guest-v2-host.html",window.location.href).href,searchParams:{nestHostV1:!0},title:"Guest V1",targetElement:document.getElementById("guest-container")});window.saveLoan=async()=>{const e=loanObj.getLoanDetails();(await hostV2.dispatchEvent({event:loanObj.onPreSave,eventParams:e,eventOptions:{timeout:1e3}})).some(e=>!1===e)?document.getElementById("pre-save-feedback-result").innerText="Pre save failed":document.getElementById("pre-save-feedback-result").innerText="Pre save succeeded"}</script></body></html>
@@ -16,6 +16,7 @@ export declare class SSFHost<AppObjects extends ScriptingObjects = ScriptingObje
16
16
  * unique identifier for the host
17
17
  */
18
18
  hostId: string;
19
+ static readonly DEFAULT_TIMING_DEDUP_WINDOW_MS = 10000;
19
20
  /**
20
21
  * Create a new host
21
22
  * @param hostId unique identifier for the host
@@ -61,6 +61,15 @@ export type HostOption = {
61
61
  * callback to process guest event unsubscription
62
62
  */
63
63
  onGuestEventUnsubscribe?: GuestEventUnsubscribeCallback;
64
+ /**
65
+ * Minimum interval (ms) between timing measurements for the same metric name.
66
+ * When a timing for a given name is recorded, subsequent timings for the same
67
+ * name are skipped until this window elapses. Avoids redundant cross-window
68
+ * analytics calls for high-frequency APIs and events.
69
+ * Set to 0 to disable deduplication (time every call).
70
+ * @default 10000
71
+ */
72
+ timingDedupWindowMs?: number;
64
73
  };
65
74
  /**
66
75
  * parameters for rendering a guest
@@ -101,6 +110,12 @@ export type LoadGuestParam<SearchParams> = {
101
110
  * @param guestId - unique id of the guest application
102
111
  */
103
112
  onError?: (guestId: string) => void;
113
+ /**
114
+ * arbitrary metadata to associate with this guest.
115
+ * Automatically included in callContext.callChain when
116
+ * the guest's scripting object calls are forwarded to a parent host.
117
+ */
118
+ metadata?: Record<string, unknown>;
104
119
  };
105
120
  export type GuestCloseParam = {
106
121
  /**
@@ -0,0 +1 @@
1
+ export {};
@@ -2,3 +2,4 @@ import { GenericFunction } from './types.js';
2
2
  export declare const getOrigin: (url: string) => string;
3
3
  export declare const flatten: (source: unknown[]) => unknown[];
4
4
  export declare function isFunction(value: any): value is GenericFunction;
5
+ export declare const isTrustedDomain: (url: string) => boolean;