@elliemae/ssf-host 2.23.6 → 2.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/cjs/callchain-host.html +262 -0
  2. package/dist/cjs/callchain-intermediate.html +92 -0
  3. package/dist/cjs/e2e-host.html +603 -0
  4. package/dist/cjs/e2e-index.html +234 -0
  5. package/dist/cjs/host.js +93 -36
  6. package/dist/cjs/index.html +304 -151
  7. package/dist/cjs/popup-focus-host.html +318 -0
  8. package/dist/cjs/utils.js +14 -1
  9. package/dist/cjs/v2-host-v1-guest.html +3 -0
  10. package/dist/esm/callchain-host.html +262 -0
  11. package/dist/esm/callchain-intermediate.html +92 -0
  12. package/dist/esm/e2e-host.html +603 -0
  13. package/dist/esm/e2e-index.html +234 -0
  14. package/dist/esm/host.js +94 -37
  15. package/dist/esm/index.html +304 -151
  16. package/dist/esm/popup-focus-host.html +318 -0
  17. package/dist/esm/utils.js +14 -1
  18. package/dist/esm/v2-host-v1-guest.html +3 -0
  19. package/dist/public/callchain-host.html +34 -0
  20. package/dist/public/callchain-host.js +3 -0
  21. package/dist/public/callchain-host.js.br +0 -0
  22. package/dist/public/callchain-host.js.gz +0 -0
  23. package/dist/public/callchain-host.js.map +1 -0
  24. package/dist/public/callchain-intermediate.html +1 -0
  25. package/dist/public/e2e-host.html +5 -0
  26. package/dist/public/e2e-host.js +8 -0
  27. package/dist/public/e2e-host.js.br +0 -0
  28. package/dist/public/e2e-host.js.gz +0 -0
  29. package/dist/public/e2e-host.js.map +1 -0
  30. package/dist/public/e2e-index.html +1 -0
  31. package/dist/public/index.html +1 -1
  32. package/dist/public/init.js +1 -1
  33. package/dist/public/init.js.br +0 -0
  34. package/dist/public/init.js.gz +0 -0
  35. package/dist/public/init.js.map +1 -1
  36. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js +3 -0
  37. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.br +0 -0
  38. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.gz +0 -0
  39. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.map +1 -0
  40. package/dist/public/loan-object.js +1 -1
  41. package/dist/public/loan-object.js.br +0 -0
  42. package/dist/public/loan-object.js.gz +0 -0
  43. package/dist/public/loan-object.js.map +1 -1
  44. package/dist/public/popup-focus-host.html +1 -0
  45. package/dist/public/popup-focus-host.js +6 -0
  46. package/dist/public/popup-focus-host.js.br +0 -0
  47. package/dist/public/popup-focus-host.js.gz +0 -0
  48. package/dist/public/popup-focus-host.js.map +1 -0
  49. package/dist/public/v1-guest-v2-host.html +1 -1
  50. package/dist/public/v2-host-v1-guest.html +1 -1
  51. package/dist/types/lib/host.d.ts +1 -0
  52. package/dist/types/lib/ihost.d.ts +15 -0
  53. package/dist/types/lib/tests/timingDedup.test.d.ts +1 -0
  54. package/dist/types/lib/utils.d.ts +1 -0
  55. package/dist/types/tsconfig.tsbuildinfo +1 -1
  56. package/dist/umd/callchain-host.html +34 -0
  57. package/dist/umd/callchain-host.js +3 -0
  58. package/dist/umd/callchain-host.js.br +0 -0
  59. package/dist/umd/callchain-host.js.gz +0 -0
  60. package/dist/umd/callchain-host.js.map +1 -0
  61. package/dist/umd/callchain-intermediate.html +1 -0
  62. package/dist/umd/e2e-host.html +5 -0
  63. package/dist/umd/e2e-host.js +8 -0
  64. package/dist/umd/e2e-host.js.br +0 -0
  65. package/dist/umd/e2e-host.js.gz +0 -0
  66. package/dist/umd/e2e-host.js.map +1 -0
  67. package/dist/umd/e2e-index.html +1 -0
  68. package/dist/umd/index.html +1 -1
  69. package/dist/umd/index.js +1 -1
  70. package/dist/umd/index.js.br +0 -0
  71. package/dist/umd/index.js.gz +0 -0
  72. package/dist/umd/index.js.map +1 -1
  73. package/dist/umd/init.js +1 -1
  74. package/dist/umd/init.js.br +0 -0
  75. package/dist/umd/init.js.gz +0 -0
  76. package/dist/umd/init.js.map +1 -1
  77. package/dist/umd/loan-object.js +1 -1
  78. package/dist/umd/loan-object.js.br +0 -0
  79. package/dist/umd/loan-object.js.gz +0 -0
  80. package/dist/umd/loan-object.js.map +1 -1
  81. package/dist/umd/popup-focus-host.html +1 -0
  82. package/dist/umd/popup-focus-host.js +6 -0
  83. package/dist/umd/popup-focus-host.js.br +0 -0
  84. package/dist/umd/popup-focus-host.js.gz +0 -0
  85. package/dist/umd/popup-focus-host.js.map +1 -0
  86. package/dist/umd/v2-host-v1-guest.html +1 -1
  87. package/package.json +5 -5
  88. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js +0 -3
  89. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js.br +0 -0
  90. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js.gz +0 -0
  91. package/dist/public/js/emuiSsfHost.5855ec3cd0fa60013d84.js.map +0 -1
