@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/cjs/host.js CHANGED
@@ -93,6 +93,21 @@ class SSFHost {
93
93
  * callback for guest event unsubscription
94
94
  */
95
95
  #onGuestEventUnsubscribe = null;
96
+ /**
97
+ * host-supplied metadata keyed by guest id, forwarded in callChain
98
+ */
99
+ #guestMetadata = /* @__PURE__ */ new Map();
100
+ /**
101
+ * Tracks the last time a timing measurement was recorded for each metric name.
102
+ * Used to skip redundant startTiming/endTiming cross-window calls for
103
+ * the same API or event within the dedup window.
104
+ */
105
+ #timingLastRecorded = /* @__PURE__ */ new Map();
106
+ /**
107
+ * Minimum interval (ms) between timing measurements for the same metric name.
108
+ */
109
+ #timingDedupWindowMs;
110
+ static DEFAULT_TIMING_DEDUP_WINDOW_MS = 1e4;
96
111
  /**
97
112
  * Create a new host
98
113
  * @param hostId unique identifier for the host
@@ -104,6 +119,7 @@ class SSFHost {
104
119
  if (!option?.analyticsObj) throw new Error("Analytics object is required");
105
120
  this.#logger = option.logger;
106
121
  this.#analyticsObj = option.analyticsObj;
122
+ this.#timingDedupWindowMs = option?.timingDedupWindowMs ?? SSFHost.DEFAULT_TIMING_DEDUP_WINDOW_MS;
107
123
  this.#correlationId = (0, import_uuid.v4)();
108
124
  this.#remoting = new import_microfe_common.Remoting(this.#logger, this.#correlationId);
109
125
  if (option?.readyStateCallback && typeof option?.readyStateCallback !== "function")
@@ -139,6 +155,22 @@ class SSFHost {
139
155
  this.#logger.debug(`Analytics endTiming failed: ${e.message}`);
140
156
  });
141
157
  };
158
+ /**
159
+ * Returns true if a timing measurement should be recorded for the given
160
+ * metric name. Skips the measurement when one was already recorded for the
161
+ * same name within the configured dedup window ({@link HostOption.timingDedupWindowMs}).
162
+ * Always returns true when dedup is disabled (window = 0).
163
+ * @param name
164
+ */
165
+ #shouldTrackTiming = (name) => {
166
+ if (this.#timingDedupWindowMs <= 0) return true;
167
+ const now = performance.now();
168
+ const last = this.#timingLastRecorded.get(name);
169
+ if (last !== void 0 && now - last < this.#timingDedupWindowMs)
170
+ return false;
171
+ this.#timingLastRecorded.set(name, now);
172
+ return true;
173
+ };
142
174
  #closeAllPopupGuests = () => {
143
175
  const popupIds = Array.from(this.#guests.values()).filter((guest) => guest.openMode === import_types.OpenMode.Popup).map((guest) => guest.id);
144
176
  popupIds.forEach((id) => this.unloadGuest(id));
@@ -204,7 +236,8 @@ class SSFHost {
204
236
  guest,
205
237
  obj,
206
238
  functionName,
207
- functionParams
239
+ functionParams,
240
+ callerChain
208
241
  }) => {
209
242
  const func = obj[functionName];
210
243
  if (!(0, import_utils.isFunction)(func)) {
@@ -222,7 +255,10 @@ class SSFHost {
222
255
  );
223
256
  return new Promise((resolve) => {
224
257
  Object.defineProperty(func, "callContext", {
225
- value: { guest },
258
+ value: {
259
+ guest,
260
+ ...callerChain?.length ? { callChain: callerChain } : {}
261
+ },
226
262
  configurable: true,
227
263
  enumerable: true,
228
264
  writable: true
@@ -505,7 +541,7 @@ class SSFHost {
505
541
  requestId,
506
542
  body
507
543
  }) => {
508
- const { objectId } = body;
544
+ const { objectId, callerChain } = body;
509
545
  const guest = this.#getGuestForWindow(sourceWin);
510
546
  if (!guest) {
511
547
  this.#logger.warn(
@@ -530,15 +566,20 @@ class SSFHost {
530
566
  return false;
531
567
  }
532
568
  const guestInfo = guest.getInfo();
533
- this.#startTiming(`ScriptingObject.API.${objectId}.${body.functionName}`, {
534
- appId: guestInfo.guestId,
535
- appUrl: guestInfo.guestUrl
536
- });
569
+ const timingName = `ScriptingObject.API.${objectId}.${body.functionName}`;
570
+ const trackTiming = this.#shouldTrackTiming(timingName);
571
+ if (trackTiming) {
572
+ this.#startTiming(timingName, {
573
+ appId: guestInfo.guestId,
574
+ appUrl: guestInfo.guestUrl
575
+ });
576
+ }
537
577
  this.#invoke({
538
578
  guest,
539
579
  obj,
540
580
  functionName: body.functionName,
541
- functionParams: body.functionParams
581
+ functionParams: body.functionParams,
582
+ callerChain
542
583
  }).then((val) => {
543
584
  this.#remoting.respond({
544
585
  targetWin: sourceWin,
@@ -568,13 +609,12 @@ class SSFHost {
568
609
  ...guestInfo
569
610
  });
570
611
  }).finally(() => {
571
- this.#endTiming(
572
- `ScriptingObject.API.${objectId}.${body.functionName}`,
573
- {
612
+ if (trackTiming) {
613
+ this.#endTiming(timingName, {
574
614
  appId: guestInfo.guestId,
575
615
  appUrl: guestInfo.guestUrl
576
- }
577
- );
616
+ });
617
+ }
578
618
  });
