@elliemae/ssf-host 2.24.0 → 2.25.1

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 (40) hide show
  1. package/dist/cjs/guest.js +15 -16
  2. package/dist/cjs/host.js +40 -56
  3. package/dist/cjs/performanceTracker.js +111 -0
  4. package/dist/esm/guest.js +15 -16
  5. package/dist/esm/host.js +40 -56
  6. package/dist/esm/performanceTracker.js +91 -0
  7. package/dist/public/callchain-host.html +1 -1
  8. package/dist/public/callchain-intermediate.html +1 -1
  9. package/dist/public/e2e-host.html +1 -1
  10. package/dist/public/e2e-index.html +1 -1
  11. package/dist/public/index.html +1 -1
  12. package/dist/public/js/emuiSsfHost.7dbe9d9cafa1bad23e6c.js +3 -0
  13. package/dist/public/js/emuiSsfHost.7dbe9d9cafa1bad23e6c.js.br +0 -0
  14. package/dist/public/js/emuiSsfHost.7dbe9d9cafa1bad23e6c.js.gz +0 -0
  15. package/dist/public/js/emuiSsfHost.7dbe9d9cafa1bad23e6c.js.map +1 -0
  16. package/dist/public/popup-focus-host.html +1 -1
  17. package/dist/public/utils.js +1 -1
  18. package/dist/public/utils.js.br +0 -0
  19. package/dist/public/utils.js.gz +0 -0
  20. package/dist/public/utils.js.map +1 -1
  21. package/dist/public/v1-guest-v2-host.html +1 -1
  22. package/dist/public/v2-host-v1-guest.html +1 -1
  23. package/dist/types/lib/guest.d.ts +4 -3
  24. package/dist/types/lib/host.d.ts +0 -1
  25. package/dist/types/lib/ihost.d.ts +26 -1
  26. package/dist/types/lib/performanceTracker.d.ts +46 -0
  27. package/dist/types/tsconfig.tsbuildinfo +1 -1
  28. package/dist/umd/index.js +1 -1
  29. package/dist/umd/index.js.br +0 -0
  30. package/dist/umd/index.js.gz +0 -0
  31. package/dist/umd/index.js.map +1 -1
  32. package/dist/umd/utils.js +1 -1
  33. package/dist/umd/utils.js.br +0 -0
  34. package/dist/umd/utils.js.gz +0 -0
  35. package/dist/umd/utils.js.map +1 -1
  36. package/package.json +4 -4
  37. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js +0 -3
  38. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.br +0 -0
  39. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.gz +0 -0
  40. package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.map +0 -1
package/dist/cjs/guest.js CHANGED
@@ -74,9 +74,9 @@ class Guest {
74
74
  */
75
75
  #remoting;
76
76
  /**
77
- * analytics object
77
+ * performance tracker for timing measurements
78
78
  */
79
- #analyticsObj;
79
+ #perfTracker;
80
80
  /**
81
81
  * Create object representing guest application
82
82
  * @param {GuestOption} option - options for creating a guest application
@@ -91,7 +91,7 @@ class Guest {
91
91
  searchParams = {},
92
92
  openMode = import_types.OpenMode.Embed,
93
93
  remoting,
94
- analyticsObj
94
+ perfTracker
95
95
  } = option;
96
96
  this.id = guestId;
97
97
  this.title = title;
@@ -102,7 +102,7 @@ class Guest {
102
102
  this.window = window;
103
103
  this.openMode = openMode;
104
104
  this.capabilities = {};
105
- this.#analyticsObj = analyticsObj;
105
+ this.#perfTracker = perfTracker;
106
106
  this.#remoting = remoting;
107
107
  }
108
108
  /**
@@ -188,16 +188,17 @@ class Guest {
188
188
  * invokes event callback on the guest application
189
189
  * @param {EventObject} event - event object
190
190
  * @param {number} timeout - timeout in milliseconds
191
+ * @returns {Promise} resolves when the guest handles the event
191
192
  */
192
193
  dispatchEvent = (event, timeout) => {
193
- this.#analyticsObj.startTiming(
194
- `ScriptingObject.Event.${event.object.objectId}.${event.eventName}`,
195
- {
194
+ let timingToken;
195
+ if (this.#perfTracker.enabled) {
196
+ const name = `ScriptingObject.Event.${event.object.objectId}.${event.eventName}`;
197
+ timingToken = this.#perfTracker.start(name, {
196
198
  appId: this.id,
197
199
  appUrl: this.url
198
- }
199
- ).catch(() => {
200
- });
200
+ });
201
+ }
201
202
  return this.#remoting.invoke({
