@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,318 @@
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>Popup Focus E2E Test</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
8
+ <script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script>
9
+ </head>
10
+ <body class="bg-gray-50">
11
+ <header
12
+ class="bg-purple-600 text-white px-4 py-3 flex items-center justify-between"
13
+ >
14
+ <h1 class="text-lg font-semibold">Popup Focus E2E Test</h1>
15
+ <a href="./index.html" class="text-purple-200 hover:text-white text-sm"
16
+ >&larr; Back to main</a
17
+ >
18
+ </header>
19
+
20
+ <main class="mx-auto max-w-5xl px-4 py-4">
21
+ <!-- Architecture + Behavior -->
22
+ <div class="grid grid-cols-2 gap-4 mb-4">
23
+ <div class="bg-purple-50 border border-purple-200 rounded-md p-3">
24
+ <h2 class="text-sm font-bold text-purple-900 mb-2">
25
+ Behavior Under Test
26
+ </h2>
27
+ <ul class="text-xs text-purple-800 space-y-1 list-disc list-inside">
28
+ <li>
29
+ <strong>Trusted domains</strong> (.ice.com, .elliemae.com,
30
+ .ellielabs.com, localhost): <code>window.opener</code> is kept
31
+ intact. Re-opening uses <code>window.open('', name)</code> to
32
+ bring the popup to front.
33
+ </li>
34
+ <li>
35
+ <strong>Untrusted domains</strong> (everything else):
36
+ <code>window.opener = null</code> is set for security. Re-opening
37
+ falls back to <code>guest.window.focus()</code>.
38
+ </li>
39
+ <li>
40
+ The host tracks popup windows and detects closure via a periodic
41
+ monitor.
42
+ </li>
43
+ </ul>
44
+ </div>
45
+
46
+ <div class="bg-green-50 border border-green-200 rounded-md p-3">
47
+ <h2 class="text-sm font-bold text-green-900 mb-2">Test Steps</h2>
48
+ <ol
49
+ class="text-xs text-green-800 space-y-1.5 list-decimal list-inside"
50
+ >
51
+ <li>
52
+ Wait for <code>"Popup Focus Host ready"</code> in the Event Log.
53
+ </li>
54
+ <li>
55
+ <strong>Trusted popup test:</strong> Click "Open Title Service" →
56
+ popup opens. Click "Re-open Title Service" → the
57
+ <em>existing</em> popup should come to the foreground (no new
58
+ window).
59
+ </li>
60
+ <li>
61
+ <strong>Second trusted popup:</strong> Click "Open Credit Service"
62
+ → second popup opens. Click "Re-open Credit" → same foreground
63
+ behavior.
64
+ </li>
65
+ <li>
66
+ <strong>Untrusted popup test:</strong> Click "Open Third-Party" →
67
+ popup opens to <code>example.com</code>. Click "Re-open
68
+ Third-Party" → focus may or may not work (opener was nulled).
69
+ Observe the behavior.
70
+ </li>
71
+ <li>
72
+ Verify the Guest Status panel shows the open/closed state of each
73
+ popup.
74
+ </li>
75
+ <li>
76
+ Manually close a popup window, then check Guest Status updates to
77
+ "closed".
78
+ </li>
79
+ </ol>
80
+ </div>
81
+ </div>
82
+
83
+ <!-- Expected Behavior -->
84
+ <div class="bg-amber-50 border border-amber-200 rounded-md p-3 mb-4">
85
+ <h2 class="text-sm font-bold text-amber-900 mb-2">Expected Behavior</h2>
86
+ <div class="grid grid-cols-3 gap-3 text-xs">
87
+ <div>
88
+ <h3 class="font-semibold text-amber-800 mb-1">
89
+ Title / Credit Service (Trusted)
90
+ </h3>
91
+ <ul class="text-amber-900 list-disc list-inside space-y-0.5">
92
+ <li>"Open" → new popup window appears</li>
93
+ <li>
94
+ "Re-open" → existing popup comes to front,
95
+ <strong>no new window</strong>
96
+ </li>
97
+ <li>
98
+ Guest Status shows
99
+ <span class="text-green-600 font-semibold">open</span>
100
+ </li>
101
+ <li>Embedded Pricing Service renders normally</li>
102
+ </ul>
103
+ </div>
104
+ <div>
105
+ <h3 class="font-semibold text-amber-800 mb-1">
106
+ Third-Party (Untrusted)
107
+ </h3>
108
+ <ul class="text-amber-900 list-disc list-inside space-y-0.5">
109
+ <li>"Open" → popup opens to <code>example.com</code></li>
110
+ <li>
111
+ "Re-open" → <code>focus()</code> is called on stored reference
112
+ (browser may or may not honor it)
113
+ </li>
114
+ <li><code>opener</code> is <code>null</code> inside the popup</li>
115
+ </ul>
116
+ </div>
117
+ <div>
118
+ <h3 class="font-semibold text-amber-800 mb-1">Popup Closure</h3>
119
+ <ul class="text-amber-900 list-disc list-inside space-y-0.5">
120
+ <li>Close a popup manually (X button)</li>
121
+ <li>
122
+ Guest Status updates to
123
+ <span class="text-red-500 font-semibold">closed</span> within 2
124
+ seconds
125
+ </li>
126
+ <li>Re-opening a closed popup should open a new window</li>
127
+ </ul>
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ <!-- Popup controls -->
133
+ <div class="grid grid-cols-2 gap-4 mb-4">
134
+ <div class="bg-white rounded-lg shadow p-4">
135
+ <h2 class="text-sm font-semibold text-gray-700 mb-3">
136
+ Popup Actions
137
+ </h2>
138
+ <div class="flex flex-col gap-2">
139
+ <p class="text-xs text-gray-400 font-medium">
140
+ Trusted (localhost — opener preserved)
141
+ </p>
142
+ <button
143
+ data-testid="btn-open-title-popup"
144
+ id="btnOpenTitle"
145
+ class="rounded bg-purple-600 px-4 py-2 text-sm text-white hover:bg-purple-700"
146
+ >
147
+ Open Title Service (Popup)
148
+ </button>
149
+ <button
150
+ data-testid="btn-reopen-title-popup"
151
+ id="btnReopenTitle"
152
+ class="rounded bg-purple-500 px-4 py-2 text-sm text-white hover:bg-purple-600"
153
+ >
154
+ Re-open Title Service (should focus existing)
155
+ </button>
156
+ <button
157
+ data-testid="btn-open-credit-popup"
158
+ id="btnOpenCredit"
159
+ class="rounded bg-indigo-600 px-4 py-2 text-sm text-white hover:bg-indigo-700"
160
+ >
161
+ Open Credit Service (Popup)
162
+ </button>
163
+ <button
164
+ data-testid="btn-reopen-credit-popup"
165
+ id="btnReopenCredit"
166
+ class="rounded bg-indigo-500 px-4 py-2 text-sm text-white hover:bg-indigo-600"
167
+ >
168
+ Re-open Credit Service (should focus existing)
169
+ </button>
170
+ <hr class="my-2 border-gray-300" />
171
+ <p class="text-xs text-gray-400 font-medium">
172
+ Untrusted (opener nulled)
173
+ </p>
174
+ <button
175
+ data-testid="btn-open-thirdparty-popup"
176
+ id="btnOpenThirdParty"
177
+ class="rounded bg-red-600 px-4 py-2 text-sm text-white hover:bg-red-700"
178
+ >
179
+ Open Third-Party (Popup) — untrusted
180
+ </button>
181
+ <button
182
+ data-testid="btn-reopen-thirdparty-popup"
183
+ id="btnReopenThirdParty"
184
+ class="rounded bg-red-500 px-4 py-2 text-sm text-white hover:bg-red-600"
185
+ >
186
+ Re-open Third-Party (focus test — opener nulled)
187
+ </button>
188
+ </div>
189
+ </div>
190
+
191
+ <div class="bg-white rounded-lg shadow p-4">
192
+ <h2 class="text-sm font-semibold text-gray-700 mb-3">Guest Status</h2>
193
+ <div
194
+ data-testid="popup-guest-list"
195
+ id="guestList"
196
+ class="text-xs space-y-1 min-h-[100px]"
197
+ >
198
+ <p class="text-gray-400">No popup guests loaded yet</p>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ <!-- Event log -->
204
+ <div class="bg-white rounded-lg shadow p-4 mb-4">
205
+ <h2 class="text-sm font-semibold text-gray-700 mb-2">Event Log</h2>
206
+ <div
207
+ data-testid="popup-event-log"
208
+ id="eventLog"
209
+ class="text-xs bg-gray-100 rounded p-2 min-h-[100px] max-h-[250px] overflow-y-auto"
210
+ ></div>
211
+ </div>
212
+
213
+ <!-- Embedded guest area (for comparison) -->
214
+ <div class="mb-4">
215
+ <h2 class="text-sm font-semibold text-gray-700 mb-2">
216
+ Embedded Guest (for comparison)
217
+ </h2>
218
+ <div
219
+ data-testid="embedded-container"
220
+ id="embedded-container"
221
+ class="border-2 border-dashed border-purple-300 rounded-lg min-h-[150px]"
222
+ ></div>
223
+ </div>
224
+
225
+ <!-- Verification Checklist -->
226
+ <div class="bg-gray-100 border border-gray-300 rounded-md p-3">
227
+ <h2 class="text-sm font-bold text-gray-800 mb-2">
228
+ Verification Checklist
229
+ </h2>
230
+ <div class="grid grid-cols-2 gap-2 text-xs">
231
+ <label class="flex items-start gap-2">
232
+ <input type="checkbox" class="mt-0.5" />
233
+ <span
234
+ ><strong>TC-POP-01:</strong> Host initializes, embedded Pricing
235
+ Service guest loads without error</span
236
+ >
237
+ </label>
238
+ <label class="flex items-start gap-2">
239
+ <input type="checkbox" class="mt-0.5" />
240
+ <span
241
+ ><strong>TC-POP-02:</strong> "Open Title Service" opens a new
242
+ popup window</span
243
+ >
244
+ </label>
245
+ <label class="flex items-start gap-2">
246
+ <input type="checkbox" class="mt-0.5" />
247
+ <span
248
+ ><strong>TC-POP-03:</strong> "Re-open Title Service" brings the
249
+ existing popup to front —
250
+ <strong>no new window or about:blank tab</strong></span
251
+ >
252
+ </label>
253
+ <label class="flex items-start gap-2">
254
+ <input type="checkbox" class="mt-0.5" />
255
+ <span
256
+ ><strong>TC-POP-04:</strong> "Open Credit Service" opens a second
257
+ popup (both coexist)</span
258
+ >
259
+ </label>
260
+ <label class="flex items-start gap-2">
261
+ <input type="checkbox" class="mt-0.5" />
262
+ <span
263
+ ><strong>TC-POP-05:</strong> "Re-open Credit Service" brings
264
+ credit popup to front</span
265
+ >
266
+ </label>
267
+ <label class="flex items-start gap-2">
268
+ <input type="checkbox" class="mt-0.5" />
269
+ <span
270
+ ><strong>TC-POP-06:</strong> Guest Status shows each popup as
271
+ <span class="text-green-600 font-semibold">open</span></span
272
+ >
273
+ </label>
274
+ <label class="flex items-start gap-2">
275
+ <input type="checkbox" class="mt-0.5" />
276
+ <span
277
+ ><strong>TC-POP-07:</strong> Manually close a popup → Guest Status
278
+ updates to
279
+ <span class="text-red-500 font-semibold">closed</span> within
280
+ ~2s</span
281
+ >
282
+ </label>
283
+ <label class="flex items-start gap-2">
284
+ <input type="checkbox" class="mt-0.5" />
285
+ <span
286
+ ><strong>TC-POP-08:</strong> Re-opening a closed popup opens a new
287
+ window (not just focus)</span
288
+ >
289
+ </label>
290
+ <label class="flex items-start gap-2">
291
+ <input type="checkbox" class="mt-0.5" />
292
+ <span
293
+ ><strong>TC-POP-09:</strong> "Open Third-Party" opens
294
+ <code>example.com</code> popup</span
295
+ >
296
+ </label>
297
+ <label class="flex items-start gap-2">
298
+ <input type="checkbox" class="mt-0.5" />
299
+ <span
300
+ ><strong>TC-POP-10:</strong> "Re-open Third-Party" attempts
301
+ <code>focus()</code> (may open about:blank — expected for
302
+ untrusted, opener was nulled)</span
303
+ >
304
+ </label>
305
+ <label class="flex items-start gap-2">
306
+ <input type="checkbox" class="mt-0.5" />
307
+ <span
308
+ ><strong>TC-POP-11:</strong> No console errors during trusted
309
+ popup operations</span
310
+ >
311
+ </label>
312
+ </div>
313
+ </div>
314
+ </main>
315
+
316
+ <script src="./popup-focus-host.js" type="module"></script>
317
+ </body>
318
+ </html>
package/dist/cjs/utils.js CHANGED
@@ -20,7 +20,8 @@ var utils_exports = {};
20
20
  __export(utils_exports, {
21
21
  flatten: () => flatten,
22
22
  getOrigin: () => getOrigin,
23
- isFunction: () => isFunction
23
+ isFunction: () => isFunction,
24
+ isTrustedDomain: () => isTrustedDomain
24
25
  });
