@elliemae/ssf-guest 2.23.6 → 2.25.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 (58) hide show
  1. package/dist/cjs/callchain-grandchild.html +60 -0
  2. package/dist/cjs/e2e-guest.html +323 -0
  3. package/dist/cjs/guest.js +36 -14
  4. package/dist/cjs/tests/utils.js +6 -2
  5. package/dist/esm/callchain-grandchild.html +60 -0
  6. package/dist/esm/e2e-guest.html +323 -0
  7. package/dist/esm/guest.js +38 -15
  8. package/dist/esm/tests/utils.js +6 -2
  9. package/dist/public/callchain-grandchild.html +1 -0
  10. package/dist/public/callchain-grandchild.js +4 -0
  11. package/dist/public/callchain-grandchild.js.br +0 -0
  12. package/dist/public/callchain-grandchild.js.gz +0 -0
  13. package/dist/public/callchain-grandchild.js.map +1 -0
  14. package/dist/public/creditService.html +1 -1
  15. package/dist/public/e2e-guest.html +1 -0
  16. package/dist/public/e2e-guest.js +3 -0
  17. package/dist/public/e2e-guest.js.br +0 -0
  18. package/dist/public/e2e-guest.js.gz +0 -0
  19. package/dist/public/e2e-guest.js.map +1 -0
  20. package/dist/public/index.html +1 -1
  21. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js +3 -0
  22. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js.br +0 -0
  23. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js.gz +0 -0
  24. package/dist/public/js/emuiSsfGuest.42c1a5949e7eb13de9e0.js.map +1 -0
  25. package/dist/public/loanValidation.html +1 -1
  26. package/dist/public/pricingService.html +1 -1
  27. package/dist/public/titleService.html +1 -1
  28. package/dist/public/util.js +1 -1
  29. package/dist/public/util.js.br +0 -0
  30. package/dist/public/util.js.gz +0 -0
  31. package/dist/public/util.js.map +1 -1
  32. package/dist/public/v2-guest.html +1 -1
  33. package/dist/types/lib/guest.d.ts +16 -0
  34. package/dist/types/lib/tests/utils.d.ts +4 -2
  35. package/dist/types/tsconfig.tsbuildinfo +1 -1
  36. package/dist/umd/callchain-grandchild.html +1 -0
  37. package/dist/umd/callchain-grandchild.js +4 -0
  38. package/dist/umd/callchain-grandchild.js.br +0 -0
  39. package/dist/umd/callchain-grandchild.js.gz +0 -0
  40. package/dist/umd/callchain-grandchild.js.map +1 -0
  41. package/dist/umd/e2e-guest.html +1 -0
  42. package/dist/umd/e2e-guest.js +3 -0
  43. package/dist/umd/e2e-guest.js.br +0 -0
  44. package/dist/umd/e2e-guest.js.gz +0 -0
  45. package/dist/umd/e2e-guest.js.map +1 -0
  46. package/dist/umd/index.js +1 -1
  47. package/dist/umd/index.js.br +0 -0
  48. package/dist/umd/index.js.gz +0 -0
  49. package/dist/umd/index.js.map +1 -1
  50. package/dist/umd/util.js +1 -1
  51. package/dist/umd/util.js.br +0 -0
  52. package/dist/umd/util.js.gz +0 -0
  53. package/dist/umd/util.js.map +1 -1
  54. package/package.json +4 -4
  55. package/dist/public/js/emuiSsfGuest.650afabac7fe99fb3a5b.js +0 -3
  56. package/dist/public/js/emuiSsfGuest.650afabac7fe99fb3a5b.js.br +0 -0
  57. package/dist/public/js/emuiSsfGuest.650afabac7fe99fb3a5b.js.gz +0 -0
  58. package/dist/public/js/emuiSsfGuest.650afabac7fe99fb3a5b.js.map +0 -1