202
203
  targetWin: this.window,
203
204
  targetOrigin: this.origin,
@@ -205,14 +206,12 @@ class Guest {
205
206
  messageBody: event,
206
207
  responseTimeoutMs: timeout
207
208
  }).finally(() => {
208
- this.#analyticsObj.endTiming(
209
- `ScriptingObject.Event.${event.object.objectId}.${event.eventName}`,
210
- {
209
+ if (timingToken) {
210
+ this.#perfTracker.end(timingToken, {
211
211
  appId: this.id,
212
212
  appUrl: this.url
213
- }
214
- ).catch(() => {
215
- });
213
+ });
214
+ }
216
215
  });
217
216
  };
218
217
  /**
package/dist/cjs/host.js CHANGED
@@ -27,6 +27,7 @@ var import_microfe_common = require("@elliemae/microfe-common");
27
27
  var import_types = require("./types.js");
28
28
  var import_guest = require("./guest.js");
29
29
  var import_utils = require("./utils.js");
30
+ var import_performanceTracker = require("./performanceTracker.js");
30
31
  const SANDBOX_DEFAULT = [
31
32
  import_types.IFrameSandboxValues.AllowScripts,
32
33
  import_types.IFrameSandboxValues.AllowPopups,
@@ -98,16 +99,13 @@ class SSFHost {
98
99
  */
99
100
  #guestMetadata = /* @__PURE__ */ new Map();
100
101
  /**
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.
102
+ * Performance tracker for host-side timing (with dedup).
104
103
  */
105
- #timingLastRecorded = /* @__PURE__ */ new Map();
104
+ #perfTracker;
106
105
  /**
107
- * Minimum interval (ms) between timing measurements for the same metric name.
106
+ * Performance tracker shared by all guests (no dedup).
108
107
  */
