@elliemae/ssf-guest 2.24.0 → 2.25.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 (36) hide show
  1. package/dist/cjs/guest.js +11 -5
  2. package/dist/cjs/tests/utils.js +6 -2
  3. package/dist/esm/guest.js +13 -6
  4. package/dist/esm/tests/utils.js +6 -2
  5. package/dist/public/callchain-grandchild.html +1 -1
  6. package/dist/public/creditService.html +1 -1
  7. package/dist/public/e2e-guest.html +1 -1
  8. package/dist/public/index.html +1 -1
  9. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js +3 -0
  10. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js.br +0 -0
  11. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js.gz +0 -0
  12. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js.map +1 -0
  13. package/dist/public/loanValidation.html +1 -1
  14. package/dist/public/pricingService.html +1 -1
  15. package/dist/public/titleService.html +1 -1
  16. package/dist/public/util.js +1 -1
  17. package/dist/public/util.js.br +0 -0
  18. package/dist/public/util.js.gz +0 -0
  19. package/dist/public/util.js.map +1 -1
  20. package/dist/public/v2-guest.html +1 -1
  21. package/dist/types/lib/guest.d.ts +16 -0
  22. package/dist/types/lib/tests/utils.d.ts +3 -1
  23. package/dist/types/tsconfig.tsbuildinfo +1 -1
  24. package/dist/umd/index.js +1 -1
  25. package/dist/umd/index.js.br +0 -0
  26. package/dist/umd/index.js.gz +0 -0
  27. package/dist/umd/index.js.map +1 -1
  28. package/dist/umd/util.js +1 -1
  29. package/dist/umd/util.js.br +0 -0
  30. package/dist/umd/util.js.gz +0 -0
  31. package/dist/umd/util.js.map +1 -1
  32. package/package.json +2 -2
  33. package/dist/public/js/emuiSsfGuest.bb7f9a5114e5717cda3d.js +0 -3
  34. package/dist/public/js/emuiSsfGuest.bb7f9a5114e5717cda3d.js.br +0 -0
  35. package/dist/public/js/emuiSsfGuest.bb7f9a5114e5717cda3d.js.gz +0 -0
  36. package/dist/public/js/emuiSsfGuest.bb7f9a5114e5717cda3d.js.map +0 -1