@@ -0,0 +1,60 @@
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>Grandchild Guest (C)</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-green-50 p-2">
11
+ <h3 class="text-sm font-semibold text-green-800 mb-2">
12
+ Grandchild Guest (C)
13
+ </h3>
14
+
15
+ <div class="flex flex-wrap gap-2 mb-2">
16
+ <button
17
+ data-testid="btn-get-caller-info"
18
+ id="btnGetCallerInfo"
19
+ disabled
20
+ class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
21
+ >
22
+ Call getCallerInfo()
23
+ </button>
24
+ <button
25
+ data-testid="btn-ping"
26
+ id="btnPing"
27
+ disabled
28
+ class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
29
+ >
30
+ Call ping("hello from C")
31
+ </button>
32
+ <button
33
+ data-testid="btn-get-loan"
34
+ id="btnGetLoan"
35
+ disabled
36
+ class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
37
+ >
38
+ Call getLoanDetails()
39
+ </button>
40
+ </div>
41
+
42
+ <div
43
+ data-testid="grandchild-status"
44
+ id="status"
45
+ class="text-xs bg-green-100 rounded p-2 mb-2 min-h-[20px]"
46
+ >
47
+ Connecting...
48
+ </div>
49
+
50
+ <div
51
+ data-testid="grandchild-result"
52
+ id="result"
53
+ class="text-xs bg-white rounded p-2 min-h-[40px]"
54
+ >
55
+ <pre id="resultPre" class="whitespace-pre-wrap">No results yet</pre>
56
+ </div>
57
+
58
+ <script src="./callchain-grandchild.js" type="module"></script>
59
+ </body>
60
+ </html>
@@ -0,0 +1,323 @@
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 Guest</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-emerald-50 p-2 text-xs">
11
+ <!-- Header with connection status -->
12
+ <div class="flex items-center justify-between mb-1">
13
+ <h3 class="font-semibold text-emerald-800">E2E Guest</h3>
14
+ <span
15
+ data-testid="guest-connection-status"
16
+ id="connStatus"
17
+ class="px-2 py-0.5 rounded text-white bg-gray-400"
18
+ >disconnected</span
19
+ >
20
+ </div>
21
+
22
+ <!-- Guest info -->
23
+ <div
24
+ data-testid="guest-info"
25
+ id="guestInfo"
26
+ class="bg-emerald-100 rounded p-1 mb-1 text-xs text-emerald-700"
27
+ >
28
+ Params: <span id="searchParams">—</span>
29
+ </div>
30
+
31
+ <!-- Quick guide -->
32
+ <details
33
+ class="bg-emerald-100 rounded p-1 mb-1 text-[10px] text-emerald-700"
34
+ >
35
+ <summary class="font-semibold cursor-pointer">
36
+ Quick Guide (click to expand)
37
+ </summary>
38
+ <ul class="mt-1 list-disc list-inside space-y-0.5">
39
+ <li>
40
+ <strong>Objects tab:</strong> Use <code>getObject()</code> to fetch a
41
+ scripting object, then <code>Invoke</code> to call a method. Result +
42
+ errors appear below.
43
+ </li>
44
+ <li>
45
+ <strong>Events tab:</strong> Subscribe to events (with or without
46
+ criteria). When the host dispatches, received events appear below. Use
47
+ the Feedback dropdown to control what the callback returns.
48
+ </li>
49
+ <li>
50
+ <strong>Lifecycle tab:</strong> Change log level or
51
+ disconnect/reconnect. Status badge at top right should reflect the
52
+ state.
53
+ </li>
54
+ <li>
55
+ <strong>Verify:</strong> Connection badge =
56
+ <span class="text-green-700 font-bold">connected</span>. After
57
+ getObject: methods are listed. After invoke: result appears. After
58
+ subscribe+dispatch: events appear.
59
+ </li>
60
+ </ul>
61
+ </details>
62
+
63
+ <!-- Tabs -->
64
+ <div class="flex gap-1 mb-2 border-b border-emerald-300">
65
+ <button
66
+ data-testid="tab-objects"
67
+ class="tab-btn px-2 py-1 rounded-t font-medium bg-emerald-200 text-emerald-800"
68
+ data-tab="objects"
69
+ >
70
+ Objects
71
+ </button>
72
+ <button
73
+ data-testid="tab-events"
74
+ class="tab-btn px-2 py-1 rounded-t text-emerald-600"
75
+ data-tab="events"
76
+ >
77
+ Events
78
+ </button>
79
+ <button
80
+ data-testid="tab-lifecycle"
81
+ class="tab-btn px-2 py-1 rounded-t text-emerald-600"
82
+ data-tab="lifecycle"
83
+ >
84
+ Lifecycle
85
+ </button>
86
+ </div>
87
+
88
+ <!-- Tab: Objects -->
89
+ <div id="panel-objects" class="tab-panel">
90
+ <div class="space-y-1">
91
+ <button
92
+ data-testid="btn-list-objects"
93
+ id="btnListObjects"
94
+ class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700"
95
+ >
96
+ listObjects()
97
+ </button>
98
+ <div
99
+ data-testid="objects-list"
100
+ id="objectsList"
101
+ class="bg-white rounded p-1 min-h-[20px]"
102
+ >
103
+
104
+ </div>
105
+
106
+ <div class="flex gap-1 items-center">
107
+ <input
108
+ data-testid="input-object-id"
109
+ id="inputObjectId"
110
+ type="text"
111
+ value="Loan"
112
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"
113
+ placeholder="Object ID"
114
+ />
115
+ <button
116
+ data-testid="btn-get-object"
117
+ id="btnGetObject"
118
+ class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700"
119
+ >
120
+ getObject()
121
+ </button>
122
+ </div>
123
+ <div
124
+ data-testid="current-object"
125
+ id="currentObject"
126
+ class="bg-white rounded p-1 min-h-[20px]"
127
+ >
128
+
129
+ </div>
130
+
131
+ <div class="flex gap-1 items-center">
132
+ <input
133
+ data-testid="input-method"
134
+ id="inputMethod"
135
+ type="text"
136
+ value="getLoanDetails"
137
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"
138
+ placeholder="Method name"
139
+ />
140
+ <input
141
+ data-testid="input-args"
142
+ id="inputArgs"
143
+ type="text"
144
+ value=""
145
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs w-24"
146
+ placeholder="Args (JSON)"
147
+ />
148
+ <button
149
+ data-testid="btn-invoke"
150
+ id="btnInvoke"
151
+ class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700"
152
+ >
153
+ Invoke
154
+ </button>
155
+ </div>
156
+ <div class="flex gap-1">
157
+ <button
158
+ data-testid="btn-invoke-error"
159
+ id="btnInvokeError"
160
+ class="rounded bg-red-500 px-2 py-1 text-white hover:bg-red-600"
161
+ >
162
+ TC-SO-08: Call throwError()
163
+ </button>
164
+ <button
165
+ data-testid="btn-invoke-async"
166
+ id="btnInvokeAsync"
167
+ class="rounded bg-blue-500 px-2 py-1 text-white hover:bg-blue-600"
168
+ >
169
+ TC-SO-07: Call asyncMethod(200)
170
+ </button>
171
+ </div>
172
+ <div
173
+ data-testid="invoke-result"
174
+ id="invokeResult"
175
+ class="bg-white rounded p-1 min-h-[30px] whitespace-pre-wrap"
176
+ >
177
+
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <!-- Tab: Events -->
183
+ <div id="panel-events" class="tab-panel hidden">
184
+ <div class="space-y-1">
185
+ <div class="flex gap-1 items-center">
186
+ <input
187
+ data-testid="input-event-id"
188
+ id="inputEventId"
189
+ type="text"
190
+ value="Loan.onPreSave"
191
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"
192
+ placeholder="Event ID"
193
+ />
194
+ <button
195
+ data-testid="btn-subscribe"
196
+ id="btnSubscribe"
197
+ class="rounded bg-purple-600 px-2 py-1 text-white hover:bg-purple-700"
198
+ >
199
+ Subscribe
200
+ </button>
201
+ </div>
202
+
203
+ <div class="flex gap-1 items-center">
204
+ <input
205
+ data-testid="input-criteria"
206
+ id="inputCriteria"
207
+ type="text"
208
+ value=""
209
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"
210
+ placeholder='Criteria JSON e.g. {"amount":{"gt":100000}}'
211
+ />
212
+ <button
213
+ data-testid="btn-subscribe-criteria"
214
+ id="btnSubscribeCriteria"
215
+ class="rounded bg-purple-600 px-2 py-1 text-white hover:bg-purple-700"
216
+ >
217
+ Subscribe+Criteria
218
+ </button>
219
+ </div>
220
+
221
+ <div class="flex gap-1 items-center">
222
+ <select
223
+ data-testid="select-unsub-token"
224
+ id="selectUnsubToken"
225
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"
226
+ >
227
+ <option value="">— select token —</option>
228
+ </select>
229
+ <button
230
+ data-testid="btn-unsubscribe"
231
+ id="btnUnsubscribe"
232
+ class="rounded bg-yellow-600 px-2 py-1 text-white hover:bg-yellow-700"
233
+ >
234
+ Unsubscribe
235
+ </button>
236
+ </div>
237
+
238
+ <h4 class="font-medium text-emerald-700 mt-1">Active Subscriptions</h4>
239
+ <div
240
+ data-testid="subscriptions-list"
241
+ id="subscriptionsList"
242
+ class="bg-white rounded p-1 min-h-[20px]"
243
+ >
244
+
245
+ </div>
246
+
247
+ <h4 class="font-medium text-emerald-700 mt-1">Received Events</h4>
248
+ <div
249
+ data-testid="events-received"
250
+ id="eventsReceived"
251
+ class="bg-white rounded p-1 min-h-[40px] max-h-[120px] overflow-y-auto"
252
+ >
253
+
254
+ </div>
255
+
256
+ <h4 class="font-medium text-emerald-700 mt-1">Feedback Return Value</h4>
257
+ <div class="flex gap-1 items-center">
258
+ <select
259
+ data-testid="select-feedback"
260
+ id="selectFeedback"
261
+ class="rounded border-emerald-300 px-2 py-0.5 text-xs flex-1"
262
+ >
263
+ <option value="true">return true (approve)</option>
264
+ <option value="false">return false (reject)</option>
265
+ <option value="object">return {status: "ok"}</option>
266
+ </select>
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <!-- Tab: Lifecycle -->
272
+ <div id="panel-lifecycle" class="tab-panel hidden">
273
+ <div class="space-y-1">
274
+ <button
275
+ data-testid="btn-set-loglevel-0"
276
+ id="btnLogLevel0"
277
+ class="rounded bg-gray-600 px-2 py-1 text-white hover:bg-gray-700"
278
+ >
279
+ setLogLevel(0) — Silent
280
+ </button>
281
+ <button
282
+ data-testid="btn-set-loglevel-10"
283
+ id="btnLogLevel10"
284
+ class="rounded bg-gray-600 px-2 py-1 text-white hover:bg-gray-700"
285
+ >
286
+ setLogLevel(10) — Debug
287
+ </button>
288
+ <button
289
+ data-testid="btn-disconnect"
290
+ id="btnDisconnect"
291
+ class="rounded bg-red-600 px-2 py-1 text-white hover:bg-red-700"
292
+ >
293
+ close() — Disconnect
294
+ </button>
295
+ <p class="text-[10px] text-gray-400">
296
+ Expected: status badge → "disconnected", further calls fail
297
+ </p>
298
+ <button
299
+ data-testid="btn-reconnect"
300
+ id="btnReconnect"
301
+ class="rounded bg-emerald-600 px-2 py-1 text-white hover:bg-emerald-700"
302
+ >
303
+ connect() — Reconnect
304
+ </button>
305
+ <p class="text-[10px] text-gray-400">
306
+ Expected: status badge → "connected", calls work again
307
+ </p>
308
+ </div>
309
+ </div>
310
+
311
+ <!-- Log -->
312
+ <div class="mt-2">
313
+ <h4 class="font-medium text-emerald-700">Guest Log</h4>
314
+ <div
315
+ data-testid="guest-log"
316
+ id="guestLog"
317
+ class="bg-white rounded p-1 text-xs min-h-[40px] max-h-[100px] overflow-y-auto font-mono"
318
+ ></div>
319
+ </div>
320
+
321
+ <script src="./e2e-guest.js" type="module"></script>
322
+ </body>
323
+ </html>
package/dist/cjs/guest.js CHANGED
@@ -44,6 +44,7 @@ var ResponseType = /* @__PURE__ */ ((ResponseType2) => {
44
44
  return ResponseType2;
45
45
  })(ResponseType || {});