@@ -0,0 +1,234 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>SSF E2E Test Suite</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
8
+ </head>
9
+ <body class="bg-gray-50">
10
+ <header class="bg-gray-900 text-white px-6 py-4">
11
+ <h1 class="text-xl font-bold">SSF End-to-End Test Suite</h1>
12
+ <p class="text-sm text-gray-400 mt-1">
13
+ Comprehensive blackbox tests for SSF Host &amp; Guest. Each page
14
+ includes test steps, expected values, and a verification checklist.
15
+ </p>
16
+ </header>
17
+
18
+ <main class="mx-auto max-w-5xl px-6 py-6">
19
+ <!-- Setup -->
20
+ <section
21
+ class="mb-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4"
22
+ >
23
+ <h2 class="text-sm font-semibold text-yellow-800 mb-2">
24
+ Setup Instructions
25
+ </h2>
26
+ <ol class="text-xs text-yellow-700 list-decimal ml-4 space-y-1">
27
+ <li>
28
+ Install dependencies:
29
+ <code class="bg-yellow-100 px-1 rounded">pnpm install</code>
30
+ </li>
31
+ <li>
32
+ Start SSF Host dev server:
33
+ <code class="bg-yellow-100 px-1 rounded"
34
+ >pnpm nx run ssf-host:start-server</code
35
+ >
36
+ (port 4000)
37
+ </li>
38
+ <li>
39
+ Start SSF Guest dev server:
40
+ <code class="bg-yellow-100 px-1 rounded"
41
+ >pnpm nx run ssf-guest:start</code
42
+ >
43
+ (port 4001)
44
+ </li>
45
+ <li>
46
+ Open
47
+ <code class="bg-yellow-100 px-1 rounded"
48
+ >http://localhost:4000/e2e-index.html</code
49
+ >
50
+ in the browser
51
+ </li>
52
+ <li>
53
+ Each test page has
54
+ <code class="bg-yellow-100 px-1 rounded">data-testid</code>
55
+ attributes for Playwright/Cypress selectors
56
+ </li>
57
+ </ol>
58
+ </section>
59
+
60
+ <!-- 1. Core Host-Guest Communication -->
61
+ <section class="mb-6">
62
+ <h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
63
+ 1. Core Host-Guest Communication
64
+ </h2>
65
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
66
+ <a
67
+ href="./e2e-host.html"
68
+ data-testid="link-e2e-host"
69
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
70
+ >
71
+ <h3 class="font-medium text-indigo-700">Comprehensive E2E Host</h3>
72
+ <p class="text-xs text-gray-500 mt-1">
73
+ All host-guest scenarios: guest loading, scripting objects,
74
+ events, lifecycle, metadata, sandbox, guest-scoped objects.
75
+ </p>
76
+ <p class="text-xs text-gray-400 mt-2">
77
+ <strong>What to verify:</strong> Guests load in iframes/popups.
78
+ Scripting objects can be added/removed/invoked. Events reach
79
+ subscribed guests. Unload/close cleans up properly. Sandbox attrs
80
+ applied.
81
+ </p>
82
+ <div class="mt-2 text-xs text-gray-400">
83
+ <strong>Test Cases:</strong>
84
+ TC-LOAD-01..05 &bull; TC-SO-01..08 &bull; TC-EVT-01..04 &bull;
85
+ TC-LIFE-01..03 &bull; TC-META-01 &bull; TC-SB-01..03
86
+ </div>
87
+ </a>
88
+ <a
89
+ href="./index.html"
90
+ data-testid="link-main-demo"
91
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
92
+ >
93
+ <h3 class="font-medium text-indigo-700">Main Demo App</h3>
94
+ <p class="text-xs text-gray-500 mt-1">
95
+ Original Loan Application demo with embedded and popup guests,
96
+ events, scripting objects.
97
+ </p>
98
+ <p class="text-xs text-gray-400 mt-2">
99
+ <strong>What to verify:</strong> Loan form data flows to guests.
100
+ Events (onLoanAmountChanged, etc.) update guest UIs. Popup guests
101
+ open and interact with the Loan object.
102
+ </p>
103
+ </a>
104
+ </div>
105
+ </section>
106
+
107
+ <!-- 2. CallChain & Metadata -->
108
+ <section class="mb-6">
109
+ <h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
110
+ 2. Call Chain &amp; Metadata Propagation
111
+ </h2>
112
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
113
+ <a
114
+ href="./callchain-host.html"
115
+ data-testid="link-callchain"
116
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
117
+ >
118
+ <h3 class="font-medium text-indigo-700">
119
+ Nested CallChain Test (A → B → C)
120
+ </h3>
121
+ <p class="text-xs text-gray-500 mt-1">
122
+ Root Host (A) → Intermediate Guest+Host (B) → Grandchild Guest
123
+ (C). Verifies callContext.callChain and metadata propagation.
124
+ </p>
125
+ <p class="text-xs text-gray-400 mt-2">
126
+ <strong>What to verify:</strong> Click buttons in Grandchild C.
127
+ Host A should show <code>callContext.guest</code> = B and
128
+ <code>callChain</code> containing C and B with their metadata.
129
+ Return values should flow back to C.
130
+ </p>
131
+ <div class="mt-2 text-xs text-gray-400">
132
+ <strong>Test Cases:</strong> TC-CC-01..07
133
+ </div>
134
+ </a>
135
+ </div>
136
+ </section>
137
+
138
+ <!-- 3. Popup Behavior -->
139
+ <section class="mb-6">
140
+ <h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
141
+ 3. Popup Guest Behavior
142
+ </h2>
143
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
144
+ <a
145
+ href="./popup-focus-host.html"
146
+ data-testid="link-popup-focus"
147
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
148
+ >
149
+ <h3 class="font-medium text-indigo-700">
150
+ Popup Focus &amp; Lifecycle
151
+ </h3>
152
+ <p class="text-xs text-gray-500 mt-1">
153
+ Open, close, and refocus popup guests. Tests trusted vs untrusted
154
+ domain behavior.
155
+ </p>
156
+ <p class="text-xs text-gray-400 mt-2">
157
+ <strong>What to verify:</strong> Trusted popups (localhost):
158
+ re-open brings existing popup to front. Untrusted popups: opener
159
+ is nulled, focus falls back to WindowProxy. Closed popup detection
160
+ updates guest status.
161
+ </p>
162
+ <div class="mt-2 text-xs text-gray-400">
163
+ <strong>Test Cases:</strong> TC-POP-01..11
164
+ </div>
165
+ </a>
166
+ </div>
167
+ </section>
168
+
169
+ <!-- 4. V1/V2 Interoperability -->
170
+ <section class="mb-6">
171
+ <h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
172
+ 4. V1 / V2 Interoperability
173
+ </h2>
174
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
175
+ <a
176
+ href="./v2-host-v1-guest.html"
177
+ data-testid="link-v2-host-v1-guest"
178
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
179
+ >
180
+ <h3 class="font-medium text-indigo-700">V2 Host → V1 Guest</h3>
181
+ <p class="text-xs text-gray-500 mt-1">
182
+ V2 host loads V1 guest. Loan object, save flow, event feedback.
183
+ </p>
184
+ <p class="text-xs text-gray-400 mt-2">
185
+ <strong>What to verify:</strong> V1 guest connects, gets objects,
186
+ subscribes to events, and returns feedback correctly.
187
+ </p>
188
+ <div class="mt-2 text-xs text-gray-400">
189
+ <strong>Test Cases:</strong> TC-INTEROP-01..04
190
+ </div>
191
+ </a>
192
+ <a
193
+ href="./v2-host-v1-guest.html?nestV1GuestV2Host=true"
194
+ data-testid="link-v2-v1-v2-chain"
195
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
196
+ >
197
+ <h3 class="font-medium text-indigo-700">
198
+ V2 Host → V1 Guest → V2 Host → V2 Guest
199
+ </h3>
200
+ <p class="text-xs text-gray-500 mt-1">
201
+ Full mixed-version chain. V1 guest acts as intermediate host for a
202
+ V2 grandchild.
203
+ </p>
204
+ <p class="text-xs text-gray-400 mt-2">
205
+ <strong>What to verify:</strong> Scripting objects are cloned
206
+ across V1/V2 boundary. Events flow through all layers.
207
+ </p>
208
+ <div class="mt-2 text-xs text-gray-400">
209
+ <strong>Test Cases:</strong> TC-INTEROP-05..07
210
+ </div>
211
+ </a>
212
+ </div>
213
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3">
214
+ <a
215
+ href="./v1-host.html"
216
+ data-testid="link-v1-host"
217
+ class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
218
+ >
219
+ <h3 class="font-medium text-indigo-700">
220
+ V1 Host → V1 Guest → V2 Host → V2 Guest
221
+ </h3>
222
+ <p class="text-xs text-gray-500 mt-1">
223
+ Starting from V1 host. Exercises the full V1-to-V2 upgrade path.
224
+ </p>
225
+ <p class="text-xs text-gray-400 mt-2">
226
+ <strong>What to verify:</strong> V1 host initializes. V1 guest
227
+ connects. V2 host/guest chain works through V1 intermediate.
228
+ </p>
229
+ </a>
230
+ </div>
231
+ </section>
232
+ </main>
233
+ </body>
234
+ </html>
package/dist/esm/host.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  OpenMode
14
14
  } from "./types.js";