109
- #timingDedupWindowMs;
110
- static DEFAULT_TIMING_DEDUP_WINDOW_MS = 1e4;
108
+ #guestPerfTracker;
111
109
  /**
112
110
  * Create a new host
113
111
  * @param hostId unique identifier for the host
@@ -119,9 +117,24 @@ class SSFHost {
119
117
  if (!option?.analyticsObj) throw new Error("Analytics object is required");
120
118
  this.#logger = option.logger;
121
119
  this.#analyticsObj = option.analyticsObj;
122
- this.#timingDedupWindowMs = option?.timingDedupWindowMs ?? SSFHost.DEFAULT_TIMING_DEDUP_WINDOW_MS;
120
+ const perfOpts = {
121
+ analyticsObj: option.analyticsObj,
122
+ enabled: option?.measurePerformance,
123
+ samplingRatio: option?.performanceSamplingRatio,
124
+ onError: (msg) => this.#logger.debug(msg)
125
+ };
126
+ this.#perfTracker = new import_performanceTracker.PerformanceTracker({
127
+ ...perfOpts,
128
+ dedupWindowMs: option?.performanceDedupWindowMs
129
+ });
130
+ this.#guestPerfTracker = new import_performanceTracker.PerformanceTracker({
131
+ ...perfOpts,
132
+ dedupWindowMs: 0
133
+ });
123
134
  this.#correlationId = (0, import_uuid.v4)();
124
- this.#remoting = new import_microfe_common.Remoting(this.#logger, this.#correlationId);
135
+ this.#remoting = new import_microfe_common.Remoting(this.#logger, this.#correlationId, {
136
+ unsafeAllowAnyGuestOrigin: option?.unsafeAllowAnyGuestOrigin
137
+ });
125
138
  if (option?.readyStateCallback && typeof option?.readyStateCallback !== "function")
126
139
  throw new Error("readyStateCallback must be a function");
127
140
  this.#readyStateCallback = option?.readyStateCallback || null;
@@ -143,34 +156,6 @@ class SSFHost {
143
156
  );
144
157
  });
145
158
  };
146
- #startTiming = (name, options) => {
147
- this.#analyticsObj.startTiming(name, options).catch((e) => {
148
- this.#logger.debug(
149
- `Analytics startTiming failed: ${e.message}`
150
- );
151
- });
152
- };
153
- #endTiming = (start, options) => {
154
- this.#analyticsObj.endTiming(start, options).catch((e) => {
155
- this.#logger.debug(`Analytics endTiming failed: ${e.message}`);
156
- });
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
- };
174
159
  #closeAllPopupGuests = () => {
175
160
  const popupIds = Array.from(this.#guests.values()).filter((guest) => guest.openMode === import_types.OpenMode.Popup).map((guest) => guest.id);
176
161
  popupIds.forEach((id) => this.unloadGuest(id));
@@ -293,7 +278,7 @@ class SSFHost {
293
278
  if (!guest.ready) {
294
279
  guest.ready = true;
295
280
  const guestInfo = guest.getInfo();
296
- this.#endTiming("SSF.Guest.Load", {
281
+ this.#perfTracker.end("SSF.Guest.Load", {
297
282
  appId: guestInfo.guestId,
298
283
  appUrl: guestInfo.guestUrl
299
284
  });
@@ -566,10 +551,10 @@ class SSFHost {
566
551
  return false;
567
552
  }
568
553
  const guestInfo = guest.getInfo();
569
- const timingName = `ScriptingObject.API.${objectId}.${body.functionName}`;
570
- const trackTiming = this.#shouldTrackTiming(timingName);
571
- if (trackTiming) {
572
- this.#startTiming(timingName, {
554
+ let timingToken;
555
+ if (this.#perfTracker.enabled) {
556
+ const name = `ScriptingObject.API.${objectId}.${body.functionName}`;
557
+ timingToken = this.#perfTracker.start(name, {
573
558
  appId: guestInfo.guestId,
574
559
  appUrl: guestInfo.guestUrl
575
560
  });
@@ -609,8 +594,8 @@ class SSFHost {
609
594
  ...guestInfo
610
595
  });
611
596
  }).finally(() => {
612
- if (trackTiming) {
613
- this.#endTiming(timingName, {
597
+ if (timingToken) {
598
+ this.#perfTracker.end(timingToken, {
614
599
  appId: guestInfo.guestId,
615
600
  appUrl: guestInfo.guestUrl
616
601
  });
@@ -685,7 +670,7 @@ class SSFHost {
685
670
  const guest = new import_guest.Guest({
686
671
  ...param,
687
672
  remoting: this.#remoting,
688
- analyticsObj: this.#analyticsObj
673
+ perfTracker: this.#guestPerfTracker
689
674
  });
690
675
  guest.init();
691
676
  this.#guests.set(param.guestId, guest);
@@ -1058,18 +1043,16 @@ class SSFHost {
1058
1043
  };
1059
1044
  }
1060
1045
  const guestPromises = [];
1061
- const eventTimingName = `ScriptingObject.Event.${scriptingObject.id}.${name}`;
1062
- let timingMetricStarted = false;
1046
+ let eventTimingToken;
1063
1047
  const dispatchToGuest = (guest) => {
1064
1048
  const guestInfo = guest.getInfo();
1065
1049
  if (timeout && guest?.capabilities?.eventFeedback) {
1066
1050
  guestPromises.push(guest.dispatchEvent(eventObj, timeout));
1067
- if (!timingMetricStarted && this.#shouldTrackTiming(eventTimingName)) {
1068
- this.#startTiming(eventTimingName, {
1069
- appId: this.hostId,
1070
- appUrl: window.location.href
1071
- });
1072
- timingMetricStarted = true;
1051
+ if (!eventTimingToken && this.#perfTracker.enabled) {
1052
+ eventTimingToken = this.#perfTracker.start(
1053
+ `ScriptingObject.Event.${scriptingObject.id}.${name}`,
1054
+ { appId: this.hostId, appUrl: window.location.href }
1055
+ );
1073
1056
  }
1074
1057
  this.#logger.debug({
1075
1058
  message: "Event dispatched and awaiting feedback",
@@ -1107,11 +1090,12 @@ class SSFHost {
1107
1090
  });
1108
1091
  throw ex;
1109
1092
  }).finally(() => {
1110
- if (timingMetricStarted)
1111
- this.#endTiming(eventTimingName, {
1093
+ if (eventTimingToken) {
1094
+ this.#perfTracker.end(eventTimingToken, {
1112
1095
  appId: this.hostId,
1113
1096
  appUrl: window.location.href
1114
1097
  });
1098
+ }
1115
1099
  });
1116
1100
  };
1117
1101
  /**
@@ -1155,7 +1139,7 @@ class SSFHost {
1155
1139
  const { openMode = import_types.OpenMode.Embed, popupWindowFeatures = {} } = options;
1156
1140
  const srcUrl = this.#getGuestUrl(url, searchParams);
1157
1141
  let guest = null;
1158
- this.#startTiming("SSF.Guest.Load", {
1142
+ this.#perfTracker.start("SSF.Guest.Load", {
1159
1143
  appId: instanceId,
1160
1144
  appUrl: srcUrl
1161
1145
  });
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var performanceTracker_exports = {};
20
+ __export(performanceTracker_exports, {
21
+ PerformanceTracker: () => PerformanceTracker
22
+ });
23
+ module.exports = __toCommonJS(performanceTracker_exports);
24
+ class PerformanceTracker {
25
+ #analyticsObj;
26
+ #enabled;
27
+ #samplingRatio;
28
+ #samplingConfigured = /* @__PURE__ */ new Set();
29
+ #dedupWindowMs;
30
+ #lastRecorded = /* @__PURE__ */ new Map();
31
+ #onError;
32
+ static DEFAULT_SAMPLING_RATIO = 0.05;
33
+ static DEFAULT_DEDUP_WINDOW_MS = 1e4;
34
+ static #LOW_FREQUENCY = /* @__PURE__ */ new Set(["SSF.Guest.Load"]);
35
+ constructor(options) {
36
+ this.#analyticsObj = options.analyticsObj;
37
+ this.#enabled = options.enabled ?? false;
38
+ this.#samplingRatio = options.samplingRatio ?? PerformanceTracker.DEFAULT_SAMPLING_RATIO;
39
+ this.#dedupWindowMs = options.dedupWindowMs ?? PerformanceTracker.DEFAULT_DEDUP_WINDOW_MS;
40
+ this.#onError = options.onError;
41
+ }
42
+ /**
43
+ * Whether performance tracking is active. Callers can use this to
44
+ * skip expensive work (e.g. building timing name strings) when
45
+ * tracking is disabled.
46
+ * @returns {boolean} true when performance tracking is enabled
47
+ */
48
+ get enabled() {
49
+ return this.#enabled;
50
+ }
51
+ /**
52
+ * Begin a timing measurement. Returns the timing name as a token when
53
+ * timing was started, or `undefined` when timing is disabled, deduped,
54
+ * or skipped. Pass the returned token to {@link end}.
55
+ * Never throws — failures are reported via the `onError` callback.
56
+ * @param {string} name - timing event name
57
+ * @param {TimingOptions} options - context passed to the analytics object
58
+ * @returns {string | undefined} the timing token, or undefined if skipped
59
+ */
60
+ start(name, options) {
61
+ if (!this.#enabled) return void 0;
62
+ try {
63
+ const lowFreq = PerformanceTracker.#LOW_FREQUENCY.has(name);
64
+ if (!lowFreq && !this.#shouldTrack(name)) return void 0;
65
+ if (!lowFreq && !this.#samplingConfigured.has(name)) {
66
+ this.#analyticsObj.setTimingEventSamplingRatio({
67
+ [name]: this.#samplingRatio
68
+ });
69
+ this.#samplingConfigured.add(name);
70
+ }
71
+ this.#analyticsObj.startTiming(name, options).catch((e) => {
72
+ this.#reportError(`startTiming failed: ${e.message}`);
73
+ });
74
+ return name;
75
+ } catch (e) {
76
+ this.#reportError(`startTiming failed: ${e.message}`);
77
+ return void 0;
78
+ }
79
+ }
80
+ /**
81
+ * End a timing measurement previously started via {@link start}.
82
+ * No-op when token is `undefined` (timing was not started).
83
+ * Never throws — failures are reported via the `onError` callback.
84
+ * @param {string | undefined} token - token returned by {@link start}
85
+ * @param {TimingOptions} options - context passed to the analytics object
86
+ */
87
+ end(token, options) {
88
+ if (!token) return;
89
+ try {
90
+ this.#analyticsObj.endTiming(token, options).catch((e) => {
91
+ this.#reportError(`endTiming failed: ${e.message}`);
92
+ });
93
+ } catch (e) {
94
+ this.#reportError(`endTiming failed: ${e.message}`);
95
+ }
96
+ }
97
+ #reportError(message) {
98
+ try {
99
+ this.#onError?.(message);
100
+ } catch {
101
+ }
102
+ }
103
+ #shouldTrack(name) {
104
+ if (this.#dedupWindowMs <= 0) return true;
105
+ const now = performance.now();
106
+ const last = this.#lastRecorded.get(name);
107
+ if (last !== void 0 && now - last < this.#dedupWindowMs) return false;
108
+ this.#lastRecorded.set(name, now);
109
+ return true;
110
+ }
111
+ }
package/dist/esm/guest.js CHANGED
@@ -51,9 +51,9 @@ class Guest {
51
51
  */
52
52
  #remoting;
53
53
  /**
54
- * analytics object
54
+ * performance tracker for timing measurements
55
55
  */
56
- #analyticsObj;
56
+ #perfTracker;
57
57
  /**
58
58
  * Create object representing guest application
59
59
  * @param {GuestOption} option - options for creating a guest application
@@ -68,7 +68,7 @@ class Guest {
68
68
  searchParams = {},
69
69
  openMode = OpenMode.Embed,
70
70
  remoting,
71
- analyticsObj
71
+ perfTracker
72
72
  } = option;
73
73
  this.id = guestId;
74
74
  this.title = title;
@@ -79,7 +79,7 @@ class Guest {
79
79
  this.window = window;
80
80
  this.openMode = openMode;
81
81
  this.capabilities = {};
82
- this.#analyticsObj = analyticsObj;
82
+ this.#perfTracker = perfTracker;
83
83
  this.#remoting = remoting;
84
84
  }
85
85
  /**
@@ -165,16 +165,17 @@ class Guest {
165
165
  * invokes event callback on the guest application
166
166
  * @param {EventObject} event - event object
167
167
  * @param {number} timeout - timeout in milliseconds
168
+ * @returns {Promise} resolves when the guest handles the event
168
169
  */
169
170
  dispatchEvent = (event, timeout) => {
170
- this.#analyticsObj.startTiming(
171
- `ScriptingObject.Event.${event.object.objectId}.${event.eventName}`,
172
- {
171
+ let timingToken;
172
+ if (this.#perfTracker.enabled) {
173
+ const name = `ScriptingObject.Event.${event.object.objectId}.${event.eventName}`;
174
+ timingToken = this.#perfTracker.start(name, {
173
175
  appId: this.id,
174
176
  appUrl: this.url
175
- }
176
- ).catch(() => {
177
- });
177
+ });
178
+ }
178
179
  return this.#remoting.invoke({