46
46
  const KEEP_ALIVE_INTERVAL = 12e4;
47
+ const KEEP_ALIVE_OBJECT_TIMEOUT = 2e3;
47
48
  const capabilities = {
48
49
  eventFeedback: true
49
50
  };
@@ -145,6 +146,7 @@ class SSFGuest {
145
146
  * throttled keep alive function
146
147
  */
147
148
  #throttledKeepAlive = null;
149
+ #auditThrottler;
148
150
  /**
149
151
  * Create new guest
150
152
  * @param {GuestOption} options - options for the guest
@@ -170,6 +172,11 @@ class SSFGuest {
170
172
  team,
171
173
  appName
172
174
  });
175
+ this.#auditThrottler = new import_microfe_common.AuditThrottler({
176
+ logger: this.#logger,
177
+ enabled: options?.auditOperations,
178
+ throttleMs: options?.auditThrottleMs
179
+ });
173
180
  (0, import_pui_diagnostics.webvitals)(this.#logger);
174
181
  (0, import_pui_diagnostics.logUnhandledErrors)(this.#logger);
175
182
  this.#correlationId = (0, import_uuid.v4)();
@@ -202,12 +209,17 @@ class SSFGuest {
202
209
  if (soJSON.functions) {
203
210
  soJSON.functions.forEach((functionName) => {
204
211
  Object.defineProperty(ctrl, functionName, {
205
- value: async (...args) => this.#invoke({
206
- objectId: ctrl.id,
207
- functionName,
208
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
209
- functionParams: [...args]
210
- }),
212
+ value: async (...args) => {
213
+ const fnCallContext = ctrl[functionName]?.callContext;
214
+ const callerChain = fnCallContext?.callChain;
215
+ return this.#invoke({
216
+ objectId: ctrl.id,
217
+ functionName,
218
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
219
+ functionParams: [...args],
220
+ callerChain
221
+ });
222
+ },
211
223
  enumerable: true
212
224
  });
213
225
  });
@@ -235,12 +247,14 @@ class SSFGuest {
235
247
  * @param {string} param0.objectId - The object ID to invoke
236
248
  * @param {string} param0.functionName - The function name to call
237
249
  * @param {Array<any>} param0.functionParams - The parameters for the function
250
+ * @param {Array<{id: string}>} [param0.callerChain] - Chain of original callers when forwarding through intermediaries
238
251
  * @returns {Promise<any>} The result of the function call
239
252
  */
240
253
  #invoke = async ({
241
254
  objectId,
242
255
  functionName,
243
- functionParams
256
+ functionParams,
257
+ callerChain
244
258
  }) => {
245
259
  this.#logger.debug(
246
260
  `Invoking scripting object method ${objectId}.${functionName}()...`
@@ -267,11 +281,12 @@ class SSFGuest {
267
281
  messageBody: {
268
282
  objectId,
269
283
  functionName,
270
- functionParams
284
+ functionParams,
285
+ ...callerChain?.length ? { callerChain } : {}
271
286
  }
272
287
  });