15
15
  import { Guest } from "./guest.js";
16
- import { flatten, isFunction } from "./utils.js";
16
+ import { flatten, isFunction, isTrustedDomain } from "./utils.js";
17
17
  const SANDBOX_DEFAULT = [
18
18
  IFrameSandboxValues.AllowScripts,
19
19
  IFrameSandboxValues.AllowPopups,
@@ -80,6 +80,21 @@ class SSFHost {
80
80
  * callback for guest event unsubscription
81
81
  */
82
82
  #onGuestEventUnsubscribe = null;
83
+ /**
84
+ * host-supplied metadata keyed by guest id, forwarded in callChain
85
+ */
86
+ #guestMetadata = /* @__PURE__ */ new Map();
87
+ /**
88
+ * Tracks the last time a timing measurement was recorded for each metric name.
89
+ * Used to skip redundant startTiming/endTiming cross-window calls for
90
+ * the same API or event within the dedup window.
91
+ */
92
+ #timingLastRecorded = /* @__PURE__ */ new Map();
93
+ /**
94
+ * Minimum interval (ms) between timing measurements for the same metric name.
95
+ */
96
+ #timingDedupWindowMs;
97
+ static DEFAULT_TIMING_DEDUP_WINDOW_MS = 1e4;
83
98
  /**
84
99
  * Create a new host
85
100
  * @param hostId unique identifier for the host
@@ -91,6 +106,7 @@ class SSFHost {
91
106
  if (!option?.analyticsObj) throw new Error("Analytics object is required");
92
107
  this.#logger = option.logger;
93
108
  this.#analyticsObj = option.analyticsObj;
109
+ this.#timingDedupWindowMs = option?.timingDedupWindowMs ?? SSFHost.DEFAULT_TIMING_DEDUP_WINDOW_MS;
94
110
  this.#correlationId = uuidv4();
95
111
  this.#remoting = new Remoting(this.#logger, this.#correlationId);
96
112
  if (option?.readyStateCallback && typeof option?.readyStateCallback !== "function")
@@ -126,6 +142,22 @@ class SSFHost {
126
142
  this.#logger.debug(`Analytics endTiming failed: ${e.message}`);
127
143
  });
128
144
  };
145
+ /**
146
+ * Returns true if a timing measurement should be recorded for the given
147
+ * metric name. Skips the measurement when one was already recorded for the
148
+ * same name within the configured dedup window ({@link HostOption.timingDedupWindowMs}).
149
+ * Always returns true when dedup is disabled (window = 0).
150
+ * @param name
151
+ */
152
+ #shouldTrackTiming = (name) => {
153
+ if (this.#timingDedupWindowMs <= 0) return true;
154
+ const now = performance.now();
155
+ const last = this.#timingLastRecorded.get(name);
156
+ if (last !== void 0 && now - last < this.#timingDedupWindowMs)
157
+ return false;
158
+ this.#timingLastRecorded.set(name, now);
159
+ return true;
160
+ };
129
161
  #closeAllPopupGuests = () => {
130
162
  const popupIds = Array.from(this.#guests.values()).filter((guest) => guest.openMode === OpenMode.Popup).map((guest) => guest.id);
131
163
  popupIds.forEach((id) => this.unloadGuest(id));
@@ -191,7 +223,8 @@ class SSFHost {
191
223
  guest,
192
224
  obj,
193
225
  functionName,
194
- functionParams
226
+ functionParams,
227
+ callerChain
195
228
  }) => {
196
229
  const func = obj[functionName];
197
230
  if (!isFunction(func)) {
@@ -209,7 +242,10 @@ class SSFHost {
209
242
  );
210
243
  return new Promise((resolve) => {
211
244
  Object.defineProperty(func, "callContext", {
212
- value: { guest },
245
+ value: {
246
+ guest,
247
+ ...callerChain?.length ? { callChain: callerChain } : {}
248
+ },
213
249
  configurable: true,
214
250
  enumerable: true,
215
251
  writable: true
@@ -492,7 +528,7 @@ class SSFHost {
492
528
  requestId,
493
529
  body
494
530
  }) => {
495
- const { objectId } = body;
531
+ const { objectId, callerChain } = body;
496
532
  const guest = this.#getGuestForWindow(sourceWin);
497
533
  if (!guest) {
498
534
  this.#logger.warn(
@@ -517,15 +553,20 @@ class SSFHost {
517
553
  return false;
518
554
  }
519
555
  const guestInfo = guest.getInfo();
520
- this.#startTiming(`ScriptingObject.API.${objectId}.${body.functionName}`, {
521
- appId: guestInfo.guestId,
522
- appUrl: guestInfo.guestUrl
523
- });
556
+ const timingName = `ScriptingObject.API.${objectId}.${body.functionName}`;
557
+ const trackTiming = this.#shouldTrackTiming(timingName);
558
+ if (trackTiming) {
559
+ this.#startTiming(timingName, {
560
+ appId: guestInfo.guestId,
561
+ appUrl: guestInfo.guestUrl
562
+ });
563
+ }
524
564
  this.#invoke({
525
565
  guest,
526
566
  obj,
527
567
  functionName: body.functionName,
528
- functionParams: body.functionParams
568
+ functionParams: body.functionParams,
569
+ callerChain
529
570
  }).then((val) => {
530
571
  this.#remoting.respond({
531
572
  targetWin: sourceWin,
@@ -555,13 +596,12 @@ class SSFHost {
555
596
  ...guestInfo
556
597
  });
557
598
  }).finally(() => {
558
- this.#endTiming(
559
- `ScriptingObject.API.${objectId}.${body.functionName}`,
560
- {
599
+ if (trackTiming) {
600
+ this.#endTiming(timingName, {
561
601
  appId: guestInfo.guestId,
562
602
  appUrl: guestInfo.guestUrl
563
- }
564
- );
603
+ });
604
+ }
565
605
  });