179
180
  targetWin: this.window,
180
181
  targetOrigin: this.origin,
@@ -182,14 +183,12 @@ class Guest {
182
183
  messageBody: event,
183
184
  responseTimeoutMs: timeout
184
185
  }).finally(() => {
185
- this.#analyticsObj.endTiming(
186
- `ScriptingObject.Event.${event.object.objectId}.${event.eventName}`,
187
- {
186
+ if (timingToken) {
187
+ this.#perfTracker.end(timingToken, {
188
188
  appId: this.id,
189
189
  appUrl: this.url
190
- }
191
- ).catch(() => {
192
- });
190
+ });
191
+ }
193
192
  });
194
193
  };
195
194
  /**
package/dist/esm/host.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "./types.js";
15
15
  import { Guest } from "./guest.js";
16
16
  import { flatten, isFunction, isTrustedDomain } from "./utils.js";
17
+ import { PerformanceTracker } from "./performanceTracker.js";
17
18
  const SANDBOX_DEFAULT = [
18
19
  IFrameSandboxValues.AllowScripts,
19
20
  IFrameSandboxValues.AllowPopups,
@@ -85,16 +86,13 @@ class SSFHost {
85
86
  */
86
87
  #guestMetadata = /* @__PURE__ */ new Map();
87
88
  /**
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.
89
+ * Performance tracker for host-side timing (with dedup).
91
90
  */