273
288
  const retVal = this.#handleResponse(response);
274
- this.#logger.audit({
289
+ this.#auditThrottler.log(`invoke:${objectId}.${functionName}`, {
275
290
  message: "Guest proxy invoked Scripting Object method",
276
291
  scriptingObject: objectId,
277
292
  scriptingMethod: functionName,
@@ -459,14 +474,14 @@ class SSFGuest {
459
474
  requestId,
460
475
  response: values
461
476
  });
462
- this.#logger.audit({
477
+ this.#auditThrottler.log(`event:${eventId}`, {
463
478
  message: "Guest proxy processed event from host and responded",
464
479
  scriptingEventId: eventId,
465
480
  scriptingObject: object.id,
466
481
  ...this.#getGuestInfo()
467
482
  });
468
483
  } else {
469
- this.#logger.audit({
484
+ this.#auditThrottler.log(`event:${eventId}`, {
470
485
  message: "Guest proxy processed event from host",
471
486
  scriptingEventId: eventId,
472
487
  scriptingObject: object.id,
@@ -530,7 +545,14 @@ class SSFGuest {
530
545
  #startKeepSessionAlive = async () => {
531
546
  if (this.#keepAlive) {
532
547
  try {
533
- const appObj = await this.getObject("application");
548
+ const appObj = await Promise.race([
549
+ this.getObject(
550
+ "application"
551
+ ),
552
+ new Promise((resolve) => {
553
+ setTimeout(() => resolve(null), KEEP_ALIVE_OBJECT_TIMEOUT);
554
+ })
555
+ ]);
534
556
  if (appObj) {
535
557
  this.#throttledKeepAlive = (0, import_throttle.default)(
536
558
  async () => {
@@ -736,7 +758,7 @@ class SSFGuest {
736
758
  }
737
759
  });
738
760
  const obj = this.#fromJSON(response.object);
739
- this.#logger.audit({
761
+ this.#auditThrottler.log(`getObject:${objectId}`, {
740
762
  message: "Received scripting object from host",
741
763
  scriptingObject: objectId,
742
764
  ...this.#getGuestInfo()
@@ -759,7 +781,7 @@ class SSFGuest {
759
781
  messageType: import_microfe_common.MessageType.ListObjects,
760
782
  messageBody: {}
761
783
  });
762
- this.#logger.audit({
784
+ this.#auditThrottler.log("listObjects", {
763
785
  message: "Received names of all scripting objects exposed by host",
764
786
  objects,
765
787
  ...this.#getGuestInfo()
@@ -34,7 +34,9 @@ const createGuest = ({
34
34
  keepAlive,
35
35
  usesDevConnectAPI,
36
36
  keepAliveInterval,
37
- console
37
+ console,
38
+ auditOperations,
39
+ auditThrottleMs
38
40
  }) => new import__.SSFGuest({
39
41
  logger: {
40
42
  console: console ?? true,
@@ -44,7 +46,9 @@ const createGuest = ({
44
46
  },
45
47
  keepAlive,
46
48
  usesDevConnectAPI,
47
- keepAliveInterval
49
+ keepAliveInterval,
50
+ auditOperations,
51
+ auditThrottleMs
48
52
  });
49
53
  const postMessage = ({
50
54
  srcWindow,
@@ -0,0 +1,60 @@
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>Grandchild Guest (C)</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-green-50 p-2">
11
+ <h3 class="text-sm font-semibold text-green-800 mb-2">
12
+ Grandchild Guest (C)
13
+ </h3>
14
+
15
+ <div class="flex flex-wrap gap-2 mb-2">
16
+ <button
17
+ data-testid="btn-get-caller-info"
18
+ id="btnGetCallerInfo"
19
+ disabled
20
+ class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
21
+ >
22
+ Call getCallerInfo()
23
+ </button>
24
+ <button
25
+ data-testid="btn-ping"
26
+ id="btnPing"
27
+ disabled
28
+ class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
29
+ >
30
+ Call ping("hello from C")
31
+ </button>
32
+ <button
33
+ data-testid="btn-get-loan"
34
+ id="btnGetLoan"
35
+ disabled
36
+ class="rounded bg-green-600 px-3 py-1 text-xs text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
37
+ >
38
+ Call getLoanDetails()
39
+ </button>
40
+ </div>
41
+
42
+ <div
43
+ data-testid="grandchild-status"
44
+ id="status"
45
+ class="text-xs bg-green-100 rounded p-2 mb-2 min-h-[20px]"
46
+ >
47
+ Connecting...
48
+ </div>
49
+
50
+ <div
51
+ data-testid="grandchild-result"
52
+ id="result"
53
+ class="text-xs bg-white rounded p-2 min-h-[40px]"
54
+ >
55
+ <pre id="resultPre" class="whitespace-pre-wrap">No results yet</pre>
56
+ </div>
57
+
58
+ <script src="./callchain-grandchild.js" type="module"></script>
59
+ </body>
60
+ </html>