566
606
  return true;
567
607
  };
@@ -704,10 +744,11 @@ class SSFHost {
704
744
  let guest = this.#getGuestForUrl(url);
705
745
  if (guest) {
706
746
  if (!guest.window.closed) {
707
- guest.send({
708
- messageType: MessageType.GuestFocus,
709
- messageBody: {}
710
- });
747
+ if (isTrustedDomain(url)) {
748
+ window.open("", title);
749
+ } else {
750
+ guest.window.focus();
751
+ }
711
752
  }
712
753
  } else {
713
754
  const windowFeatures = Object.entries({ width, height, top, left }).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join(",");
@@ -734,7 +775,9 @@ class SSFHost {
734
775
  }
735
776
  }, 0);
736
777
  }
737
- guestWindow.opener = null;
778
+ if (!isTrustedDomain(url)) {
779
+ guestWindow.opener = null;
780
+ }
738
781
  guest = this.#attachGuest({
739
782
  guestId,
740
783
  window: guestWindow,
@@ -901,6 +944,22 @@ class SSFHost {
901
944
  } else if (isFunction(propValue)) {
902
945
  Object.defineProperty(so, propName, {
903
946
  value: async (...args) => {
947
+ const callerCtx = so[propName]?.callContext;
948
+ if (callerCtx?.guest) {
949
+ const existingChain = callerCtx.callChain ?? [];
950
+ const guestId = callerCtx.guest.id;
951
+ const meta = this.#guestMetadata.get(guestId);
952
+ const callerEntry = { id: guestId };
953
+ if (meta) callerEntry.metadata = meta;
954
+ Object.defineProperty(propValue, "callContext", {
955
+ value: {
956
+ callChain: [...existingChain, callerEntry]
957
+ },
958
+ configurable: true,
959
+ enumerable: true,
960
+ writable: true
961
+ });
962
+ }
904
963
  const retVal = await propValue(...args);
905
964
  return isScriptingObjectProxy(retVal) ? this.cloneScriptingObject(retVal) : retVal;
906
965
  },
@@ -935,6 +994,7 @@ class SSFHost {
935
994
  this.#popupGuestMonitor = null;
936
995
  }
937
996
  this.#closeAllPopupGuests();
997
+ this.#guestMetadata.clear();
938
998
  this.#remoting.close();
939
999
  window.removeEventListener("beforeunload", this.#closeAllPopupGuests);
940
1000
  this.#logger.debug(
@@ -985,19 +1045,17 @@ class SSFHost {
985
1045
  };
986
1046
  }
987
1047
  const guestPromises = [];
1048
+ const eventTimingName = `ScriptingObject.Event.${scriptingObject.id}.${name}`;
988
1049
  let timingMetricStarted = false;
989
1050
  const dispatchToGuest = (guest) => {
990
1051
  const guestInfo = guest.getInfo();
991
1052
  if (timeout && guest?.capabilities?.eventFeedback) {
992
1053
  guestPromises.push(guest.dispatchEvent(eventObj, timeout));
993
- if (!timingMetricStarted) {
994
- this.#startTiming(
995
- `ScriptingObject.Event.${scriptingObject.id}.${name}`,
996
- {
997
- appId: this.hostId,
998
- appUrl: window.location.href
999
- }
1000
- );
1054
+ if (!timingMetricStarted && this.#shouldTrackTiming(eventTimingName)) {
1055
+ this.#startTiming(eventTimingName, {
1056
+ appId: this.hostId,
1057
+ appUrl: window.location.href
1058
+ });
1001
1059
  timingMetricStarted = true;
1002
1060
  }
1003
1061
  this.#logger.debug({
@@ -1022,7 +1080,7 @@ class SSFHost {
1022
1080
  } else {
1023
1081
  this.#guests.forEach(dispatchToGuest);
1024
1082
  }
1025
- const retValue = await Promise.all(guestPromises).then((values) => {
1083
+ return Promise.all(guestPromises).then((values) => {
1026
1084
  this.#logger.debug({
1027
1085
  message: "Event feedback received",
1028
1086
  scriptingEventId: id
@@ -1037,15 +1095,11 @@ class SSFHost {
1037
1095
  throw ex;
1038
1096
  }).finally(() => {
1039
1097
  if (timingMetricStarted)
1040
- this.#endTiming(
1041
- `ScriptingObject.Event.${scriptingObject.id}.${name}`,
1042
- {
1043
- appId: this.hostId,
1044
- appUrl: window.location.href
1045
- }
1046
- );
1098
+ this.#endTiming(eventTimingName, {
1099
+ appId: this.hostId,
1100
+ appUrl: window.location.href
1101
+ });
1047
1102
  });
1048
- return retValue;
1049
1103
  };
1050
1104
  /**
1051
1105
  * get reference to all guest applications
@@ -1073,7 +1127,8 @@ class SSFHost {
1073
1127
  searchParams = {},
1074
1128
  onLoad,
1075
1129
  onError,
1076
- options = {}
1130
+ options = {},
1131
+ metadata
1077
1132
  } = param;
1078
1133
  if (!guestId) throw new Error("id for guest application is required");
1079
1134
  let instanceId = guestId;
@@ -1115,6 +1170,7 @@ class SSFHost {
1115
1170
  } else {
1116
1171
  throw new Error(`Invalid openMode: ${openMode}`);
1117
1172
  }
1173
+ if (metadata) this.#guestMetadata.set(instanceId, metadata);
1118
1174
  this.#logger.audit({
1119
1175
  message: "Guest loaded",
1120
1176
  ...guest.getInfo()
@@ -1180,6 +1236,7 @@ class SSFHost {
1180
1236
  guest.dispose();
1181
1237
  this.#guestsByWindow.delete(guest.window);
1182
1238
  this.#guestsByUrl.delete(guest.url);
1239
+ this.#guestMetadata.delete(guest.id);
1183
1240
  this.#guests.delete(guest.id);
1184
1241
  this.#logger.audit({
1185
1242
  message: `Guest is removed from host`,