92
- #timingLastRecorded = /* @__PURE__ */ new Map();
91
+ #perfTracker;
93
92
  /**
94
- * Minimum interval (ms) between timing measurements for the same metric name.
93
+ * Performance tracker shared by all guests (no dedup).
95
94
  */
96
- #timingDedupWindowMs;
97
- static DEFAULT_TIMING_DEDUP_WINDOW_MS = 1e4;
95
+ #guestPerfTracker;
98
96
  /**
99
97
  * Create a new host
100
98
  * @param hostId unique identifier for the host
@@ -106,9 +104,24 @@ class SSFHost {
106
104
  if (!option?.analyticsObj) throw new Error("Analytics object is required");
107
105
  this.#logger = option.logger;
108
106
  this.#analyticsObj = option.analyticsObj;
109
- this.#timingDedupWindowMs = option?.timingDedupWindowMs ?? SSFHost.DEFAULT_TIMING_DEDUP_WINDOW_MS;
107
+ const perfOpts = {
108
+ analyticsObj: option.analyticsObj,
109
+ enabled: option?.measurePerformance,
110
+ samplingRatio: option?.performanceSamplingRatio,
111
+ onError: (msg) => this.#logger.debug(msg)
112
+ };
113
+ this.#perfTracker = new PerformanceTracker({
114
+ ...perfOpts,
115
+ dedupWindowMs: option?.performanceDedupWindowMs
116
+ });
117
+ this.#guestPerfTracker = new PerformanceTracker({
118
+ ...perfOpts,
119
+ dedupWindowMs: 0
120
+ });
110
121
  this.#correlationId = uuidv4();
111
- this.#remoting = new Remoting(this.#logger, this.#correlationId);
122
+ this.#remoting = new Remoting(this.#logger, this.#correlationId, {
123
+ unsafeAllowAnyGuestOrigin: option?.unsafeAllowAnyGuestOrigin
124
+ });
112
125
  if (option?.readyStateCallback && typeof option?.readyStateCallback !== "function")
113
126
  throw new Error("readyStateCallback must be a function");
114
127
  this.#readyStateCallback = option?.readyStateCallback || null;
@@ -130,34 +143,6 @@ class SSFHost {
130
143
  );
131
144
  });
132
145
  };
133
- #startTiming = (name, options) => {
134
- this.#analyticsObj.startTiming(name, options).catch((e) => {
135
- this.#logger.debug(
136
- `Analytics startTiming failed: ${e.message}`
137
- );
138
- });
139
- };
140
- #endTiming = (start, options) => {
141
- this.#analyticsObj.endTiming(start, options).catch((e) => {
142
- this.#logger.debug(`Analytics endTiming failed: ${e.message}`);
143
- });
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
- };
161
146
  #closeAllPopupGuests = () => {
162
147
  const popupIds = Array.from(this.#guests.values()).filter((guest) => guest.openMode === OpenMode.Popup).map((guest) => guest.id);
163
148
  popupIds.forEach((id) => this.unloadGuest(id));
@@ -280,7 +265,7 @@ class SSFHost {
280
265
  if (!guest.ready) {
281
266
  guest.ready = true;
282
267
  const guestInfo = guest.getInfo();
283
- this.#endTiming("SSF.Guest.Load", {
268
+ this.#perfTracker.end("SSF.Guest.Load", {
284
269
  appId: guestInfo.guestId,
285
270
  appUrl: guestInfo.guestUrl
286
271
  });
@@ -553,10 +538,10 @@ class SSFHost {
553
538
  return false;
554
539
  }
555
540
  const guestInfo = guest.getInfo();
556
- const timingName = `ScriptingObject.API.${objectId}.${body.functionName}`;
557
- const trackTiming = this.#shouldTrackTiming(timingName);
558
- if (trackTiming) {
559
- this.#startTiming(timingName, {
541
+ let timingToken;
542
+ if (this.#perfTracker.enabled) {
543
+ const name = `ScriptingObject.API.${objectId}.${body.functionName}`;
544
+ timingToken = this.#perfTracker.start(name, {
560
545
  appId: guestInfo.guestId,
561
546
  appUrl: guestInfo.guestUrl
562
547
  });
@@ -596,8 +581,8 @@ class SSFHost {
596
581
  ...guestInfo
597
582
  });
598
583
  }).finally(() => {
599
- if (trackTiming) {
600
- this.#endTiming(timingName, {
584
+ if (timingToken) {
585
+ this.#perfTracker.end(timingToken, {
601
586
  appId: guestInfo.guestId,
602
587
  appUrl: guestInfo.guestUrl
603
588
  });
@@ -672,7 +657,7 @@ class SSFHost {
672
657
  const guest = new Guest({
673
658
  ...param,
674
659
  remoting: this.#remoting,
675
- analyticsObj: this.#analyticsObj
660
+ perfTracker: this.#guestPerfTracker
676
661
  });
677
662
  guest.init();
678
663
  this.#guests.set(param.guestId, guest);
@@ -1045,18 +1030,16 @@ class SSFHost {
1045
1030
  };
1046
1031
  }
1047
1032
  const guestPromises = [];
1048
- const eventTimingName = `ScriptingObject.Event.${scriptingObject.id}.${name}`;
1049
- let timingMetricStarted = false;
1033
+ let eventTimingToken;
1050
1034
  const dispatchToGuest = (guest) => {
1051
1035
  const guestInfo = guest.getInfo();
1052
1036
  if (timeout && guest?.capabilities?.eventFeedback) {
1053
1037
  guestPromises.push(guest.dispatchEvent(eventObj, timeout));
1054
- if (!timingMetricStarted && this.#shouldTrackTiming(eventTimingName)) {
1055
- this.#startTiming(eventTimingName, {
1056
- appId: this.hostId,
1057
- appUrl: window.location.href
1058
- });
1059
- timingMetricStarted = true;
1038
+ if (!eventTimingToken && this.#perfTracker.enabled) {
1039
+ eventTimingToken = this.#perfTracker.start(
1040
+ `ScriptingObject.Event.${scriptingObject.id}.${name}`,
1041
+ { appId: this.hostId, appUrl: window.location.href }
1042
+ );
1060
1043
  }
1061
1044
  this.#logger.debug({
1062
1045
  message: "Event dispatched and awaiting feedback",
@@ -1094,11 +1077,12 @@ class SSFHost {
1094
1077
  });
1095
1078
  throw ex;
1096
1079
  }).finally(() => {
1097
- if (timingMetricStarted)
1098
- this.#endTiming(eventTimingName, {
1080
+ if (eventTimingToken) {
1081
+ this.#perfTracker.end(eventTimingToken, {
1099
1082
  appId: this.hostId,
1100
1083
  appUrl: window.location.href
1101
1084
  });
1085
+ }
1102
1086
  });
1103
1087
  };
1104
1088
  /**
@@ -1142,7 +1126,7 @@ class SSFHost {
1142
1126
  const { openMode = OpenMode.Embed, popupWindowFeatures = {} } = options;
1143
1127
  const srcUrl = this.#getGuestUrl(url, searchParams);
1144
1128
  let guest = null;
1145
- this.#startTiming("SSF.Guest.Load", {
1129
+ this.#perfTracker.start("SSF.Guest.Load", {
1146
1130
  appId: instanceId,
1147
1131
  appUrl: srcUrl
1148
1132
  });