25
26
  module.exports = __toCommonJS(utils_exports);
26
27
  const getOrigin = (url) => {
@@ -37,3 +38,15 @@ const flatten = (source) => source.flat(Infinity).filter((v) => v !== void 0);
37
38
  function isFunction(value) {
38
39
  return typeof value === "function";
39
40
  }
41
+ const TRUSTED_DOMAIN_SUFFIXES = [".ice.com", ".elliemae.com", ".ellielabs.com"];
42
+ const TRUSTED_HOSTNAMES = ["localhost", "127.0.0.1"];
43
+ const isTrustedDomain = (url) => {
44
+ try {
45
+ const { hostname } = new URL(url, document.baseURI);
46
+ return TRUSTED_HOSTNAMES.includes(hostname) || TRUSTED_DOMAIN_SUFFIXES.some(
47
+ (suffix) => hostname === suffix.slice(1) || hostname.endsWith(suffix)
48
+ );
49
+ } catch {
50
+ return false;
51
+ }
52
+ };
@@ -36,13 +36,16 @@
36
36
  </main>
37
37
  <script type="module">
38
38
  import { Loan } from './loan-object.js';
39
+ import { Application } from './application-object-v2.js';
39
40
  import { getGuestBaseUrl, getHost } from './utils.js';
40
41
  import { Analytics } from './analytics-object-v2.js';
41
42
 
42
43
  const analyticsObj = new Analytics();
43
44
  const hostV2 = getHost(analyticsObj);
44
45
  const loanObj = new Loan();
46
+ const appObj = new Application();
45
47
  hostV2.addScriptingObject(loanObj);
48
+ hostV2.addScriptingObject(appObj);
46
49
  const params = new URLSearchParams(document.location.search);
47
50
  const { id } = hostV2.loadGuest({
48
51
  id: 'guestV1',
@@ -0,0 +1,262 @@
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>CallChain E2E Test - Root Host</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
8
+ <script src="https://cdn.mortgagetech.q1.ice.com/pui-diagnostics@3"></script>
9
+ </head>
10
+ <body class="bg-gray-50">
11
+ <header
12
+ class="bg-indigo-600 text-white px-4 py-3 flex items-center justify-between"
13
+ >
14
+ <h1 class="text-lg font-semibold">CallChain E2E Test — Root Host (A)</h1>
15
+ <a href="./index.html" class="text-indigo-200 hover:text-white text-sm"
16
+ >&larr; Back to main</a
17
+ >
18
+ </header>
19
+
20
+ <main class="mx-auto max-w-7xl px-4 py-4">
21
+ <!-- Architecture + Instructions -->
22
+ <div class="grid grid-cols-2 gap-4 mb-4">
23
+ <div class="bg-blue-50 border border-blue-200 rounded-md p-3">
24
+ <h2 class="text-sm font-bold text-blue-900 mb-2">Architecture</h2>
25
+ <pre class="text-xs text-blue-800 leading-relaxed">
26
+ Root Host (A) [localhost:4000]
27
+ ├─ Exposes: <strong>TestService</strong> scripting object
28
+ ├─ Loads guest <strong>"intermediateHost"</strong>
29
+ │ metadata: { vendor: "Acme Credit Corp", tier: "premium" }
30
+
31
+ └─ Intermediate (B) [localhost:4000, same-origin iframe]
32
+ ├─ Connects as V2 guest to A
33
+ ├─ Loads ice.guest from localhost:4001/index.js
34
+ ├─ Gets TestService proxy, clones it
35
+ ├─ Creates own SSFHost, loads guest <strong>"grandchildGuest"</strong>
36
+ │ metadata: { role: "validator", region: "US-East" }
37
+
38
+ └─ Grandchild (C) [localhost:4001, cross-origin iframe]
39
+ └─ Connects as V2 guest to B
40
+ └─ Gets TestService proxy → calls methods</pre
41
+ >
42
+ </div>
43
+
44
+ <div class="bg-green-50 border border-green-200 rounded-md p-3">
45
+ <h2 class="text-sm font-bold text-green-900 mb-2">Test Steps</h2>
46
+ <ol class="text-xs text-green-800 space-y-2 list-decimal list-inside">
47
+ <li>
48
+ <strong>Wait for initialization</strong> — check the Invocation
49
+ Log shows: <code>"Root Host (A) ready"</code>, Intermediate shows
50
+ <code>"Intermediate (B) ready"</code>, Grandchild shows
51
+ <code>"Got TestService proxy"</code>.
52
+ </li>
53
+ <li>
54
+ <strong>Click any button</strong> inside the Grandchild (C) panel
55
+ (e.g. "Call getCallerInfo()").
56
+ </li>
57
+ <li>
58
+ <strong>Verify on the left</strong> — the "callContext.guest" and
59
+ "callContext.callChain" panels update with the expected values
60
+ shown below.
61
+ </li>
62
+ <li>
63
+ <strong>Verify on grandchild</strong> — the return value appears
64
+ in the grandchild's result area (proves round-trip works).
65
+ </li>
66
+ </ol>
67
+ </div>
68
+ </div>
69
+
70
+ <!-- Expected Values -->
71
+ <div class="bg-amber-50 border border-amber-200 rounded-md p-3 mb-4">
72
+ <h2 class="text-sm font-bold text-amber-900 mb-2">
73
+ Expected Values (when grandchild C calls any method)
74
+ </h2>
75
+ <div class="grid grid-cols-2 gap-4 text-xs">
76
+ <div>
77
+ <h3 class="font-semibold text-amber-800 mb-1">
78
+ callContext.guest (direct caller = B)
79
+ </h3>
80
+ <pre
81
+ data-testid="expected-guest"
82
+ class="bg-white rounded p-2 text-amber-900"
83
+ >
84
+ {
85
+ "id": "intermediateHost",
86
+ "title": "Intermediate Guest+Host (B)",
87
+ "url": "...callchain-intermediate.html"
88
+ }</pre
89
+ >
90
+ </div>
91
+ <div>
92
+ <h3 class="font-semibold text-amber-800 mb-1">
93
+ callContext.callChain (originator chain)
94
+ </h3>
95
+ <pre
96
+ data-testid="expected-callchain"
97
+ class="bg-white rounded p-2 text-amber-900"
98
+ >
99
+ [
100
+ {
101
+ "id": "grandchildGuest",
102
+ "metadata": { "role": "validator", "region": "US-East" }
103
+ },
104
+ {
105
+ "id": "intermediateHost",
106
+ "metadata": { "vendor": "Acme Credit Corp", "tier": "premium" }
107
+ }
108
+ ]</pre
109
+ >
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ <div class="grid grid-cols-2 gap-4">
115
+ <!-- Left: callContext results -->
116
+ <div>
117
+ <h2 class="text-md font-semibold mb-2">
118
+ Actual Results (from Root Host A)
119
+ </h2>
120
+
121
+ <div
122
+ data-testid="direct-caller-panel"
123
+ class="bg-white rounded-lg shadow p-3 mb-3"
124
+ >
125
+ <h3 class="text-sm font-medium text-gray-500 mb-1">
126
+ callContext.guest (direct caller)
127
+ </h3>
128
+ <pre
129
+ data-testid="direct-caller-result"
130
+ id="directCallerResult"
131
+ class="text-xs bg-gray-100 rounded p-2 min-h-[40px] whitespace-pre-wrap"
132
+ >
133
+ Waiting — click a button in Grandchild (C)...</pre
134
+ >
135
+ </div>
136
+
137
+ <div
138
+ data-testid="callchain-panel"
139
+ class="bg-white rounded-lg shadow p-3 mb-3"
140
+ >
141
+ <h3 class="text-sm font-medium text-gray-500 mb-1">
142
+ callContext.callChain (original callers)
143
+ </h3>
144
+ <pre
145
+ data-testid="callchain-result"
146
+ id="callChainResult"
147
+ class="text-xs bg-gray-100 rounded p-2 min-h-[60px] whitespace-pre-wrap"
148
+ >
149
+ Waiting — click a button in Grandchild (C)...</pre
150
+ >
151
+ </div>
152
+
153
+ <div
154
+ data-testid="method-result-panel"
155
+ class="bg-white rounded-lg shadow p-3 mb-3"
156
+ >
157
+ <h3 class="text-sm font-medium text-gray-500 mb-1">
158
+ Method return value (returned to grandchild)
159
+ </h3>
160
+ <pre
161
+ data-testid="method-return-result"
162
+ id="methodReturnResult"
163
+ class="text-xs bg-gray-100 rounded p-2 min-h-[40px] whitespace-pre-wrap"
164
+ >
165
+ Waiting — click a button in Grandchild (C)...</pre
166
+ >
167
+ </div>
168
+
169
+ <div
170
+ data-testid="invocation-log-panel"
171
+ class="bg-white rounded-lg shadow p-3"
172
+ >
173
+ <h3 class="text-sm font-medium text-gray-500 mb-1">
174
+ Invocation Log
175
+ </h3>
176
+ <div
177
+ data-testid="invocation-log"
178
+ id="invocationLog"
179
+ class="text-xs bg-gray-100 rounded p-2 min-h-[80px] max-h-[200px] overflow-y-auto"
180
+ ></div>
181
+ </div>
182
+ </div>
183
+
184
+ <!-- Right: guest container -->
185
+ <div>
186
+ <h2 class="text-md font-semibold mb-2">
187
+ Intermediate Guest (B) → Grandchild Guest (C)
188
+ </h2>
189
+ <div
190
+ data-testid="intermediate-guest-container"
191
+ id="intermediate-container"
192
+ class="border-2 border-dashed border-indigo-300 rounded-lg h-[400px]"
193
+ ></div>
194
+ </div>
195
+ </div>
196
+
197
+ <!-- Verification Checklist -->
198
+ <div class="bg-gray-100 border border-gray-300 rounded-md p-3 mt-4">
199
+ <h2 class="text-sm font-bold text-gray-800 mb-2">
200
+ Verification Checklist
201
+ </h2>
202
+ <div class="grid grid-cols-2 gap-2 text-xs">
203
+ <label class="flex items-start gap-2">
204
+ <input type="checkbox" class="mt-0.5" />
205
+ <span
206
+ ><strong>TC-CC-01:</strong> All three layers initialized without
207
+ errors (Log shows "ready" messages, no ERROR lines)</span
208
+ >
209
+ </label>
210
+ <label class="flex items-start gap-2">
211
+ <input type="checkbox" class="mt-0.5" />
212
+ <span
213
+ ><strong>TC-CC-02:</strong> <code>callContext.guest.id</code> =
214
+ <code>"intermediateHost"</code> (B is the direct caller, not
215
+ C)</span
216
+ >
217
+ </label>
218
+ <label class="flex items-start gap-2">
219
+ <input type="checkbox" class="mt-0.5" />
220
+ <span
221
+ ><strong>TC-CC-03:</strong> <code>callChain[0].id</code> =
222
+ <code>"grandchildGuest"</code> with metadata
223
+ <code>{ role, region }</code></span
224
+ >
225
+ </label>
226
+ <label class="flex items-start gap-2">
227
+ <input type="checkbox" class="mt-0.5" />
228
+ <span
229
+ ><strong>TC-CC-04:</strong> <code>callChain[1].id</code> =
230
+ <code>"intermediateHost"</code> with metadata
231
+ <code>{ vendor, tier }</code></span
232
+ >
233
+ </label>
234
+ <label class="flex items-start gap-2">
235
+ <input type="checkbox" class="mt-0.5" />
236
+ <span
237
+ ><strong>TC-CC-05:</strong> Grandchild (C) receives the method
238
+ return value (result appears in C's panel)</span
239
+ >
240
+ </label>
241
+ <label class="flex items-start gap-2">
242
+ <input type="checkbox" class="mt-0.5" />
243
+ <span
244
+ ><strong>TC-CC-06:</strong> All three methods work:
245
+ <code>getCallerInfo()</code>, <code>ping()</code>,
246
+ <code>getLoanDetails()</code></span
247
+ >
248
+ </label>
249
+ <label class="flex items-start gap-2">
250
+ <input type="checkbox" class="mt-0.5" />
251
+ <span
252
+ ><strong>TC-CC-07:</strong> No cross-origin or console errors
253
+ during invocations</span
254
+ >
255
+ </label>
256
+ </div>
257
+ </div>
258
+ </main>
259
+
260
+ <script src="./callchain-host.js" type="module"></script>
261
+ </body>
262
+ </html>