579
619
  return true;
580
620
  };
@@ -717,10 +757,11 @@ class SSFHost {
717
757
  let guest = this.#getGuestForUrl(url);
718
758
  if (guest) {
719
759
  if (!guest.window.closed) {
720
- guest.send({
721
- messageType: import_microfe_common.MessageType.GuestFocus,
722
- messageBody: {}
723
- });
760
+ if ((0, import_utils.isTrustedDomain)(url)) {
761
+ window.open("", title);
762
+ } else {
763
+ guest.window.focus();
764
+ }
724
765
  }
725
766
  } else {
726
767
  const windowFeatures = Object.entries({ width, height, top, left }).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join(",");
@@ -747,7 +788,9 @@ class SSFHost {
747
788
  }
748
789
  }, 0);
749
790
  }
750
- guestWindow.opener = null;
791
+ if (!(0, import_utils.isTrustedDomain)(url)) {
792
+ guestWindow.opener = null;
793
+ }
751
794
  guest = this.#attachGuest({
752
795
  guestId,
753
796
  window: guestWindow,
@@ -914,6 +957,22 @@ class SSFHost {
914
957
  } else if ((0, import_utils.isFunction)(propValue)) {
915
958
  Object.defineProperty(so, propName, {
916
959
  value: async (...args) => {
960
+ const callerCtx = so[propName]?.callContext;
961
+ if (callerCtx?.guest) {
962
+ const existingChain = callerCtx.callChain ?? [];
963
+ const guestId = callerCtx.guest.id;
964
+ const meta = this.#guestMetadata.get(guestId);
965
+ const callerEntry = { id: guestId };
966
+ if (meta) callerEntry.metadata = meta;
967
+ Object.defineProperty(propValue, "callContext", {
968
+ value: {
969
+ callChain: [...existingChain, callerEntry]
970
+ },
971
+ configurable: true,
972
+ enumerable: true,
973
+ writable: true
974
+ });
975
+ }
917
976
  const retVal = await propValue(...args);
918
977
  return (0, import_microfe_common.isScriptingObjectProxy)(retVal) ? this.cloneScriptingObject(retVal) : retVal;
919
978
  },
@@ -948,6 +1007,7 @@ class SSFHost {
948
1007
  this.#popupGuestMonitor = null;
949
1008
  }
950
1009
  this.#closeAllPopupGuests();
1010
+ this.#guestMetadata.clear();
951
1011
  this.#remoting.close();
952
1012
  window.removeEventListener("beforeunload", this.#closeAllPopupGuests);
953
1013
  this.#logger.debug(
@@ -998,19 +1058,17 @@ class SSFHost {
998
1058
  };
999
1059
  }
1000
1060
  const guestPromises = [];
1061
+ const eventTimingName = `ScriptingObject.Event.${scriptingObject.id}.${name}`;
1001
1062
  let timingMetricStarted = false;
1002
1063
  const dispatchToGuest = (guest) => {
1003
1064
  const guestInfo = guest.getInfo();
1004
1065
  if (timeout && guest?.capabilities?.eventFeedback) {
1005
1066
  guestPromises.push(guest.dispatchEvent(eventObj, timeout));
1006
- if (!timingMetricStarted) {
1007
- this.#startTiming(
1008
- `ScriptingObject.Event.${scriptingObject.id}.${name}`,
1009
- {
1010
- appId: this.hostId,
1011
- appUrl: window.location.href
1012
- }
1013
- );
1067
+ if (!timingMetricStarted && this.#shouldTrackTiming(eventTimingName)) {
1068
+ this.#startTiming(eventTimingName, {
1069
+ appId: this.hostId,
1070
+ appUrl: window.location.href
1071
+ });
1014
1072
  timingMetricStarted = true;
1015
1073
  }
1016
1074
  this.#logger.debug({
@@ -1035,7 +1093,7 @@ class SSFHost {
1035
1093
  } else {
1036
1094
  this.#guests.forEach(dispatchToGuest);
1037
1095
  }
1038
- const retValue = await Promise.all(guestPromises).then((values) => {
1096
+ return Promise.all(guestPromises).then((values) => {
1039
1097
  this.#logger.debug({
1040
1098
  message: "Event feedback received",
1041
1099
  scriptingEventId: id
@@ -1050,15 +1108,11 @@ class SSFHost {
1050
1108
  throw ex;
1051
1109
  }).finally(() => {
1052
1110
  if (timingMetricStarted)
1053
- this.#endTiming(
1054
- `ScriptingObject.Event.${scriptingObject.id}.${name}`,
1055
- {
1056
- appId: this.hostId,
1057
- appUrl: window.location.href
1058
- }
1059
- );
1111
+ this.#endTiming(eventTimingName, {
1112
+ appId: this.hostId,
1113
+ appUrl: window.location.href
1114
+ });
1060
1115
  });
1061
- return retValue;
1062
1116
  };
1063
1117
  /**
1064
1118
  * get reference to all guest applications
@@ -1086,7 +1140,8 @@ class SSFHost {
1086
1140
  searchParams = {},
1087
1141
  onLoad,
1088
1142
  onError,
1089
- options = {}
1143
+ options = {},
1144
+ metadata
1090
1145
  } = param;
1091
1146
  if (!guestId) throw new Error("id for guest application is required");
1092
1147
  let instanceId = guestId;
@@ -1128,6 +1183,7 @@ class SSFHost {
1128
1183
  } else {
1129
1184
  throw new Error(`Invalid openMode: ${openMode}`);
1130
1185
  }
1186
+ if (metadata) this.#guestMetadata.set(instanceId, metadata);
1131
1187
  this.#logger.audit({
1132
1188
  message: "Guest loaded",
1133
1189
  ...guest.getInfo()
@@ -1193,6 +1249,7 @@ class SSFHost {
1193
1249
  guest.dispose();
1194
1250
  this.#guestsByWindow.delete(guest.window);
1195
1251
  this.#guestsByUrl.delete(guest.url);
1252
+ this.#guestMetadata.delete(guest.id);
1196
1253
  this.#guests.delete(guest.id);
1197
1254
  this.#logger.audit({
1198
1255
  message: `Guest is removed from host`,