@elliemae/ssf-host 2.23.4 → 2.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/callchain-host.html +262 -0
- package/dist/cjs/callchain-intermediate.html +92 -0
- package/dist/cjs/e2e-host.html +603 -0
- package/dist/cjs/e2e-index.html +234 -0
- package/dist/cjs/host.js +100 -43
- package/dist/cjs/index.html +304 -151
- package/dist/cjs/popup-focus-host.html +318 -0
- package/dist/cjs/utils.js +14 -1
- package/dist/cjs/v2-host-v1-guest.html +3 -0
- package/dist/esm/callchain-host.html +262 -0
- package/dist/esm/callchain-intermediate.html +92 -0
- package/dist/esm/e2e-host.html +603 -0
- package/dist/esm/e2e-index.html +234 -0
- package/dist/esm/host.js +101 -44
- package/dist/esm/index.html +304 -151
- package/dist/esm/popup-focus-host.html +318 -0
- package/dist/esm/utils.js +14 -1
- package/dist/esm/v2-host-v1-guest.html +3 -0
- package/dist/public/callchain-host.html +34 -0
- package/dist/public/callchain-host.js +3 -0
- package/dist/public/callchain-host.js.br +0 -0
- package/dist/public/callchain-host.js.gz +0 -0
- package/dist/public/callchain-host.js.map +1 -0
- package/dist/public/callchain-intermediate.html +1 -0
- package/dist/public/e2e-host.html +5 -0
- package/dist/public/e2e-host.js +8 -0
- package/dist/public/e2e-host.js.br +0 -0
- package/dist/public/e2e-host.js.gz +0 -0
- package/dist/public/e2e-host.js.map +1 -0
- package/dist/public/e2e-index.html +1 -0
- package/dist/public/index.html +1 -1
- package/dist/public/init.js +1 -1
- package/dist/public/init.js.br +0 -0
- package/dist/public/init.js.gz +0 -0
- package/dist/public/init.js.map +1 -1
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js +3 -0
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.br +0 -0
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.gz +0 -0
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.map +1 -0
- package/dist/public/loan-object.js +1 -1
- package/dist/public/loan-object.js.br +0 -0
- package/dist/public/loan-object.js.gz +0 -0
- package/dist/public/loan-object.js.map +1 -1
- package/dist/public/popup-focus-host.html +1 -0
- package/dist/public/popup-focus-host.js +6 -0
- package/dist/public/popup-focus-host.js.br +0 -0
- package/dist/public/popup-focus-host.js.gz +0 -0
- package/dist/public/popup-focus-host.js.map +1 -0
- package/dist/public/v1-guest-v2-host.html +1 -1
- package/dist/public/v2-host-v1-guest.html +1 -1
- package/dist/types/lib/host.d.ts +1 -0
- package/dist/types/lib/ihost.d.ts +15 -0
- package/dist/types/lib/tests/timingDedup.test.d.ts +1 -0
- package/dist/types/lib/utils.d.ts +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dist/umd/callchain-host.html +34 -0
- package/dist/umd/callchain-host.js +3 -0
- package/dist/umd/callchain-host.js.br +0 -0
- package/dist/umd/callchain-host.js.gz +0 -0
- package/dist/umd/callchain-host.js.map +1 -0
- package/dist/umd/callchain-intermediate.html +1 -0
- package/dist/umd/e2e-host.html +5 -0
- package/dist/umd/e2e-host.js +8 -0
- package/dist/umd/e2e-host.js.br +0 -0
- package/dist/umd/e2e-host.js.gz +0 -0
- package/dist/umd/e2e-host.js.map +1 -0
- package/dist/umd/e2e-index.html +1 -0
- package/dist/umd/index.html +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.br +0 -0
- package/dist/umd/index.js.gz +0 -0
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/init.js +1 -1
- package/dist/umd/init.js.br +0 -0
- package/dist/umd/init.js.gz +0 -0
- package/dist/umd/init.js.map +1 -1
- package/dist/umd/loan-object.js +1 -1
- package/dist/umd/loan-object.js.br +0 -0
- package/dist/umd/loan-object.js.gz +0 -0
- package/dist/umd/loan-object.js.map +1 -1
- package/dist/umd/popup-focus-host.html +1 -0
- package/dist/umd/popup-focus-host.js +6 -0
- package/dist/umd/popup-focus-host.js.br +0 -0
- package/dist/umd/popup-focus-host.js.gz +0 -0
- package/dist/umd/popup-focus-host.js.map +1 -0
- package/dist/umd/v2-host-v1-guest.html +1 -1
- package/package.json +5 -5
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js +0 -3
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.br +0 -0
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.gz +0 -0
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.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 & 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 • TC-SO-01..08 • TC-EVT-01..04 •
|
|
85
|
+
TC-LIFE-01..03 • TC-META-01 • 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 & 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 & 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: {
|
|
258
|
+
value: {
|
|
259
|
+
guest,
|
|
260
|
+
...callerChain?.length ? { callChain: callerChain } : {}
|
|
261
|
+
},
|
|
226
262
|
configurable: true,
|
|
227
263
|
enumerable: true,
|
|
228
264
|
writable: true
|
|
@@ -335,7 +371,7 @@ class SSFHost {
|
|
|
335
371
|
requestId,
|
|
336
372
|
response: objects
|
|
337
373
|
});
|
|
338
|
-
this.#logger.
|
|
374
|
+
this.#logger.debug({
|
|
339
375
|
message: `name of scripting objects returned`,
|
|
340
376
|
requestId,
|
|
341
377
|
objects,
|
|
@@ -378,7 +414,7 @@ class SSFHost {
|
|
|
378
414
|
requestId,
|
|
379
415
|
response: this.#encodeResponse(obj, guest)
|
|
380
416
|
});
|
|
381
|
-
this.#logger.
|
|
417
|
+
this.#logger.debug({
|
|
382
418
|
message: `Scripting Object returned`,
|
|
383
419
|
requestId,
|
|
384
420
|
scriptingObject: objectId,
|
|
@@ -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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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,
|
|
@@ -546,7 +587,7 @@ class SSFHost {
|
|
|
546
587
|
requestId,
|
|
547
588
|
response: this.#encodeResponse(val, guest)
|
|
548
589
|
});
|
|
549
|
-
this.#logger.
|
|
590
|
+
this.#logger.debug({
|
|
550
591
|
message: `Value returned for Scripting Object method call`,
|
|
551
592
|
requestId,
|
|
552
593
|
scriptingObject: objectId,
|
|
@@ -560,7 +601,7 @@ class SSFHost {
|
|
|
560
601
|
requestId,
|
|
561
602
|
ex
|
|
562
603
|
});
|
|
563
|
-
this.#logger.
|
|
604
|
+
this.#logger.error({
|
|
564
605
|
message: `Exception thrown for Scripting Object method call`,
|
|
565
606
|
requestId,
|
|
566
607
|
scriptingObject: objectId,
|
|
@@ -568,13 +609,12 @@ class SSFHost {
|
|
|
568
609
|
...guestInfo
|
|
569
610
|
});
|
|
570
611
|
}).finally(() => {
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
721
|
-
|
|
722
|
-
|
|
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
|
-
|
|
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,22 +1058,20 @@ 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
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
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
|
-
this.#logger.
|
|
1074
|
+
this.#logger.debug({
|
|
1017
1075
|
message: "Event dispatched and awaiting feedback",
|
|
1018
1076
|
scriptingEventId: id,
|
|
1019
1077
|
...guestInfo
|
|
@@ -1023,7 +1081,7 @@ class SSFHost {
|
|
|
1023
1081
|
messageType: import_microfe_common.MessageType.ObjectEvent,
|
|
1024
1082
|
messageBody: eventObj
|
|
1025
1083
|
});
|
|
1026
|
-
this.#logger.
|
|
1084
|
+
this.#logger.debug({
|
|
1027
1085
|
message: "Event dispatched",
|
|
1028
1086
|
scriptingEventId: id,
|
|
1029
1087
|
...guestInfo
|
|
@@ -1035,8 +1093,8 @@ class SSFHost {
|
|
|
1035
1093
|
} else {
|
|
1036
1094
|
this.#guests.forEach(dispatchToGuest);
|
|
1037
1095
|
}
|
|
1038
|
-
|
|
1039
|
-
this.#logger.
|
|
1096
|
+
return Promise.all(guestPromises).then((values) => {
|
|
1097
|
+
this.#logger.debug({
|
|
1040
1098
|
message: "Event feedback received",
|
|
1041
1099
|
scriptingEventId: id
|
|
1042
1100
|
});
|
|
@@ -1050,15 +1108,11 @@ class SSFHost {
|
|
|
1050
1108
|
throw ex;
|
|
1051
1109
|
}).finally(() => {
|
|
1052
1110
|
if (timingMetricStarted)
|
|
1053
|
-
this.#endTiming(
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
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`,
|