package/dist/cjs/guest.js CHANGED
@@ -146,6 +146,7 @@ class SSFGuest {
146
146
  * throttled keep alive function
147
147
  */
148
148
  #throttledKeepAlive = null;
149
+ #auditThrottler;
149
150
  /**
150
151
  * Create new guest
151
152
  * @param {GuestOption} options - options for the guest
@@ -171,6 +172,11 @@ class SSFGuest {
171
172
  team,
172
173
  appName
173
174
  });
175
+ this.#auditThrottler = new import_microfe_common.AuditThrottler({
176
+ logger: this.#logger,
177
+ enabled: options?.auditOperations,
178
+ throttleMs: options?.auditThrottleMs
179
+ });
174
180
  (0, import_pui_diagnostics.webvitals)(this.#logger);
175
181
  (0, import_pui_diagnostics.logUnhandledErrors)(this.#logger);
176
182
  this.#correlationId = (0, import_uuid.v4)();
@@ -280,7 +286,7 @@ class SSFGuest {
280
286
  }
281
287
  });
282
288
  const retVal = this.#handleResponse(response);
283
- this.#logger.audit({
289
+ this.#auditThrottler.log(`invoke:${objectId}.${functionName}`, {
284
290
  message: "Guest proxy invoked Scripting Object method",
285
291
  scriptingObject: objectId,
286
292
  scriptingMethod: functionName,
@@ -468,14 +474,14 @@ class SSFGuest {
468
474
  requestId,
469
475
  response: values
470
476
  });
471
- this.#logger.audit({
477
+ this.#auditThrottler.log(`event:${eventId}`, {
472
478
  message: "Guest proxy processed event from host and responded",
473
479
  scriptingEventId: eventId,
474
480
  scriptingObject: object.id,
475
481
  ...this.#getGuestInfo()
476
482
  });
477
483
  } else {
478
- this.#logger.audit({
484
+ this.#auditThrottler.log(`event:${eventId}`, {
479
485
  message: "Guest proxy processed event from host",
480
486
  scriptingEventId: eventId,
481
487
  scriptingObject: object.id,
@@ -752,7 +758,7 @@ class SSFGuest {
752
758
  }
753
759
  });
754
760
  const obj = this.#fromJSON(response.object);
755
- this.#logger.audit({
761
+ this.#auditThrottler.log(`getObject:${objectId}`, {
756
762
  message: "Received scripting object from host",
757
763
  scriptingObject: objectId,
758
764
  ...this.#getGuestInfo()
@@ -775,7 +781,7 @@ class SSFGuest {
775
781
  messageType: import_microfe_common.MessageType.ListObjects,
776
782
  messageBody: {}
777
783
  });
778
- this.#logger.audit({
784
+ this.#auditThrottler.log("listObjects", {
779
785
  message: "Received names of all scripting objects exposed by host",
780
786
  objects,
781
787
  ...this.#getGuestInfo()
@@ -34,7 +34,9 @@ const createGuest = ({
34
34
  keepAlive,
35
35
  usesDevConnectAPI,
36
36
  keepAliveInterval,
37
- console
37
+ console,
38
+ auditOperations,
39
+ auditThrottleMs
38
40
  }) => new import__.SSFGuest({
39
41
  logger: {
40
42
  console: console ?? true,
@@ -44,7 +46,9 @@ const createGuest = ({
44
46
  },
45
47
  keepAlive,
46
48
  usesDevConnectAPI,
47
- keepAliveInterval
49
+ keepAliveInterval,
50
+ auditOperations,
51
+ auditThrottleMs
48
52
  });
49
53
  const postMessage = ({
50
54
  srcWindow,
package/dist/esm/guest.js CHANGED
@@ -6,7 +6,8 @@ import {
6
6
  MessageType,
7
7
  getEventId,
8
8
  ProxyEvent,
9
- ScriptingObjectProxy
9
+ ScriptingObjectProxy,
10
+ AuditThrottler
10
11
  } from "@elliemae/microfe-common";
11
12
  import {
12
13
  logger as puiLogger,
@@ -131,6 +132,7 @@ class SSFGuest {
131
132
  * throttled keep alive function
132
133
  */
133
134
  #throttledKeepAlive = null;
135
+ #auditThrottler;
134
136
  /**
135
137
  * Create new guest
136
138
  * @param {GuestOption} options - options for the guest
@@ -156,6 +158,11 @@ class SSFGuest {
156
158
  team,
157
159
  appName
158
160
  });
161
+ this.#auditThrottler = new AuditThrottler({
162
+ logger: this.#logger,
163
+ enabled: options?.auditOperations,
164
+ throttleMs: options?.auditThrottleMs
165
+ });
159
166
  webvitals(this.#logger);
160
167
  logUnhandledErrors(this.#logger);
161
168
  this.#correlationId = uuidv4();
@@ -265,7 +272,7 @@ class SSFGuest {
265
272
  }
266
273
  });
267
274
  const retVal = this.#handleResponse(response);
268
- this.#logger.audit({
275
+ this.#auditThrottler.log(`invoke:${objectId}.${functionName}`, {
269
276
  message: "Guest proxy invoked Scripting Object method",
270
277
  scriptingObject: objectId,
271
278
  scriptingMethod: functionName,
@@ -453,14 +460,14 @@ class SSFGuest {
453
460
  requestId,
454
461
  response: values
455
462
  });
456
- this.#logger.audit({
463
+ this.#auditThrottler.log(`event:${eventId}`, {
457
464
  message: "Guest proxy processed event from host and responded",
458
465
  scriptingEventId: eventId,
459
466
  scriptingObject: object.id,
460
467
  ...this.#getGuestInfo()
461
468
  });
462
469
  } else {
463
- this.#logger.audit({
470
+ this.#auditThrottler.log(`event:${eventId}`, {
464
471
  message: "Guest proxy processed event from host",
465
472
  scriptingEventId: eventId,
466
473
  scriptingObject: object.id,
@@ -737,7 +744,7 @@ class SSFGuest {
737
744
  }
738
745
  });
739
746
  const obj = this.#fromJSON(response.object);
740
- this.#logger.audit({
747
+ this.#auditThrottler.log(`getObject:${objectId}`, {
741
748
  message: "Received scripting object from host",
742
749
  scriptingObject: objectId,
743
750
  ...this.#getGuestInfo()
@@ -760,7 +767,7 @@ class SSFGuest {
760
767
  messageType: MessageType.ListObjects,
761
768
  messageBody: {}
762
769
  });
763
- this.#logger.audit({
770
+ this.#auditThrottler.log("listObjects", {
764
771
  message: "Received names of all scripting objects exposed by host",
765
772
  objects,
766
773
  ...this.#getGuestInfo()
@@ -11,7 +11,9 @@ const createGuest = ({
11
11
  keepAlive,
12
12
  usesDevConnectAPI,
13
13
  keepAliveInterval,
14
- console
14
+ console,
15
+ auditOperations,
16
+ auditThrottleMs
15
17
  }) => new SSFGuest({
16
18
  logger: {
17
19
  console: console ?? true,
@@ -21,7 +23,9 @@ const createGuest = ({
21
23
  },
22
24
  keepAlive,
23
25
  usesDevConnectAPI,
24
- keepAliveInterval
26
+ keepAliveInterval,
27
+ auditOperations,
28
+ auditThrottleMs
25
29
  });
26
30
  const postMessage = ({
27
31
  srcWindow,
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Grandchild Guest (C)</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/emuiSsfGuest.bb7f9a5114e5717cda3d.js"></script></head><body class="bg-green-50 p-2"><h3 class="text-sm font-semibold text-green-800 mb-2">Grandchild Guest (C)</h3><div class="flex flex-wrap gap-2 mb-2"><button data-testid="btn-get-caller-info" id="btnGetCallerInfo" disabled="disabled" class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed">Call getCallerInfo()</button> <button data-testid="btn-ping" id="btnPing" disabled="disabled" class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed">Call ping("hello from C")</button> <button data-testid="btn-get-loan" id="btnGetLoan" disabled="disabled" class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed">Call getLoanDetails()</button></div><div data-testid="grandchild-status" id="status" class="text-xs bg-green-100 rounded p-2 mb-2 min-h-[20px]">Connecting...</div><div data-testid="grandchild-result" id="result" class="text-xs bg-white rounded p-2 min-h-[40px]"><pre id="resultPre" class="whitespace-pre-wrap">No results yet</pre></div><script src="./callchain-grandchild.js" type="module"></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>Grandchild Guest (C)</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/emuiSsfGuest.42c1a5949e7eb13de9e0.js"></script></head><body class="bg-green-50 p-2"><h3 class="text-sm font-semibold text-green-800 mb-2">Grandchild Guest (C)</h3><div class="flex flex-wrap gap-2 mb-2"><button data-testid="btn-get-caller-info" id="btnGetCallerInfo" disabled="disabled" class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed">Call getCallerInfo()</button> <button data-testid="btn-ping" id="btnPing" disabled="disabled" class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed">Call ping("hello from C")</button> <button data-testid="btn-get-loan" id="btnGetLoan" disabled="disabled" class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed">Call getLoanDetails()</button></div><div data-testid="grandchild-status" id="status" class="text-xs bg-green-100 rounded p-2 mb-2 min-h-[20px]">Connecting...</div><div data-testid="grandchild-result" id="result" class="text-xs bg-white rounded p-2 min-h-[40px]"><pre id="resultPre" class="whitespace-pre-wrap">No results yet</pre></div><script src="./callchain-grandchild.js" type="module"></script></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>Credit Service</title><style>body{margin:0}</style><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script>window.addEventListener("DOMContentLoaded",async()=>{window.__ICE__={diagnosticsUrl:"https://int.api.ellielabs.com/diagnostics/v2/logging"};const e=new URL(window.location),i=e?.searchParams?.get?.("src");window.__ICE__.ssfGuest=new ice.guest.SSFGuest({logger:{index:"creditServiceGuest",team:"ui platform",appName:"credit-service"}}),await window.__ICE__.ssfGuest.addScript(i,document.body)})</script><script defer="defer" src="js/emuiSsfGuest.bb7f9a5114e5717cda3d.js"></script></head><body></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>Credit Service</title><style>body{margin:0}</style><script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script><script>window.addEventListener("DOMContentLoaded",async()=>{window.__ICE__={diagnosticsUrl:"https://int.api.ellielabs.com/diagnostics/v2/logging"};const e=new URL(window.location),i=e?.searchParams?.get?.("src");window.__ICE__.ssfGuest=new ice.guest.SSFGuest({logger:{index:"creditServiceGuest",team:"ui platform",appName:"credit-service"}}),await window.__ICE__.ssfGuest.addScript(i,document.body)})</script><script defer="defer" src="js/emuiSsfGuest.42c1a5949e7eb13de9e0.js"></script></head><body></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>SSF E2E Guest</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/emuiSsfGuest.bb7f9a5114e5717cda3d.js"></script></head><body class="bg-emerald-50 p-2 text-xs"><div class="flex items-center justify-between mb-1"><h3 class="font-semibold text-emerald-800">E2E Guest</h3><span data-testid="guest-connection-status" id="connStatus" class="px-2 py-0.5 rounded text-white bg-gray-400">disconnected</span></div><div data-testid="guest-info" id="guestInfo" class="bg-emerald-100 rounded p-1 mb-1 text-xs text-emerald-700">Params: <span id="searchParams">—</span></div><details class="bg-emerald-100 rounded p-1 mb-1 text-[10px] text-emerald-700"><summary class="font-semibold cursor-pointer">Quick Guide (click to expand)</summary><ul class="mt-1 list-disc list-inside space-y-0.5"><li><strong>Objects tab:</strong> Use <code>getObject()</code> to fetch a scripting object, then <code>Invoke</code> to call a method. Result + errors appear below.</li><li><strong>Events tab:</strong> Subscribe to events (with or without criteria). When the host dispatches, received events appear below. Use the Feedback dropdown to control what the callback returns.</li><li><strong>Lifecycle tab:</strong> Change log level or disconnect/reconnect. Status badge at top right should reflect the state.</li><li><strong>Verify:</strong> Connection badge = <span class="text-green-700 font-bold">connected</span>. After getObject: methods are listed. After invoke: result appears. After subscribe+dispatch: events appear.</li></ul></details><div class="flex gap-1 mb-2 border-b border-emerald-300"><button data-testid="tab-objects" class="tab-btn px-2 py-1 rounded-t font-medium bg-emerald-200 text-emerald-800" data-tab="objects">Objects</button> <button data-testid="tab-events" class="tab-btn px-2 py-1 rounded-t text-emerald-600" data-tab="events">Events</button> <button data-testid="tab-lifecycle" class="tab-btn px-2 py-1 rounded-t text-emerald-600" data-tab="lifecycle">Lifecycle</button></div><div id="panel-objects" class="tab-panel"><div class="space-y-1"><button data-testid="btn-list-objects" id="btnListObjects" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">listObjects()</button><div data-testid="objects-list" id="objectsList" class="bg-white rounded p-1 min-h-[20px]">—</div><div class="flex gap-1 items-center"><input data-testid="input-object-id" id="inputObjectId" value="Loan" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder="Object ID"/> <button data-testid="btn-get-object" id="btnGetObject" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">getObject()</button></div><div data-testid="current-object" id="currentObject" class="bg-white rounded p-1 min-h-[20px]">—</div><div class="flex gap-1 items-center"><input data-testid="input-method" id="inputMethod" value="getLoanDetails" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder="Method name"/> <input data-testid="input-args" id="inputArgs" class="rounded border-emerald-300 px-2 py-0.5 text-xs w-24" placeholder="Args (JSON)"/> <button data-testid="btn-invoke" id="btnInvoke" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">Invoke</button></div><div class="flex gap-1"><button data-testid="btn-invoke-error" id="btnInvokeError" class="rounded bg-red-500 px-2 py-1 text-white hover:bg-red-600">TC-SO-08: Call throwError()</button> <button data-testid="btn-invoke-async" id="btnInvokeAsync" class="rounded bg-blue-500 px-2 py-1 text-white hover:bg-blue-600">TC-SO-07: Call asyncMethod(200)</button></div><div data-testid="invoke-result" id="invokeResult" class="bg-white rounded p-1 min-h-[30px] whitespace-pre-wrap">—</div></div></div><div id="panel-events" class="tab-panel hidden"><div class="space-y-1"><div class="flex gap-1 items-center"><input data-testid="input-event-id" id="inputEventId" value="Loan.onPreSave" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder="Event ID"/> <button data-testid="btn-subscribe" id="btnSubscribe" class="rounded bg-purple-600 px-2 py-1 text-white hover:bg-purple-700">Subscribe</button></div><div class="flex gap-1 items-center"><input data-testid="input-criteria" id="inputCriteria" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder='Criteria JSON e.g. {"amount":{"gt":100000}}'/> <button data-testid="btn-subscribe-criteria" id="btnSubscribeCriteria" class="rounded bg-purple-600 px-2 py-1 text-white hover:bg-purple-700">Subscribe+Criteria</button></div><div class="flex gap-1 items-center"><select data-testid="select-unsub-token" id="selectUnsubToken" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"><option value="">— select token —</option></select> <button data-testid="btn-unsubscribe" id="btnUnsubscribe" class="rounded bg-yellow-600 px-2 py-1 text-white hover:bg-yellow-700">Unsubscribe</button></div><h4 class="font-medium text-emerald-700 mt-1">Active Subscriptions</h4><div data-testid="subscriptions-list" id="subscriptionsList" class="bg-white rounded p-1 min-h-[20px]">—</div><h4 class="font-medium text-emerald-700 mt-1">Received Events</h4><div data-testid="events-received" id="eventsReceived" class="bg-white rounded p-1 min-h-[40px] max-h-[120px] overflow-y-auto">—</div><h4 class="font-medium text-emerald-700 mt-1">Feedback Return Value</h4><div class="flex gap-1 items-center"><select data-testid="select-feedback" id="selectFeedback" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"><option value="true">return true (approve)</option><option value="false">return false (reject)</option><option value="object">return {status: "ok"}</option></select></div></div></div><div id="panel-lifecycle" class="tab-panel hidden"><div class="space-y-1"><button data-testid="btn-set-loglevel-0" id="btnLogLevel0" class="rounded bg-gray-600 px-2 py-1 text-white hover:bg-gray-700">setLogLevel(0) — Silent</button> <button data-testid="btn-set-loglevel-10" id="btnLogLevel10" class="rounded bg-gray-600 px-2 py-1 text-white hover:bg-gray-700">setLogLevel(10) — Debug</button> <button data-testid="btn-disconnect" id="btnDisconnect" class="rounded bg-red-600 px-2 py-1 text-white hover:bg-red-700">close() — Disconnect</button><p class="text-[10px] text-gray-400">Expected: status badge → "disconnected", further calls fail</p><button data-testid="btn-reconnect" id="btnReconnect" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">connect() — Reconnect</button><p class="text-[10px] text-gray-400">Expected: status badge → "connected", calls work again</p></div></div><div class="mt-2"><h4 class="font-medium text-emerald-700">Guest Log</h4><div data-testid="guest-log" id="guestLog" class="bg-white rounded p-1 text-xs min-h-[40px] max-h-[100px] overflow-y-auto font-mono"></div></div><script src="./e2e-guest.js" type="module"></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>SSF E2E Guest</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/emuiSsfGuest.42c1a5949e7eb13de9e0.js"></script></head><body class="bg-emerald-50 p-2 text-xs"><div class="flex items-center justify-between mb-1"><h3 class="font-semibold text-emerald-800">E2E Guest</h3><span data-testid="guest-connection-status" id="connStatus" class="px-2 py-0.5 rounded text-white bg-gray-400">disconnected</span></div><div data-testid="guest-info" id="guestInfo" class="bg-emerald-100 rounded p-1 mb-1 text-xs text-emerald-700">Params: <span id="searchParams">—</span></div><details class="bg-emerald-100 rounded p-1 mb-1 text-[10px] text-emerald-700"><summary class="font-semibold cursor-pointer">Quick Guide (click to expand)</summary><ul class="mt-1 list-disc list-inside space-y-0.5"><li><strong>Objects tab:</strong> Use <code>getObject()</code> to fetch a scripting object, then <code>Invoke</code> to call a method. Result + errors appear below.</li><li><strong>Events tab:</strong> Subscribe to events (with or without criteria). When the host dispatches, received events appear below. Use the Feedback dropdown to control what the callback returns.</li><li><strong>Lifecycle tab:</strong> Change log level or disconnect/reconnect. Status badge at top right should reflect the state.</li><li><strong>Verify:</strong> Connection badge = <span class="text-green-700 font-bold">connected</span>. After getObject: methods are listed. After invoke: result appears. After subscribe+dispatch: events appear.</li></ul></details><div class="flex gap-1 mb-2 border-b border-emerald-300"><button data-testid="tab-objects" class="tab-btn px-2 py-1 rounded-t font-medium bg-emerald-200 text-emerald-800" data-tab="objects">Objects</button> <button data-testid="tab-events" class="tab-btn px-2 py-1 rounded-t text-emerald-600" data-tab="events">Events</button> <button data-testid="tab-lifecycle" class="tab-btn px-2 py-1 rounded-t text-emerald-600" data-tab="lifecycle">Lifecycle</button></div><div id="panel-objects" class="tab-panel"><div class="space-y-1"><button data-testid="btn-list-objects" id="btnListObjects" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">listObjects()</button><div data-testid="objects-list" id="objectsList" class="bg-white rounded p-1 min-h-[20px]">—</div><div class="flex gap-1 items-center"><input data-testid="input-object-id" id="inputObjectId" value="Loan" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder="Object ID"/> <button data-testid="btn-get-object" id="btnGetObject" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">getObject()</button></div><div data-testid="current-object" id="currentObject" class="bg-white rounded p-1 min-h-[20px]">—</div><div class="flex gap-1 items-center"><input data-testid="input-method" id="inputMethod" value="getLoanDetails" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder="Method name"/> <input data-testid="input-args" id="inputArgs" class="rounded border-emerald-300 px-2 py-0.5 text-xs w-24" placeholder="Args (JSON)"/> <button data-testid="btn-invoke" id="btnInvoke" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">Invoke</button></div><div class="flex gap-1"><button data-testid="btn-invoke-error" id="btnInvokeError" class="rounded bg-red-500 px-2 py-1 text-white hover:bg-red-600">TC-SO-08: Call throwError()</button> <button data-testid="btn-invoke-async" id="btnInvokeAsync" class="rounded bg-blue-500 px-2 py-1 text-white hover:bg-blue-600">TC-SO-07: Call asyncMethod(200)</button></div><div data-testid="invoke-result" id="invokeResult" class="bg-white rounded p-1 min-h-[30px] whitespace-pre-wrap">—</div></div></div><div id="panel-events" class="tab-panel hidden"><div class="space-y-1"><div class="flex gap-1 items-center"><input data-testid="input-event-id" id="inputEventId" value="Loan.onPreSave" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder="Event ID"/> <button data-testid="btn-subscribe" id="btnSubscribe" class="rounded bg-purple-600 px-2 py-1 text-white hover:bg-purple-700">Subscribe</button></div><div class="flex gap-1 items-center"><input data-testid="input-criteria" id="inputCriteria" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1" placeholder='Criteria JSON e.g. {"amount":{"gt":100000}}'/> <button data-testid="btn-subscribe-criteria" id="btnSubscribeCriteria" class="rounded bg-purple-600 px-2 py-1 text-white hover:bg-purple-700">Subscribe+Criteria</button></div><div class="flex gap-1 items-center"><select data-testid="select-unsub-token" id="selectUnsubToken" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"><option value="">— select token —</option></select> <button data-testid="btn-unsubscribe" id="btnUnsubscribe" class="rounded bg-yellow-600 px-2 py-1 text-white hover:bg-yellow-700">Unsubscribe</button></div><h4 class="font-medium text-emerald-700 mt-1">Active Subscriptions</h4><div data-testid="subscriptions-list" id="subscriptionsList" class="bg-white rounded p-1 min-h-[20px]">—</div><h4 class="font-medium text-emerald-700 mt-1">Received Events</h4><div data-testid="events-received" id="eventsReceived" class="bg-white rounded p-1 min-h-[40px] max-h-[120px] overflow-y-auto">—</div><h4 class="font-medium text-emerald-700 mt-1">Feedback Return Value</h4><div class="flex gap-1 items-center"><select data-testid="select-feedback" id="selectFeedback" class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"><option value="true">return true (approve)</option><option value="false">return false (reject)</option><option value="object">return {status: "ok"}</option></select></div></div></div><div id="panel-lifecycle" class="tab-panel hidden"><div class="space-y-1"><button data-testid="btn-set-loglevel-0" id="btnLogLevel0" class="rounded bg-gray-600 px-2 py-1 text-white hover:bg-gray-700">setLogLevel(0) — Silent</button> <button data-testid="btn-set-loglevel-10" id="btnLogLevel10" class="rounded bg-gray-600 px-2 py-1 text-white hover:bg-gray-700">setLogLevel(10) — Debug</button> <button data-testid="btn-disconnect" id="btnDisconnect" class="rounded bg-red-600 px-2 py-1 text-white hover:bg-red-700">close() — Disconnect</button><p class="text-[10px] text-gray-400">Expected: status badge → "disconnected", further calls fail</p><button data-testid="btn-reconnect" id="btnReconnect" class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700">connect() — Reconnect</button><p class="text-[10px] text-gray-400">Expected: status badge → "connected", calls work again</p></div></div><div class="mt-2"><h4 class="font-medium text-emerald-700">Guest Log</h4><div data-testid="guest-log" id="guestLog" class="bg-white rounded p-1 text-xs min-h-[40px] max-h-[100px] overflow-y-auto font-mono"></div></div><script src="./e2e-guest.js" type="module"></script></body></html>
@@ -1 +1 @@
1
- <!doctype html><html lang="en" style="height:100%"><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>Plugin</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script defer="defer" src="js/emuiSsfGuest.bb7f9a5114e5717cda3d.js"></script></head><body class="px-2 h-full"><main class="h-full"><h1 class="text-md font-bold">Credit Score Service</h1><div class="h-full mt-2"><output id="msg" class="mt-2 p-2"></output></div></main></body></html>
1
+ <!doctype html><html lang="en" style="height:100%"><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>Plugin</title><script src="https://cdn.tailwindcss.com?plugins=forms"></script><script defer="defer" src="js/emuiSsfGuest.42c1a5949e7eb13de9e0.js"></script></head><body class="px-2 h-full"><main class="h-full"><h1 class="text-md font-bold">Credit Score Service</h1><div class="h-full mt-2"><output id="msg" class="mt-2 p-2"></output></div></main></body></html>
@@ -0,0 +1,3 @@
1
+ (function(q,R){typeof exports=="object"&&typeof module=="object"?module.exports=R(require("emuiDiagnostics")):typeof define=="function"&&define.amd?define(["emuiDiagnostics"],R):typeof exports=="object"?exports.ice=R(require("emuiDiagnostics")):(q.ice=q.ice||{},q.ice.guest=R(q.emuiDiagnostics))})(globalThis,J=>(()=>{var q={572(a,l,c){var g=c(736),h=g.Symbol;a.exports=h},561(a,l,c){var g=c(572),h=c(504),w=c(567),u="[object Null]",f="[object Undefined]",I=g?g.toStringTag:void 0;function v(y){return y==null?y===void 0?f:u:I&&I in Object(y)?h(y):w(y)}a.exports=v},605(a,l,c){var g=c(327),h=/^\s+/;function w(u){return u&&u.slice(0,g(u)+1).replace(h,"")}a.exports=w},433(a){var l=typeof globalThis=="object"&&globalThis&&globalThis.Object===Object&&globalThis;a.exports=l},504(a,l,c){var g=c(572),h=Object.prototype,w=h.hasOwnProperty,u=h.toString,f=g?g.toStringTag:void 0;function I(v){var y=w.call(v,f),b=v[f];try{v[f]=void 0;var m=!0}catch{}var T=u.call(v);return m&&(y?v[f]=b:delete v[f]),T}a.exports=I},567(a){var l=Object.prototype,c=l.toString;function g(h){return c.call(h)}a.exports=g},736(a,l,c){var g=c(433),h=typeof self=="object"&&self&&self.Object===Object&&self,w=g||h||Function("return this")();a.exports=w},327(a){var l=/\s/;function c(g){for(var h=g.length;h--&&l.test(g.charAt(h)););return h}a.exports=c},702(a,l,c){var g=c(230),h=c(381),w=c(569),u="Expected a function",f=Math.max,I=Math.min;function v(y,b,m){var T,k,U,P,E,_,A=0,S=!1,C=!1,H=!0;if(typeof y!="function")throw new TypeError(u);b=w(b)||0,g(m)&&(S=!!m.leading,C="maxWait"in m,U=C?f(w(m.maxWait)||0,b):U,H="trailing"in m?!!m.trailing:H);function N(p){var $=T,M=k;return T=k=void 0,A=p,P=y.apply(M,$),P}function Y(p){return A=p,E=setTimeout(D,b),S?N(p):P}function X(p){var $=p-_,M=p-A,B=b-$;return C?I(B,U-M):B}function Q(p){var $=p-_,M=p-A;return _===void 0||$>=b||$<0||C&&M>=U}function D(){var p=h();if(Q(p))return G(p);E=setTimeout(D,X(p))}function G(p){return E=void 0,H&&T?N(p):(T=k=void 0,P)}function V(){E!==void 0&&clearTimeout(E),A=0,T=_=k=E=void 0}function z(){return E===void 0?P:G(h())}function W(){var p=h(),$=Q(p);if(T=arguments,k=this,_=p,$){if(E===void 0)return Y(_);if(C)return clearTimeout(E),E=setTimeout(D,b),N(_)}return E===void 0&&(E=setTimeout(D,b)),P}return W.cancel=V,W.flush=z,W}a.exports=v},230(a){function l(c){var g=typeof c;return c!=null&&(g=="object"||g=="function")}a.exports=l},470(a){function l(c){return c!=null&&typeof c=="object"}a.exports=l},89(a,l,c){var g=c(561),h=c(470),w="[object Symbol]";function u(f){return typeof f=="symbol"||h(f)&&g(f)==w}a.exports=u},381(a,l,c){var g=c(736),h=function(){return g.Date.now()};a.exports=h},345(a,l,c){var g=c(702),h=c(230),w="Expected a function";function u(f,I,v){var y=!0,b=!0;if(typeof f!="function")throw new TypeError(w);return h(v)&&(y="leading"in v?!!v.leading:y,b="trailing"in v?!!v.trailing:b),g(f,I,{leading:y,maxWait:I,trailing:b})}a.exports=u},569(a,l,c){var g=c(605),h=c(230),w=c(89),u=NaN,f=/^[-+]0x[0-9a-f]+$/i,I=/^0b[01]+$/i,v=/^0o[0-7]+$/i,y=parseInt;function b(m){if(typeof m=="number")return m;if(w(m))return u;if(h(m)){var T=typeof m.valueOf=="function"?m.valueOf():m;m=h(T)?T+"":T}if(typeof m!="string")return m===0?m:+m;m=g(m);var k=I.test(m);return k||v.test(m)?y(m.slice(2),k?2:8):f.test(m)?u:+m}a.exports=b},388(a){"use strict";a.exports=J}},R={};function x(a){var l=R[a];if(l!==void 0)return l.exports;var c=R[a]={exports:{}};return q[a](c,c.exports,x),c.exports}x.n=a=>{var l=a&&a.__esModule?()=>a.default:()=>a;return x.d(l,{a:l}),l},x.d=(a,l)=>{for(var c in l)x.o(l,c)&&!x.o(a,c)&&Object.defineProperty(a,c,{enumerable:!0,get:l[c]})},x.o=(a,l)=>Object.prototype.hasOwnProperty.call(a,l),x.r=a=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})};var K={};return(()=>{"use strict";x.r(K),x.d(K,{SSFGuest:()=>de});const l={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};let c;const g=new Uint8Array(16);function h(){if(!c&&(c=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!c))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return c(g)}var w;const u=[];for(let n=0;n<256;++n)u.push((n+256).toString(16).slice(1));function f(n,e=0){return u[n[e+0]]+u[n[e+1]]+u[n[e+2]]+u[n[e+3]]+"-"+u[n[e+4]]+u[n[e+5]]+"-"+u[n[e+6]]+u[n[e+7]]+"-"+u[n[e+8]]+u[n[e+9]]+"-"+u[n[e+10]]+u[n[e+11]]+u[n[e+12]]+u[n[e+13]]+u[n[e+14]]+u[n[e+15]]}function I(n,e=0){const t=f(n,e);if(!w(t))throw TypeError("Stringified UUID is invalid");return t}const v=null;function y(n,e,t){if(l.randomUUID&&!e&&!n)return l.randomUUID();n=n||{};const s=n.random||(n.rng||h)();if(s[6]=s[6]&15|64,s[8]=s[8]&63|128,e){t=t||0;for(let i=0;i<16;++i)e[t+i]=s[i];return e}return f(s)}const b=y;var m=x(345),T=x.n(m);const k="elli:remoting",U="elli:remoting:response",P="elli:remoting:exception",E=({messageType:n,messageBody:e,requestId:t,onewayMsg:s=!1})=>({requestId:t??(s?null:b()),source:k,type:n,body:e}),_=n=>{const{targetWin:e,targetOrigin:t,messageType:s,messageBody:i}=n,r=E({messageType:s,messageBody:i});e.postMessage(r,t)};class A{#s;#e;#t=new Map;#n=new Map;#r=null;#i=null;#a=new Map;constructor(e,t){if(!e)throw new Error("logger is required");if(!t)throw new Error("correlationId is required");this.#s=t,this.#e=e}#f=()=>{this.#r=null;const e=Date.now(),t=[];let s=null;if(this.#n.forEach((i,r)=>{const{requestId:o,cancelTime:d}=i;d&&d<=e?(this.#e.debug(`Detected response timeout for requestId: ${o}...`),t.push(r),i.resolve(void 0)):d&&(s=s===null?d:Math.min(s,d))}),t.forEach(i=>{this.#n.delete(i)}),s!==null){const i=Math.max(s-Date.now(),0);this.#g(i)}};#g=e=>{this.#r===null&&(this.#r=window.setTimeout(this.#f,e))};#b=()=>{this.#r!==null&&(window.clearTimeout(this.#r),this.#r=null)};#o=e=>{const t=this.#n.get(e);return this.#e.debug(`serving requestId: ${e}`),this.#n.delete(e),t};#c=e=>{const{requestId:t}=e;this.#e.debug(`Response received for invocation requestId: ${t}`);const s=this.#o(t);return s?(s.resolve(e.body),!0):(this.#e.debug(`Received response to stale/invalid request with requestId: ${t}`),!1)};#h=e=>{this.#e.debug(`Exception received for invocation (requestId = ${e.requestId})`);const t=this.#o(e.requestId);return t?(t.reject(new Error(e.body)),!0):(this.#e.warn(`Received exception for stale/invalid request (requestId = ${e.requestId})`),!1)};#w=({sourceWin:e,sourceOrigin:t,message:s})=>{this.#e.debug(`Received message of type "${s.type}"`);const i=this.#t.get(s.type);return i?(i.forEach(r=>{this.#e.debug(`Invoking message handler ${r.name}`),r({sourceWin:e,sourceOrigin:t,requestId:s.requestId,type:s.type,body:s.body})}),!0):!1};#l=e=>{if(this.#a.size===0||!e.source)return!1;const t=this.#a.get(e.source);return!t||e?.data?.source!==k?!1:(this.#e.debug(`Remoting: Received message of type "${e.data.type}"`),e.data.type===U?this.#c(e.data):e.data.type===P?this.#h(e.data):this.#w({sourceWin:e.source,sourceOrigin:t,message:e.data}),!0)};addSender=e=>{const{origin:t,window:s}=e;if(!t)throw new Error("origin is required");if(!s)throw new Error("window is required");this.#a.set(s,t)};initialize=e=>{this.#i&&this.#i.removeEventListener("message",this.#l),e.addEventListener("message",this.#l),this.#i=e,this.#e.debug(`initialized remoting id: ${this.#s}`)};close=()=>{this.#i&&(this.#i.removeEventListener("message",this.#l),this.#i=null),this.#b(),this.#e.debug(`closed remoting id: ${this.#s}`)};invoke=e=>{const{targetWin:t,targetOrigin:s,messageType:i,messageBody:r,responseTimeoutMs:o}=e;return new Promise((d,O)=>{const j=E({messageType:i,messageBody:r});this.#n.set(j.requestId,{requestId:j.requestId,resolve:d,reject:O,cancelTime:o?Date.now()+o:null}),t.postMessage(j,s);const{requestId:L}=j;this.#e.debug(`Posted invocation message of type ${i} requestId: ${L||""}`),o&&(this.#e.debug(`scheduling timeout check for requestId: ${L||""} in ${o} ms`),this.#g(o))})};listen=e=>{const{messageType:t,callback:s}=e,i=this.#t.get(t)||[];i.push(s),this.#t.set(t,i)};unlisten=e=>{const{messageType:t,callback:s}=e,i=this.#t.get(t);if(!i)return;const r=i.indexOf(s);r!==-1&&i.splice(r,1)};send=e=>{const{targetWin:t,targetOrigin:s,messageType:i,messageBody:r}=e,o=E({messageType:i,messageBody:r,onewayMsg:!0});t.postMessage(o,s),this.#e.debug(`Posted one-way message of type "${i}"`)};removeSender=e=>{const{window:t}=e;t&&this.#a.delete(t)};respond=e=>{const{targetWin:t,targetOrigin:s,requestId:i,response:r}=e,o=E({messageType:U,messageBody:r,requestId:i});t.postMessage(o,s),this.#e.debug(`Response sent to caller for invocation requestId: ${i}`)};raiseException=e=>{const{targetWin:t,targetOrigin:s,requestId:i,ex:r}=e,o=r instanceof Error?r.message:r,d=E({messageType:P,messageBody:o,requestId:i});t.postMessage(d,s),this.#e.debug(`Exception sent to caller for invocation. requestId: ${i}`)}}var S=(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))(S||{});class C{#s;#e;#t;#n=new Map;static DEFAULT_THROTTLE_MS=1e4;constructor(e){this.#s=e.logger,this.#e=e.enabled??!0,this.#t=e.throttleMs??C.DEFAULT_THROTTLE_MS}get enabled(){return this.#e}log(e,t){if(!this.#e){this.#s.debug(t);return}if(this.#t>0){const s=performance.now(),i=this.#n.get(e);if(i!==void 0&&s-i<this.#t){this.#s.debug(t);return}this.#n.set(e,s)}this.#s.audit(t)}}class H{__TYPE__="Proxy";id;objectType;constructor(e,t){this.id=e,this.objectType=t}}const N=n=>n?.constructor?.name==="Proxy"||n?.constructor?.name==="ScriptingObjectProxy"||n?.__TYPE__==="Proxy";class Y{name;objectId;id;constructor(e){const{name:t,objectId:s}=e;if(!t)throw new Error("Event name is required");if(!s)throw new Error("Scripting object id is required");this.objectId=s,this.name=t,this.id=`${this.objectId}.${this.name}`.toLowerCase()}}class X{static[Symbol.hasInstance](e){return typeof e=="object"&&e!==null&&"getType"in e&&typeof e.getType=="function"&&e.getType()==="ProxyEvent"}#s;objectId;name;id;getType(){return"ProxyEvent"}constructor(e){const{name:t,objectId:s,eventSrc:i}=e;if(!t)throw new Error("Event name is required");if(!s)throw new Error("Scripting object id is required");if(!i)throw new Error("Event source is required");this.objectId=s,this.name=t,this.#s=i,this.id=`${this.objectId}.${this.name}`.toLowerCase()}subscribe=e=>this.#s.subscribe({eventId:this.id,callback:e});unsubscribe=e=>{this.#s.unsubscribe({eventId:this.id,token:e})}}const Q=n=>n instanceof Y,D=(n,e)=>`${n.toLowerCase()}.${e.toLowerCase()}`;var G=x(388);const V=()=>{const{parent:n}=window;try{return n?.location?.href}catch{return null}},z=n=>{if(!n||n==="about:blank")return"*";try{const{origin:e}=new URL(n);return e==="null"||!e?"*":e}catch{try{const{origin:e}=new URL(n,document.baseURI);return e==="null"||!e?"*":e}catch{return"*"}}},W=()=>window.self!==window.parent,p=(n,e)=>{const t=e||document.body,s=document.defaultView?.getComputedStyle?.(t,null),i=s?s[n]:"0";return parseInt(i,10)},$=/^https?:\/\/([a-z0-9-]+\.)*(?:elliemae\.io|ellielabs\.com|ellieservices\.com|encompassloconnect\.com|ice\.com|localhost:[0-9]+)$/i,M="elli:remoting",B=3e4,re=()=>new Promise((n,e)=>{if(W())n({origin:z(document.referrer||V()),window:window.parent});else{const t=window;let s;const i=r=>{if($.test(r.origin)){const{source:o,type:d}=r.data||{};if(o===M&&d===S.HandShake){const{origin:O}=r,j=r.source;clearTimeout(s),_({targetWin:j,targetOrigin:O,messageType:S.HandShakeAck,messageBody:{}}),t.removeEventListener("message",i),n({origin:O,window:j})}}};s=setTimeout(()=>{t.removeEventListener("message",i),e(new Error(`Host handshake timed out after ${B}ms`))},B),t.addEventListener("message",i)}});let F=!1;const oe=n=>{F=n},ee=(n=null)=>{let e=n;const t=()=>{if(!e&&(e=window.ice?.guest?.SSFGuest?new window.ice.guest.SSFGuest:null,!e))throw new Error("Guest definition not found");return e};window.elli=window.elli??{},window.elli.script={guest:{create:(s,i)=>t().addScript(s,i)},connect:s=>t().connect(s),getObject:s=>t().getObject(s),setLogLevel:s=>t().setLogLevel(s),subscribe:(s,i,r,o)=>t().subscribe({eventId:`${s}.${i}`,callback:d=>r(d?.obj,d?.eventParams,d?.eventOptions),criteria:o?.criteria}),unsubscribe:(s,i,r)=>t().unsubscribe({eventId:`${s}.${i}`,token:r})}};try{ee()}catch{}var ce=(n=>(n.OBJECT="object",n.VALUE="value",n))(ce||{});const te=12e4,ae=2e3,le={eventFeedback:!0},se=()=>{const{document:n}=window,e=n.body,t=n.documentElement,i={height:e.offsetHeight+p("marginTop")+p("marginBottom"),width:Math.max(e.scrollWidth,t.scrollWidth)};_({targetWin:window.parent,targetOrigin:z(n.referrer||V()),messageType:S.GuestResize,messageBody:i}),window.removeEventListener("load",se)};W()&&window.addEventListener("load",se);const ie={"cdn.ice.com":"https://api.elliemae.com/diagnostics/v2/logging","cdn.np.ice.com":"https://stg.api.elliemae.com/diagnostics/v2/logging","cdn.uat.ice.com":"https://concept.api.elliemae.com/diagnostics/v2/logging","cdn.pt1.ice.com":"https://pel1.api.ellielabs.com/diagnostics/v2/logging","cdn.qa1.ice.com":"https://int.api.ellielabs.com/diagnostics/v2/logging","cdn.dev1.ice.com":"https://int.api.ellielabs.com/diagnostics/v2/logging","cdn.mortgagetech.ice.com":"https://api.elliemae.com/diagnostics/v2/logging","cdn.mortgagetech.s1.ice.com":"https://stg.api.elliemae.com/diagnostics/v2/logging","cdn.mortgagetech.uat1.ice.com":"https://concept.api.elliemae.com/diagnostics/v2/logging","cdn.mortgagetech.pl1.ice.com":"https://pel1.api.ellielabs.com/diagnostics/v2/logging","cdn.mortgagetech.q1.ice.com":"https://int.api.ellielabs.com/diagnostics/v2/logging","cdn.mortgagetech.d1.ice.com":"https://int.api.ellielabs.com/diagnostics/v2/logging",localhost:"https://int.api.ellielabs.com/diagnostics/v2/logging"},ne=["click","scroll","keypress","touchstart"],ue=()=>{const n=document.currentScript?.src||"file:///opt/jenkins/workspace/UIPlatform_pui-microfe_master/256/libs/ssf-guest/lib/guest.ts",e=n?new URL(n).hostname:"localhost";return ie[e]||ie["cdn.mortgagetech.q1.ice.com"]};class de{#s;#e;#t;#n=!1;#r=null;#i=!1;#a=new Map;#f=null;#g=null;#b=null;#o=null;#c=null;#h=null;#w=new Map;#l(){if(!this.#c||!this.#o)throw new Error("Guest is not connected to a host. Call connect() first.");return{hostWindow:this.#c,hostOrigin:this.#o}}#S=!1;#u=null;#m=null;#v=null;#j=!0;#O=te;#T;#y=null;#p;constructor(e){const{console:t=!1,url:s=window.__ICE__?.diagnosticsUrl||ue(),index:i="ssf-guest-v2",team:r="ICE",appName:o="SSF Guest V2 Application"}=e?.logger??{};this.#T=e?.logger,this.#S=e?.usesDevConnectAPI??!1,this.#j=e?.keepAlive??!0,this.#O=e?.keepAliveInterval??te;const d=t?(0,G.Console)():(0,G.http)(s);this.#e=e?.appLogger??(0,G.logger)({transport:d,index:i,team:r,appName:o}),this.#p=new C({logger:this.#e,enabled:e?.auditOperations,throttleMs:e?.auditThrottleMs}),(0,G.webvitals)(this.#e),(0,G.logUnhandledErrors)(this.#e),this.#s=b(),this.#t=new A(this.#e,this.#s),ee(this),this.#e.audit({message:"Guest created",guestUrl:window.location.href,correlationId:this.#s})}#d=()=>({guestId:this.#f,guestTitle:this.#g,guestUrl:this.#b});#E=e=>{if(!e||!e.objectId)throw this.#e.error("Deserialization of scripting object failed. Object does not have an Object ID."),new Error("Cannot deserialize object JSON into proxy.");const t=new H(e.objectId,e.objectType);return e.functions&&e.functions.forEach(s=>{Object.defineProperty(t,s,{value:async(...i)=>{const o=t[s]?.callContext?.callChain;return this.#x({objectId:t.id,functionName:s,functionParams:[...i],callerChain:o})},enumerable:!0})}),e.events&&e.events.forEach(s=>{Object.defineProperty(t,s,{value:new X({objectId:t.id,name:s,eventSrc:this}),enumerable:!0})}),this.#e.debug(`Created guest proxy for scripting object (id = "${t.id}")`),t};#x=async({objectId:e,functionName:t,functionParams:s,callerChain:i})=>{this.#e.debug(`Invoking scripting object method ${e}.${t}()...`);const r=[];if(s)for(let L=0;L<s.length;L+=1){const Z=s[L];Z instanceof Promise&&r.push(Z.then(ge=>{s[L]=ge}))}await Promise.all(r);const{hostWindow:o,hostOrigin:d}=this.#l(),O=await this.#t.invoke({targetWin:o,targetOrigin:d,messageType:S.ObjectInvoke,messageBody:{objectId:e,functionName:t,functionParams:s,...i?.length?{callerChain:i}:{}}}),j=this.#k(O);return this.#p.log(`invoke:${e}.${t}`,{message:"Guest proxy invoked Scripting Object method",scriptingObject:e,scriptingMethod:t,...this.#d()}),j};#I(e,t){return Object.entries(t).every(([s,i])=>{const r=e[s];if("eq"in i)return r===i.eq;if("in"in i)return Array.isArray(i.in)&&i.in.includes(r);if("includes"in i)return Array.isArray(r)&&r.includes(i.includes);if("gt"in i){const o=typeof r=="number"?r:Number(r);return!Number.isNaN(o)&&o>i.gt}if("lt"in i){const o=typeof r=="number"?r:Number(r);return!Number.isNaN(o)&&o<i.lt}if("regex"in i)try{let o=this.#w.get(i.regex);return o||(o=new RegExp(i.regex),this.#w.set(i.regex,o)),typeof r=="string"&&o.test(r)}catch{return!1}return!1})}#k=e=>e?.type==="object"&&"object"in e?this.#E(e.object):e?.type==="value"?e.value:e;#L=({body:e})=>{e?.logLevel&&(this.#e.setLogLevel(e.logLevel),this.#e.debug(`Log level changed by host to ${e.logLevel}`)),this.#f=e?.guestId??null,this.#g=e?.guestTitle??null,this.#b=e?.guestUrl??null,this.#e.audit({message:"Received configuration from Host",...this.#d()})};#_=()=>{window.focus()};#$=async()=>{await this.close(),window.close()};#P=()=>{W()||(this.#h=setInterval(()=>{this.#c?.closed&&this.close().then(()=>{window.close()}).catch(()=>{})},1e3))};#C=async({sourceWin:e,sourceOrigin:t,requestId:s,body:i})=>{const r=this.#E(i.object);if(r){const o=D(r.id,i.eventName);this.#e.debug(`Received event "${o}" from host`);let d;i.eventHandler?d=[{callback:window[i.eventHandler],token:b()}]:d=this.#a.get(o)||[];const O=[];if(d.forEach(j=>{if(j?.callback){if(j.criteria&&!(i.eventParams?this.#I(i.eventParams,j.criteria):!1)){this.#e.debug(`Event ${o} did not match criteria for subscriber ${j.callback.name}`);return}this.#e.debug(`Invoking event subscriber ${j.callback.name} for event ${o}`);const L=j.callback({obj:r,eventName:i.eventName,eventParams:i.eventParams,eventOptions:i.eventOptions});L instanceof Promise?O.push(L):typeof L<"u"&&O.push(Promise.resolve(L))}}),s){const j=await Promise.all(O);this.#t.respond({targetWin:e,targetOrigin:t,requestId:s,response:j}),this.#p.log(`event:${o}`,{message:"Guest proxy processed event from host and responded",scriptingEventId:o,scriptingObject:r.id,...this.#d()})}else this.#p.log(`event:${o}`,{message:"Guest proxy processed event from host",scriptingEventId:o,scriptingObject:r.id,...this.#d()})}};#G=async()=>{if(!(!this.#u||!this.#m||!this.#v))try{await fetch(`${this.#v}/oauth2/v1/token/introspection?${new URLSearchParams({token:this.#u,client_id:this.#m}).toString()}`,{headers:{"content-type":"application/x-www-form-urlencoded;charset=UTF-8"},method:"POST"})}catch(e){this.#e.warn(`Failed to extend token lifetime: ${e.message}`)}};#R=async()=>{if(this.#u&&this.#m&&this.#v)try{await fetch(`${this.#v}/oauth2/v1/token/revocation?${new URLSearchParams({token:this.#u,client_id:this.#m}).toString()}`,{method:"POST"})}catch(e){this.#e.error(`Error revoking token. ${e.message}`)}finally{this.#u=null,this.#m=null}};#U=async()=>{if(this.#j)try{const e=await Promise.race([this.getObject("application"),new Promise(t=>{setTimeout(()=>t(null),ae)})]);e?(this.#y=T()(async()=>{try{await e.keepSessionAlive(),await this.#G()}catch(t){this.#e.error(`Error keeping session alive. ${t.message}`)}},this.#O,{leading:!1}),ne.forEach(t=>{document.addEventListener(t,this.#y)})):this.#e.warn("Application object not available to send keep alive")}catch(e){this.#e.error(`Error getting application object. ${e.message}`)}};addScript=async(e,t)=>{if(!this.#n&&e){await this.connect();const s=document.createElement("script");s.setAttribute("src",e),s.setAttribute("type","module"),await new Promise((i,r)=>{let o,d;const O=()=>{s.removeEventListener("load",o),s.removeEventListener("error",d)};o=()=>{O(),this.#n=!0,oe(!0),i()},d=()=>{O(),r(new Error(`Failed to load script: ${e}`))},s.addEventListener("load",o),s.addEventListener("error",d),this.#r=t.appendChild(s)})}};close=async()=>{this.#i&&(this.#c&&this.#o&&(this.#t.send({targetWin:this.#c,targetOrigin:this.#o,messageType:S.GuestClose,messageBody:{}}),this.#t.removeSender({origin:this.#o,window:this.#c})),this.#t.close(),this.#h&&(clearInterval(this.#h),this.#h=null),this.#y&&ne.forEach(e=>{document.removeEventListener(e,this.#y)}),await this.#R(),window.removeEventListener("beforeunload",this.close),this.#i=!1,this.#e.audit({message:"Guest disconnected from host",...this.#d()}),this.#f=null,this.#b=null,this.#g=null)};connect=async e=>{if(!this.#i){let t=window,s={...le};if(e){const{window:r,...o}=e;r&&(t=r),s={...s,...o}}const i=await re();this.#o=i.origin,this.#c=i.window,this.#t.initialize(t),this.#t.addSender({origin:this.#o,window:this.#c}),this.#t.listen({messageType:S.ObjectEvent,callback:this.#C.bind(this)}),this.#t.listen({messageType:S.HostConfig,callback:this.#L.bind(this)}),this.#t.listen({messageType:S.GuestFocus,callback:this.#_.bind(this)}),this.#t.listen({messageType:S.HostClose,callback:this.#$.bind(this)}),this.#t.send({targetWin:this.#c,targetOrigin:this.#o,messageType:S.GuestReady,messageBody:s}),this.#i=!0,this.#P(),this.#S&&await this.getAuthToken(),await this.#U(),window.addEventListener("beforeunload",this.close),this.#e.audit({message:"Guest connected to host",guestUrl:window.location.href})}};getAuthToken=async()=>{if(!this.#u&&this.#i)try{const e=await this.getObject("auth");if(e){const t=await e.getAccessToken();this.#u=t?.access_token,this.#v=t?.host_name,this.#m=t?.client_id}else this.#e.warn("Auth object not available to get access token from host")}catch(e){this.#e.error(`Error getting access token from auth object. ${e.message}`)}return this.#u};getObject=async e=>{if(this.#e.debug(`Retrieving scripting object "${e}" from host...`),!this.#i)throw new Error("Not connected to host");const{hostWindow:t,hostOrigin:s}=this.#l(),i=await this.#t.invoke({targetWin:t,targetOrigin:s,messageType:S.ObjectGet,messageBody:{objectId:e}}),r=this.#E(i.object);return this.#p.log(`getObject:${e}`,{message:"Received scripting object from host",scriptingObject:e,...this.#d()}),r};listObjects=async()=>{if(this.#e.debug("Retrieving names of scripting objects exposed by host..."),!this.#i)throw new Error("Not connected to host");const{hostWindow:e,hostOrigin:t}=this.#l(),s=await this.#t.invoke({targetWin:e,targetOrigin:t,messageType:S.ListObjects,messageBody:{}});return this.#p.log("listObjects",{message:"Received names of all scripting objects exposed by host",objects:s,...this.#d()}),s};removeScript=()=>{if(this.#n&&this.#r){const{parentNode:e}=this.#r;e?.removeChild?.(this.#r)}};setLogLevel=e=>{this.#e.setLogLevel(e),this.#e.debug(`Log level changed by guest to ${e}`)};subscribe=e=>{const{eventId:t,criteria:s,callback:i}=e;this.#e.debug(`Registering subscription for event ${t}`);const r=this.#a.get(t.toLowerCase())||[],o=b();r.push({callback:i,token:o,criteria:s}),this.#a.set(t.toLowerCase(),r);const{hostWindow:d,hostOrigin:O}=this.#l();return this.#t.send({targetWin:d,targetOrigin:O,messageType:S.GuestEventSubscribe,messageBody:{eventId:t,criteria:s,token:o}}),this.#e.debug(`Subscribed to event ${t} with token ${o}`),o};unsubscribe=e=>{const{eventId:t,token:s}=e,i=this.#a.get(t.toLowerCase());if(i){const r=i.length,o=i.filter(d=>d.token!==s);if(o.length<r){this.#a.set(t.toLowerCase(),o);const{hostWindow:d,hostOrigin:O}=this.#l();this.#t.send({targetWin:d,targetOrigin:O,messageType:S.GuestEventUnsubscribe,messageBody:{eventId:t,token:s}}),this.#e.debug(`Unsubscribed from event ${t}`)}}}}})(),K})());
2
+
3
+ //# sourceMappingURL=emuiSsfGuest.42c1a5949e7eb13de9e0.js.map