@ada-cx/messaging-bridge 1.0.0-setup.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/README.md +328 -0
- package/dist/bridge-client.d.ts +69 -0
- package/dist/bridge-react.d.ts +63 -0
- package/dist/build-info.d.ts +17 -0
- package/dist/derive.d.ts +115 -0
- package/dist/loader-CVynp73o.js +160 -0
- package/dist/loader-CVynp73o.js.map +1 -0
- package/dist/loader.d.ts +125 -0
- package/dist/npm-index.d.ts +17 -0
- package/dist/npm-index.js +28 -0
- package/dist/npm-index.js.map +1 -0
- package/dist/npm-react.d.ts +13 -0
- package/dist/npm-react.js +113 -0
- package/dist/npm-react.js.map +1 -0
- package/dist/operations.d.ts +323 -0
- package/dist/shared-utils/csat-settings.d.ts +61 -0
- package/dist/shared-utils/file-upload.d.ts +36 -0
- package/dist/shared-utils/index.d.ts +2 -0
- package/dist/state-keys.d.ts +105 -0
- package/dist/testing.d.ts +53 -0
- package/dist/testing.js +471 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +849 -0
- package/package.json +72 -0
package/dist/testing.js
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
var L = 400, x = 5e3, B = class extends Error {
|
|
2
|
+
constructor(a = "The bridge client was destroyed.") {
|
|
3
|
+
super(a), this.name = "BridgeClientDestroyedError";
|
|
4
|
+
}
|
|
5
|
+
static is(a) {
|
|
6
|
+
return a instanceof Error && a.name === "BridgeClientDestroyedError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
function R() {
|
|
10
|
+
const a = typeof crypto > "u" ? void 0 : crypto;
|
|
11
|
+
if (typeof a?.randomUUID == "function") return a.randomUUID();
|
|
12
|
+
const s = new Uint8Array(16);
|
|
13
|
+
if (typeof a?.getRandomValues == "function") a.getRandomValues(s);
|
|
14
|
+
else for (let i = 0; i < s.length; i += 1) s[i] = Math.floor(Math.random() * 256);
|
|
15
|
+
s[6] = s[6] & 15 | 64, s[8] = s[8] & 63 | 128;
|
|
16
|
+
const t = Array.from(s, (i) => i.toString(16).padStart(2, "0"));
|
|
17
|
+
return `${t.slice(0, 4).join("")}-${t.slice(4, 6).join("")}-${t.slice(6, 8).join("")}-${t.slice(8, 10).join("")}-${t.slice(10).join("")}`;
|
|
18
|
+
}
|
|
19
|
+
function M(a, s, t, i) {
|
|
20
|
+
return new Promise((c, l) => {
|
|
21
|
+
if (s.isDisposed()) {
|
|
22
|
+
l(new B());
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
let b = !1, d = null, g = null;
|
|
26
|
+
const E = (o) => {
|
|
27
|
+
y({ error: o });
|
|
28
|
+
}, y = (o) => {
|
|
29
|
+
b || (b = !0, s.unregister(E), d?.(), g !== null && clearTimeout(g), "value" in o ? c(o.value) : l(o.error));
|
|
30
|
+
}, I = () => {
|
|
31
|
+
const o = t(a.getState());
|
|
32
|
+
o && y(o);
|
|
33
|
+
};
|
|
34
|
+
I(), !b && (s.register(E), d = a.subscribe(I), i?.timeoutMs !== void 0 && (g = setTimeout(() => {
|
|
35
|
+
y({ error: /* @__PURE__ */ new Error(`Bridge operation timed out after ${i.timeoutMs}ms.`) });
|
|
36
|
+
}, i.timeoutMs)));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function O(a) {
|
|
40
|
+
function s(i, c, l) {
|
|
41
|
+
const b = l?.equals ?? Object.is;
|
|
42
|
+
let d = i(a.getState());
|
|
43
|
+
return l?.emitInitial === !0 && c(d, void 0), a.subscribe(() => {
|
|
44
|
+
const g = i(a.getState());
|
|
45
|
+
if (b(g, d)) return;
|
|
46
|
+
const E = d;
|
|
47
|
+
d = g, c(g, E);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function t(i, c, l) {
|
|
51
|
+
return s((b) => b?.[i], c, l);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
subscribeKey: t,
|
|
55
|
+
select: s
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function A(a) {
|
|
59
|
+
let s = !1;
|
|
60
|
+
const t = {
|
|
61
|
+
getState: () => a.getState(),
|
|
62
|
+
subscribe: (e) => s ? () => {
|
|
63
|
+
} : a.subscribe(e),
|
|
64
|
+
sendEvent: (e, ...r) => {
|
|
65
|
+
s || a.sendEvent(e, ...r);
|
|
66
|
+
}
|
|
67
|
+
}, i = /* @__PURE__ */ new Set(), c = {
|
|
68
|
+
isDisposed: () => s,
|
|
69
|
+
register: (e) => {
|
|
70
|
+
i.add(e);
|
|
71
|
+
},
|
|
72
|
+
unregister: (e) => {
|
|
73
|
+
i.delete(e);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
let l = "", b = "", d = null, g = null, E = null;
|
|
77
|
+
function y() {
|
|
78
|
+
l = "", b = "", d !== null && (clearTimeout(d), d = null);
|
|
79
|
+
}
|
|
80
|
+
function I() {
|
|
81
|
+
if (E) return;
|
|
82
|
+
let e = t.getState()?.["chat.conversationId"] ?? null;
|
|
83
|
+
E = t.subscribe(() => {
|
|
84
|
+
const r = t.getState()?.["chat.conversationId"] ?? null;
|
|
85
|
+
r !== e && (e = r, y(), g = r);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function o() {
|
|
89
|
+
d === null && (d = setTimeout(() => {
|
|
90
|
+
d = null, l > b && (b = l, t.sendEvent("chat.messages.read", {
|
|
91
|
+
cursor: l,
|
|
92
|
+
...g ? { conversationId: g } : {}
|
|
93
|
+
}));
|
|
94
|
+
}, L));
|
|
95
|
+
}
|
|
96
|
+
let w = 0;
|
|
97
|
+
const D = {
|
|
98
|
+
sendMessage(e, r) {
|
|
99
|
+
const n = typeof e == "string" ? e.trim() : "", S = r?.secret === !0, v = R();
|
|
100
|
+
let h = null, m = null;
|
|
101
|
+
if (n) {
|
|
102
|
+
const u = t.getState();
|
|
103
|
+
h = u?.["chat.error"] ?? null, m = u?.["chat.error.seq"] ?? null, t.sendEvent("chat.message.send", {
|
|
104
|
+
body: n,
|
|
105
|
+
messageType: S ? "secret" : "text",
|
|
106
|
+
tempMessageUuid: v
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
tempMessageUuid: v,
|
|
111
|
+
settled(u) {
|
|
112
|
+
return n ? S ? M(t, c, () => {
|
|
113
|
+
}, u) : M(t, c, (p) => {
|
|
114
|
+
const q = (p?.["chat.messages"] ?? []).find((T) => T.clientKey === v);
|
|
115
|
+
if (q) return { value: q };
|
|
116
|
+
const f = p?.["chat.error"] ?? null;
|
|
117
|
+
if (p !== null && "chat.error.clientKey" in p)
|
|
118
|
+
return f !== null && p["chat.error.clientKey"] === v ? { error: new Error(f) } : void 0;
|
|
119
|
+
const C = p?.["chat.error.seq"];
|
|
120
|
+
if (typeof C == "number") {
|
|
121
|
+
if (m === null) {
|
|
122
|
+
m = C;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
return f !== null && C !== m ? { error: new Error(f) } : void 0;
|
|
126
|
+
}
|
|
127
|
+
if (f === null) {
|
|
128
|
+
h = null;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (f !== h) return { error: new Error(f) };
|
|
132
|
+
}, u) : Promise.reject(/* @__PURE__ */ new Error("sendMessage requires a non-empty body."));
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
retryMessage(e) {
|
|
137
|
+
t.sendEvent("chat.message.retry", { message: {
|
|
138
|
+
id: e,
|
|
139
|
+
type: "text"
|
|
140
|
+
} });
|
|
141
|
+
},
|
|
142
|
+
canRetryMessage(e) {
|
|
143
|
+
const r = t.getState();
|
|
144
|
+
return e.type === "text" && e.isRetryable === !0 && r?.["zendesk.sdk.disconnected"] !== !0 && (e.conversationId == null || e.conversationId === (r?.["chat.conversationId"] ?? null));
|
|
145
|
+
},
|
|
146
|
+
selectQuickReply(e, r) {
|
|
147
|
+
t.sendEvent("chat.quickReply.select", {
|
|
148
|
+
label: e.label,
|
|
149
|
+
target: e.target,
|
|
150
|
+
buttonType: e.buttonType,
|
|
151
|
+
...e.value !== void 0 ? { value: e.value } : {},
|
|
152
|
+
messageId: r
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
selectOption(e, r) {
|
|
156
|
+
t.sendEvent("chat.option.select", {
|
|
157
|
+
optionId: e.id,
|
|
158
|
+
optionLabel: e.label || e.id,
|
|
159
|
+
messageId: r
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
submitListSelection(e) {
|
|
163
|
+
const r = t.getState()?.["conversation.stateData.listSelection"];
|
|
164
|
+
r && t.sendEvent("chat.selectableList.submit", {
|
|
165
|
+
selectedIds: e,
|
|
166
|
+
dataId: r.dataId
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
cancelListSelection(e) {
|
|
170
|
+
const r = t.getState()?.["conversation.stateData.listSelection"];
|
|
171
|
+
r?.cancelCaptureResponseId && t.sendEvent("listSelection.cancel", {
|
|
172
|
+
cancelResponseId: r.cancelCaptureResponseId,
|
|
173
|
+
cancelLabel: e ?? r.cancelCaptureLabel,
|
|
174
|
+
dataId: r.dataId
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
submitCapture(e) {
|
|
178
|
+
const r = t.getState()?.["conversation.stateData.capture"];
|
|
179
|
+
if (!r) return { settled: () => Promise.reject(/* @__PURE__ */ new Error("No capture field is active.")) };
|
|
180
|
+
const n = r.dataId, S = R();
|
|
181
|
+
t.sendEvent("capture.submit", {
|
|
182
|
+
value: e,
|
|
183
|
+
dataId: n,
|
|
184
|
+
submitId: S
|
|
185
|
+
});
|
|
186
|
+
let v = !1;
|
|
187
|
+
return { settled: (h) => M(t, c, (m) => {
|
|
188
|
+
const u = m?.["conversation.stateData.capture"] ?? null;
|
|
189
|
+
if (!u || u.dataId !== n) return { value: { ok: !0 } };
|
|
190
|
+
const p = u.lastSubmitFailure;
|
|
191
|
+
if (p && p.submitId === S) return { value: {
|
|
192
|
+
ok: !1,
|
|
193
|
+
message: p.message
|
|
194
|
+
} };
|
|
195
|
+
if (u.isSubmitting === !0) {
|
|
196
|
+
v = !0;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (v) return u.validationErrorMessage ? { value: {
|
|
200
|
+
ok: !1,
|
|
201
|
+
message: u.validationErrorMessage
|
|
202
|
+
} } : { value: { ok: !0 } };
|
|
203
|
+
}, h) };
|
|
204
|
+
},
|
|
205
|
+
cancelCapture(e) {
|
|
206
|
+
const r = t.getState()?.["conversation.stateData.capture"];
|
|
207
|
+
r?.cancelCaptureResponseId && t.sendEvent("capture.cancel", {
|
|
208
|
+
cancelResponseId: r.cancelCaptureResponseId,
|
|
209
|
+
cancelLabel: e ?? r.cancelCaptureLabel,
|
|
210
|
+
dataId: r.dataId
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
addReaction(e, r) {
|
|
214
|
+
t.sendEvent("chat.message.reaction.add", {
|
|
215
|
+
messageId: e,
|
|
216
|
+
reaction: r
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
reportLinkClick(e, r) {
|
|
220
|
+
t.sendEvent("chat.link.click", {
|
|
221
|
+
messageId: e,
|
|
222
|
+
...r?.navigateInHost !== void 0 ? { navigateInHost: r.navigateInHost } : {}
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
markRead(e, r) {
|
|
226
|
+
if (typeof e != "string" || e === "") return;
|
|
227
|
+
I();
|
|
228
|
+
const n = r?.conversationId ?? t.getState()?.["chat.conversationId"] ?? null;
|
|
229
|
+
n !== g && (y(), g = n), e > l && (l = e, o());
|
|
230
|
+
},
|
|
231
|
+
loadOlderMessages() {
|
|
232
|
+
const e = t.getState();
|
|
233
|
+
e?.["chat.canLoadMore"] !== !0 || e["chat.loadingPreviousMessages"] === !0 || t.sendEvent("chat.logs.loadMore");
|
|
234
|
+
},
|
|
235
|
+
onHistoryLoaded(e) {
|
|
236
|
+
let r = t.getState()?.["chat.recentMessagesLoadSeq"] ?? 0;
|
|
237
|
+
return t.subscribe(() => {
|
|
238
|
+
const n = t.getState()?.["chat.recentMessagesLoadSeq"] ?? 0;
|
|
239
|
+
n > r ? (r = n, e(n)) : n < r && (r = n);
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
submitCsat(e, r) {
|
|
243
|
+
const { surveyType: n, partial: S, conversationId: v } = r, h = t.getState()?.["csat.submitSuccess.seq"] ?? 0;
|
|
244
|
+
let m = t.getState()?.["csat.submitError"] ?? null, u = t.getState()?.["csat.submitError.seq"] ?? null;
|
|
245
|
+
t.sendEvent("csat.submit", {
|
|
246
|
+
data: e,
|
|
247
|
+
surveyType: n,
|
|
248
|
+
...S !== void 0 ? { partial: S } : {},
|
|
249
|
+
...v != null ? { conversationId: v } : {}
|
|
250
|
+
});
|
|
251
|
+
const p = (q) => v == null || (q ?? null) === v;
|
|
252
|
+
return { settled: (q) => M(t, c, (f) => {
|
|
253
|
+
if ((f?.["csat.submitSuccess.seq"] ?? 0) > h && f?.["csat.submitSuccess.surveyType"] === n && p(f?.["csat.submitSuccess.conversationId"])) return { value: { ok: !0 } };
|
|
254
|
+
const C = f?.["csat.submitError"] ?? null, T = f?.["csat.submitError.seq"];
|
|
255
|
+
if (typeof T == "number") {
|
|
256
|
+
if (u === null) {
|
|
257
|
+
u = T;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
return C !== null && T !== u && f?.["csat.submitErrorSurveyType"] === n && p(f?.["csat.submitErrorConversationId"]) ? { value: {
|
|
261
|
+
ok: !1,
|
|
262
|
+
message: C
|
|
263
|
+
} } : void 0;
|
|
264
|
+
}
|
|
265
|
+
if (C === null) {
|
|
266
|
+
m = null;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (C !== m && f?.["csat.submitErrorSurveyType"] === n && p(f?.["csat.submitErrorConversationId"])) return { value: {
|
|
270
|
+
ok: !1,
|
|
271
|
+
message: C
|
|
272
|
+
} };
|
|
273
|
+
}, q) };
|
|
274
|
+
},
|
|
275
|
+
trackCsatShown(e, r) {
|
|
276
|
+
t.sendEvent("csat.shown", {
|
|
277
|
+
surveyType: e,
|
|
278
|
+
...r != null ? { conversationId: r } : {}
|
|
279
|
+
});
|
|
280
|
+
},
|
|
281
|
+
checkEndChatEligibility(e) {
|
|
282
|
+
const r = t.getState()?.["csat.endChatEligibility.seq"] ?? 0;
|
|
283
|
+
return t.sendEvent("csat.checkEndChatEligibility"), M(t, c, (n) => {
|
|
284
|
+
if (!((n?.["csat.endChatEligibility.seq"] ?? 0) <= r))
|
|
285
|
+
return { value: {
|
|
286
|
+
eligible: n?.["csat.endChatEligible"] ?? null,
|
|
287
|
+
surveyTarget: n?.["csat.endChatSurveyTarget"] ?? null
|
|
288
|
+
} };
|
|
289
|
+
}, e);
|
|
290
|
+
},
|
|
291
|
+
skipCsatAndEndChat() {
|
|
292
|
+
t.sendEvent("endChat.skipCsat");
|
|
293
|
+
},
|
|
294
|
+
startNewConversation(e) {
|
|
295
|
+
const r = Date.now();
|
|
296
|
+
return r < w ? !1 : (w = r + (e?.cooldownMs ?? x), t.sendEvent("app.startNewConversation"), !0);
|
|
297
|
+
},
|
|
298
|
+
resetApp() {
|
|
299
|
+
t.sendEvent("app.reset");
|
|
300
|
+
},
|
|
301
|
+
notifyComposerChanged(e) {
|
|
302
|
+
e?.secret === !0 || t.getState()?.["ui.secretMessage.isOpen"] === !0 || t.sendEvent("ui.composer.textChanged");
|
|
303
|
+
},
|
|
304
|
+
toggleSecretMessage(e) {
|
|
305
|
+
t.sendEvent("ui.secretMessage.toggle", { open: e });
|
|
306
|
+
},
|
|
307
|
+
startFileUpload(e) {
|
|
308
|
+
const r = R();
|
|
309
|
+
return t.sendEvent("file.upload.start", {
|
|
310
|
+
file: e,
|
|
311
|
+
uploadId: r
|
|
312
|
+
}), {
|
|
313
|
+
uploadId: r,
|
|
314
|
+
retry() {
|
|
315
|
+
t.sendEvent("file.upload.retry", {
|
|
316
|
+
file: e,
|
|
317
|
+
uploadId: r
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
dismissError() {
|
|
321
|
+
t.sendEvent("file.upload.errorDismiss");
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
setLanguage(e) {
|
|
326
|
+
t.sendEvent("settings.language.change", { language: e });
|
|
327
|
+
},
|
|
328
|
+
emailTranscript(e, r) {
|
|
329
|
+
const n = typeof e == "string" ? e.trim() : "";
|
|
330
|
+
return n ? (t.sendEvent("settings.transcript.email.send", { email: n }), k("transcript.email.status", r)) : Promise.reject(/* @__PURE__ */ new Error("emailTranscript requires a non-empty email."));
|
|
331
|
+
},
|
|
332
|
+
downloadTranscript(e) {
|
|
333
|
+
return t.sendEvent("settings.transcript.download.request"), k("transcript.download.status", e);
|
|
334
|
+
},
|
|
335
|
+
setAlertSound(e) {
|
|
336
|
+
t.sendEvent("settings.alertSound.toggle", { enabled: e });
|
|
337
|
+
},
|
|
338
|
+
requestNotificationPermission() {
|
|
339
|
+
t.sendEvent("settings.notifications.requestPermission");
|
|
340
|
+
},
|
|
341
|
+
setTheme(e) {
|
|
342
|
+
t.sendEvent("appearance.setTheme", { theme: e });
|
|
343
|
+
},
|
|
344
|
+
setTextSize(e) {
|
|
345
|
+
t.sendEvent("appearance.setTextSize", { textSize: e });
|
|
346
|
+
},
|
|
347
|
+
close() {
|
|
348
|
+
t.sendEvent("ui.closeButton.click");
|
|
349
|
+
},
|
|
350
|
+
minimize() {
|
|
351
|
+
t.sendEvent("ui.minimizeButton.click");
|
|
352
|
+
},
|
|
353
|
+
dismissToast() {
|
|
354
|
+
t.sendEvent("ui.toast.dismiss");
|
|
355
|
+
},
|
|
356
|
+
dismissError() {
|
|
357
|
+
t.sendEvent("chat.error.dismiss");
|
|
358
|
+
},
|
|
359
|
+
setHostViewportExpanded(e) {
|
|
360
|
+
t.sendEvent("ui.hostViewport.set", { expanded: e });
|
|
361
|
+
},
|
|
362
|
+
retryAfterOutage() {
|
|
363
|
+
t.sendEvent("outage.retry");
|
|
364
|
+
},
|
|
365
|
+
reportAppError(e, r) {
|
|
366
|
+
const n = e instanceof Error ? e : null;
|
|
367
|
+
t.sendEvent("app.error.report", {
|
|
368
|
+
message: n?.message ?? String(e),
|
|
369
|
+
stack: n?.stack ?? "",
|
|
370
|
+
componentStack: r ?? ""
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
function k(e, r) {
|
|
375
|
+
const n = `${e}.seq`;
|
|
376
|
+
let S = t.getState()?.[e] ?? "idle", v = t.getState()?.[n] ?? null;
|
|
377
|
+
return M(t, c, (h) => {
|
|
378
|
+
const m = h?.[e] ?? "idle", u = h?.[n];
|
|
379
|
+
if (typeof u == "number") {
|
|
380
|
+
if (v === null) {
|
|
381
|
+
v = u;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
return u === v ? void 0 : m === "success" ? { value: void 0 } : m === "error" ? { error: /* @__PURE__ */ new Error(`${e} reported an error.`) } : void 0;
|
|
385
|
+
}
|
|
386
|
+
const p = S;
|
|
387
|
+
if (S = m, p === "pending") {
|
|
388
|
+
if (m === "success") return { value: void 0 };
|
|
389
|
+
if (m === "error") return { error: /* @__PURE__ */ new Error(`${e} reported an error.`) };
|
|
390
|
+
}
|
|
391
|
+
}, r);
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
operations: D,
|
|
395
|
+
dispose() {
|
|
396
|
+
if (!s) {
|
|
397
|
+
s = !0, y(), E?.(), E = null;
|
|
398
|
+
for (const e of [...i]) e(new B());
|
|
399
|
+
i.clear();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function U(a = null) {
|
|
405
|
+
let s = a, t = !1;
|
|
406
|
+
const i = /* @__PURE__ */ new Set(), c = [];
|
|
407
|
+
function l() {
|
|
408
|
+
for (const o of [...i]) try {
|
|
409
|
+
o();
|
|
410
|
+
} catch (w) {
|
|
411
|
+
console.error("[AdaMessaging] Bridge subscriber threw:", w);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function b() {
|
|
415
|
+
return s;
|
|
416
|
+
}
|
|
417
|
+
function d(o) {
|
|
418
|
+
return i.add(o), () => {
|
|
419
|
+
i.delete(o);
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function g(o, ...w) {
|
|
423
|
+
t || c.push({
|
|
424
|
+
event: o,
|
|
425
|
+
payload: w[0]
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const E = {
|
|
429
|
+
getState: b,
|
|
430
|
+
subscribe: d,
|
|
431
|
+
sendEvent: g
|
|
432
|
+
}, y = O(E), I = A(E);
|
|
433
|
+
return {
|
|
434
|
+
getState: b,
|
|
435
|
+
subscribe: d,
|
|
436
|
+
sendEvent: g,
|
|
437
|
+
subscribeKey: y.subscribeKey,
|
|
438
|
+
select: y.select,
|
|
439
|
+
operations: I.operations,
|
|
440
|
+
destroy() {
|
|
441
|
+
t = !0, I.dispose(), s = null, i.clear();
|
|
442
|
+
},
|
|
443
|
+
setState(o) {
|
|
444
|
+
s = o, l();
|
|
445
|
+
},
|
|
446
|
+
updateState(o) {
|
|
447
|
+
s = {
|
|
448
|
+
"chat.error.clientKey": null,
|
|
449
|
+
...s ?? {},
|
|
450
|
+
...o
|
|
451
|
+
}, l();
|
|
452
|
+
},
|
|
453
|
+
get sentEvents() {
|
|
454
|
+
return [...c];
|
|
455
|
+
},
|
|
456
|
+
clearSentEvents() {
|
|
457
|
+
c.length = 0;
|
|
458
|
+
},
|
|
459
|
+
get subscriberCount() {
|
|
460
|
+
return i.size;
|
|
461
|
+
},
|
|
462
|
+
get destroyed() {
|
|
463
|
+
return t;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
export {
|
|
468
|
+
U as createMockBridgeClient
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../src/operations.ts","../src/testing.ts"],"sourcesContent":["import type {\n\tAppDisplayState,\n\tAppEvents,\n\tCsatSubmitData,\n\tMessage,\n\tOptionItem,\n\tQuickReply,\n} from \"./types\";\n\n/**\n * `client.operations` — typed helpers over the raw event/state contract,\n * mined from Ada's reference app so custom apps inherit its validated\n * behavior (debounces, guards, correlation) instead of reimplementing it.\n *\n * Implementation note: this module lives in the CDN runtime only. The npm\n * test mock reuses it over its own in-memory host so mocked handles resolve\n * from scripted state, but the published tarball never carries transport\n * logic — every helper here speaks through `sendEvent`/`subscribe` alone.\n */\n\n/** Debounce window for {@link BridgeOperations.markRead} reports. */\nconst READ_REPORT_DEBOUNCE_MS = 400;\n/** Default {@link BridgeOperations.startNewConversation} cooldown. */\nconst START_NEW_CONVERSATION_COOLDOWN_MS = 5_000;\n\n/**\n * The minimal client surface the operations layer builds on. Satisfied by the\n * real `BridgeClient` and by the npm test mock.\n */\nexport interface BridgeOperationsHost {\n\tgetState(): AppDisplayState | null;\n\tsubscribe(callback: () => void): () => void;\n\tsendEvent<K extends keyof AppEvents>(\n\t\tevent: K,\n\t\t...args: AppEvents[K] extends undefined ? [] : [payload: AppEvents[K]]\n\t): void;\n}\n\n/** Outcome of a correlated submit: accepted, or rejected with a message. */\nexport type OperationResult = { ok: true } | { ok: false; message: string };\n\nexport interface SettleOptions {\n\t/** Reject with an `Error` if the operation has not settled in time. */\n\ttimeoutMs?: number;\n}\n\n/**\n * Rejection every in-flight `settled()` promise receives when the client is\n * destroyed, so teardown is the layer's bound of last resort even without a\n * `timeoutMs`. The class identity does not survive the npm/CDN module split,\n * so detect it by `error.name === \"BridgeClientDestroyedError\"` (or via\n * {@link BridgeClientDestroyedError.is}), never by `instanceof` alone.\n */\nexport class BridgeClientDestroyedError extends Error {\n\tconstructor(message = \"The bridge client was destroyed.\") {\n\t\tsuper(message);\n\t\tthis.name = \"BridgeClientDestroyedError\";\n\t}\n\n\tstatic is(error: unknown): error is BridgeClientDestroyedError {\n\t\treturn (\n\t\t\terror instanceof Error && error.name === \"BridgeClientDestroyedError\"\n\t\t);\n\t}\n}\n\nexport interface SendHandle {\n\t/**\n\t * The uuid put on the wire as `tempMessageUuid`. Core seeds the optimistic\n\t * message's `clientKey` from it, so the sent message is findable in\n\t * `chat.messages` by `clientKey === tempMessageUuid`.\n\t */\n\treadonly tempMessageUuid: string;\n\t/**\n\t * Resolves with the message once it appears in `chat.messages` (the\n\t * optimistic row; the durable server echo keeps the same `clientKey`).\n\t * Rejects when core reports THIS send's error before the row appears —\n\t * rate limit, over-length, session not ready, dispatch failure —\n\t * correlated exactly on `chat.error.clientKey` matching the minted\n\t * `tempMessageUuid`, so an unrelated failure in the same window never\n\t * falsely rejects a delivered send. On a core document that predates\n\t * `chat.error.clientKey`, falls back to the positional\n\t * `chat.error.seq`-advance detection, and before that to `chat.error`\n\t * text transitions. A secret send NEVER resolves — core deliberately\n\t * writes no transcript row for it — so pass `timeoutMs` or skip\n\t * `settled()` for secret sends. Destroying the client rejects every\n\t * pending `settled()` with a {@link BridgeClientDestroyedError}.\n\t */\n\tsettled(opts?: SettleOptions): Promise<Message>;\n}\n\nexport interface CaptureHandle {\n\t/**\n\t * Resolves on the verdict for THIS capture submit: `{ ok: true }` when the\n\t * value was accepted (core removes the capture block), or\n\t * `{ ok: false, message }` when it was rejected — a server validation\n\t * rejection (observed on the `isSubmitting` falling edge, with the\n\t * validation error text), or a failure/drop core stamps on the capture's\n\t * `lastSubmitFailure` keyed to this submit's own minted `submitId` (no\n\t * session to round-trip, value over the length cap, transport failure, a\n\t * duplicate submit while one is in flight, a host-driven reset). The\n\t * correlation is exact: the handle never reads the global `chat.error`,\n\t * so an unrelated failure in the same window can never masquerade as this\n\t * capture's verdict. On a core document that predates `lastSubmitFailure`,\n\t * those failures are not capture-attributable at all and the handle stays\n\t * pending — pass `timeoutMs` when targeting older cores.\n\t */\n\tsettled(opts?: SettleOptions): Promise<OperationResult>;\n}\n\nexport interface CsatSubmitHandle {\n\t/**\n\t * Resolves when core reports this survey's outcome, correlated on the\n\t * success seq advance x surveyType x conversationId triple (and the\n\t * mirror-image error triple). A `partial` submit reports no terminal\n\t * outcome, so `settled()` on one only settles via `timeoutMs`.\n\t */\n\tsettled(opts?: SettleOptions): Promise<OperationResult>;\n}\n\nexport interface FileUploadHandle {\n\t/** The minted upload id sent with `file.upload.start`. */\n\treadonly uploadId: string;\n\t/**\n\t * Re-send the EXACT same `{ file, uploadId }` pair. Core correlates the\n\t * retry to the failed attempt by that pair, so a fresh pair would orphan\n\t * the original error state.\n\t */\n\tretry(): void;\n\t/** Dismiss a reported upload error (`file.uploadError`). */\n\tdismissError(): void;\n}\n\nexport interface EndChatEligibilityResult {\n\t/** `csat.endChatEligible` at the moment the answer arrived. */\n\teligible: boolean | null;\n\t/** Which survey End Chat should present, when eligible. */\n\tsurveyTarget: \"bot\" | \"agent\" | null;\n}\n\nexport interface ObserveOptions<T> {\n\t/** Change detector; defaults to `Object.is`. */\n\tequals?: (a: T, b: T) => boolean;\n\t/** Also invoke the callback immediately with the current value. */\n\temitInitial?: boolean;\n}\n\n/**\n * Typed helpers over the bridge event/state contract. Obtain via\n * `client.operations`. Fire-and-forget helpers return `void`; correlated\n * helpers return a handle or Promise that settles from state transitions\n * (there is no request/response channel — core answers through state).\n */\nexport interface BridgeOperations {\n\t/**\n\t * Send a chat message. The body is trimmed; an empty result sends nothing\n\t * (the returned handle then rejects). `secret: true` sends a masked\n\t * message: the raw body never enters the transcript and the handle never\n\t * resolves. While `ui.secretMessage.isOpen` is true you likely want\n\t * `secret: true` to match the composer's mode.\n\t */\n\tsendMessage(body: string, opts?: { secret?: boolean }): SendHandle;\n\t/** Retry a failed send by message id (see {@link canRetryMessage}). */\n\tretryMessage(messageId: string): void;\n\t/**\n\t * Whether the retry affordance should show for a message: a `text`\n\t * message core marked `isRetryable`, no disconnected Zendesk SDK, and the\n\t * message belongs to the current conversation (or predates stamping).\n\t */\n\tcanRetryMessage(message: Message): boolean;\n\n\t/** Answer a quick-reply chip. Pass the chip and its message id verbatim. */\n\tselectQuickReply(reply: QuickReply, messageId: string): void;\n\t/** Answer an options block. The label falls back to the option id. */\n\tselectOption(option: OptionItem, messageId: string): void;\n\t/**\n\t * Submit the active selectable list (`conversation.stateData.listSelection`\n\t * supplies the `dataId`). No-op when no list selection is active.\n\t */\n\tsubmitListSelection(selectedIds: string[]): void;\n\t/**\n\t * Cancel the active selectable list. Uses the block's own cancel response\n\t * and label (`label` overrides the visible label). No-op when no list\n\t * selection is active or it has no cancel response.\n\t */\n\tcancelListSelection(label?: string): void;\n\t/**\n\t * Submit the active capture field (`conversation.stateData.capture`\n\t * supplies the `dataId`). Each call mints its own `submitId`, so the\n\t * handle settles only on ITS OWN submit's verdict or failure — a second\n\t * submit sent while one is in flight is dropped by core and its handle\n\t * resolves `{ ok: false }` on its own terms rather than inheriting the\n\t * in-flight submit's verdict.\n\t */\n\tsubmitCapture(value: string): CaptureHandle;\n\t/**\n\t * Cancel the active capture field. `label` overrides the block's own\n\t * cancel label. No-op when no capture is active or it has no cancel\n\t * response.\n\t */\n\tcancelCapture(label?: string): void;\n\n\t/** React to a bot message with a thumbs vote. */\n\taddReaction(messageId: string, reaction: 1 | -1): void;\n\t/**\n\t * Report that the user activated a `link` message. Core resolves the\n\t * stored message for tracking and, with `navigateInHost`, validated\n\t * same-tab host navigation.\n\t */\n\treportLinkClick(messageId: string, opts?: { navigateInHost?: boolean }): void;\n\n\t/**\n\t * Advance the persisted read watermark. Reports are debounced 400 ms, the\n\t * watermark is a monotonic lexical max over `_id` cursor strings (calling\n\t * with an older cursor never lowers it), and the accumulator resets when\n\t * `chat.conversationId` changes so a pending cursor from conversation A\n\t * can never commit against B. Call with `message.cursor` for each message\n\t * that scrolls into view; messages stamped with a DIFFERENT\n\t * `conversationId` must be excluded by the caller (the transcript\n\t * preserves the previous conversation's rows).\n\t */\n\tmarkRead(cursor: string, opts?: { conversationId?: string }): void;\n\n\t/**\n\t * Request an older page of history. No-op unless `chat.canLoadMore` is\n\t * true and no load is already in flight.\n\t */\n\tloadOlderMessages(): void;\n\t/**\n\t * Invoke `callback` each time restored/baseline history (re)loads\n\t * (`chat.recentMessagesLoadSeq` advances) — the one-shot trigger for\n\t * \"scroll to the bottom of restored history\" and for seeding an unread\n\t * divider. Returns an unsubscribe function.\n\t */\n\tonHistoryLoaded(callback: (seq: number) => void): () => void;\n\n\t/**\n\t * Submit a CSAT survey. Pass `conversationId: message.conversationId` for\n\t * a survey rendered IN the transcript (the row can outlive its\n\t * conversation); omit it — or pass `null`, which is treated as omitted on\n\t * the wire and in correlation — for End Chat / proactive flows, where\n\t * core resolves the conversation itself.\n\t */\n\tsubmitCsat(\n\t\tdata: CsatSubmitData,\n\t\topts: {\n\t\t\tsurveyType: string;\n\t\t\tpartial?: boolean;\n\t\t\tconversationId?: string | null;\n\t\t},\n\t): CsatSubmitHandle;\n\t/** Analytics: report that a survey was displayed. Fire-and-forget. */\n\ttrackCsatShown(surveyType: string, conversationId?: string | null): void;\n\n\t/**\n\t * Ask core whether End Chat should present a survey. Correlated by\n\t * latching `csat.endChatEligibility.seq` and resolving when it advances\n\t * (there is no request id; a `ready -> ready` re-check changes no other\n\t * observable state).\n\t */\n\tcheckEndChatEligibility(\n\t\topts?: SettleOptions,\n\t): Promise<EndChatEligibilityResult>;\n\t/** End the chat without presenting a survey. */\n\tskipCsatAndEndChat(): void;\n\t/**\n\t * Start a new conversation. Applies a local cooldown (default 5 s) that\n\t * swallows rapid re-clicks; returns `false` while cooling down.\n\t */\n\tstartNewConversation(opts?: { cooldownMs?: number }): boolean;\n\t/** Reset the app frame's conversation state. */\n\tresetApp(): void;\n\n\t/**\n\t * Report a content-free composer-changed edge (drives live-agent typing\n\t * indicators). The event is payload-free by contract — composer text must\n\t * never cross the frame boundary — and this helper suppresses it entirely\n\t * in secret mode (pass `secret: true`, or leave it to the\n\t * `ui.secretMessage.isOpen` state check): even a content-free typing edge\n\t * is a signal about the secret being typed.\n\t */\n\tnotifyComposerChanged(opts?: { secret?: boolean }): void;\n\t/** Open or close the masked (secret) composer mode. */\n\ttoggleSecretMessage(open: boolean): void;\n\n\t/**\n\t * Start a live-agent file upload. Mints and retains the `{ file,\n\t * uploadId }` pair so `retry()` replays exactly it. Progress and errors\n\t * land on `file.isUploading`, `file.transfer`, and `file.uploadError`\n\t * (core can report an error without ever setting `isUploading`).\n\t */\n\tstartFileUpload(file: File): FileUploadHandle;\n\n\t/** Change the conversation language (BCP 47 tag the bot offers). */\n\tsetLanguage(language: string): void;\n\t/**\n\t * Email the transcript. Resolves/rejects when `transcript.email.status`\n\t * reaches `success | error` with its `.seq` advanced past this call's\n\t * baseline — a request core drops without ever broadcasting `pending`\n\t * (a host-driven reset in progress) still rejects. On a core document\n\t * that predates the seq, falls back to the `pending -> terminal` edge.\n\t * The email is trimmed; an empty result rejects immediately without\n\t * sending (core drops an empty email silently, so nothing would ever\n\t * settle it).\n\t */\n\temailTranscript(email: string, opts?: SettleOptions): Promise<void>;\n\t/**\n\t * Download the transcript. Same settle contract as\n\t * {@link BridgeOperations.emailTranscript}, on\n\t * `transcript.download.status`.\n\t */\n\tdownloadTranscript(opts?: SettleOptions): Promise<void>;\n\t/** Toggle the new-message alert sound. */\n\tsetAlertSound(enabled: boolean): void;\n\t/**\n\t * Ask the HOST page to request Web Notification permission (permission is\n\t * per-origin, so the widget frame's own value is not the governing one).\n\t * Result lands on `chatter.notificationPermission`.\n\t */\n\trequestNotificationPermission(): void;\n\t/** Set the user's in-widget theme override. */\n\tsetTheme(theme: \"light\" | \"dark\" | \"auto\"): void;\n\t/** Set the user's in-widget text-size override. */\n\tsetTextSize(size: \"small\" | \"default\" | \"large\"): void;\n\n\t/** The header close affordance (ends the chat when `endChat.canEnd`). */\n\tclose(): void;\n\t/** Minimize the widget. */\n\tminimize(): void;\n\t/** Dismiss the visible toast. */\n\tdismissToast(): void;\n\t/**\n\t * Clear `chat.error` in core. Clear core state rather than hiding the\n\t * error locally, or the session's second error renders nothing.\n\t */\n\tdismissError(): void;\n\t/** Ask the SDK host to expand or restore the app's viewport surface. */\n\tsetHostViewportExpanded(expanded: boolean): void;\n\t/** Retry after a response outage banner. */\n\tretryAfterOutage(): void;\n\t/**\n\t * Report a fatal app error to core (renders the fallback UI and reports\n\t * telemetry). Callable from anywhere with a client reference — including\n\t * a class error boundary outside any React provider.\n\t */\n\treportAppError(error: unknown, componentStack?: string): void;\n}\n\nfunction mintUuid(): string {\n\tconst cryptoObj = typeof crypto === \"undefined\" ? undefined : crypto;\n\tif (typeof cryptoObj?.randomUUID === \"function\") {\n\t\treturn cryptoObj.randomUUID();\n\t}\n\tconst bytes = new Uint8Array(16);\n\tif (typeof cryptoObj?.getRandomValues === \"function\") {\n\t\tcryptoObj.getRandomValues(bytes);\n\t} else {\n\t\tfor (let index = 0; index < bytes.length; index += 1) {\n\t\t\tbytes[index] = Math.floor(Math.random() * 256);\n\t\t}\n\t}\n\t// RFC 4122 version/variant bits so core's uuid-shape validation accepts it.\n\tbytes[6] = ((bytes[6] as number) & 0x0f) | 0x40;\n\tbytes[8] = ((bytes[8] as number) & 0x3f) | 0x80;\n\tconst hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n\treturn `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n\t\t.slice(6, 8)\n\t\t.join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n\ntype Outcome<T> = { value: T } | { error: Error };\n\n/**\n * The disposal hooks a settle promise registers with, so `dispose()` can\n * reject every in-flight `settled()` with a typed\n * {@link BridgeClientDestroyedError} — the layer's bound of last resort when\n * no `timeoutMs` was passed.\n */\ninterface SettleLifecycle {\n\tisDisposed(): boolean;\n\tregister(abort: (error: Error) => void): void;\n\tunregister(abort: (error: Error) => void): void;\n}\n\n/**\n * Settle a promise from state transitions: `inspect` runs once immediately\n * and then on every state update until it returns an outcome. `timeoutMs`\n * bounds the wait; without it an operation whose answer never arrives keeps\n * its subscription until it settles or the client is destroyed, which\n * rejects it with a {@link BridgeClientDestroyedError}.\n */\nfunction settleFromState<T>(\n\thost: BridgeOperationsHost,\n\tlifecycle: SettleLifecycle,\n\tinspect: (state: AppDisplayState | null) => Outcome<T> | undefined,\n\topts?: SettleOptions,\n): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tif (lifecycle.isDisposed()) {\n\t\t\treject(new BridgeClientDestroyedError());\n\t\t\treturn;\n\t\t}\n\t\tlet done = false;\n\t\tlet unsubscribe: (() => void) | null = null;\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null;\n\t\tconst abort = (error: Error): void => {\n\t\t\tfinish({ error });\n\t\t};\n\t\tconst finish = (outcome: Outcome<T>): void => {\n\t\t\tif (done) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdone = true;\n\t\t\tlifecycle.unregister(abort);\n\t\t\tunsubscribe?.();\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\tif (\"value\" in outcome) {\n\t\t\t\tresolve(outcome.value);\n\t\t\t} else {\n\t\t\t\treject(outcome.error);\n\t\t\t}\n\t\t};\n\t\tconst run = (): void => {\n\t\t\tconst outcome = inspect(host.getState());\n\t\t\tif (outcome) {\n\t\t\t\tfinish(outcome);\n\t\t\t}\n\t\t};\n\t\trun();\n\t\tif (done) {\n\t\t\treturn;\n\t\t}\n\t\tlifecycle.register(abort);\n\t\tunsubscribe = host.subscribe(run);\n\t\tif (opts?.timeoutMs !== undefined) {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\tfinish({\n\t\t\t\t\terror: new Error(\n\t\t\t\t\t\t`Bridge operation timed out after ${opts.timeoutMs}ms.`,\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}, opts.timeoutMs);\n\t\t}\n\t});\n}\n\ninterface StateObservation {\n\tsubscribeKey<K extends keyof AppDisplayState>(\n\t\tkey: K,\n\t\tcallback: (\n\t\t\tvalue: AppDisplayState[K] | undefined,\n\t\t\tprevious: AppDisplayState[K] | undefined,\n\t\t) => void,\n\t\topts?: ObserveOptions<AppDisplayState[K] | undefined>,\n\t): () => void;\n\tselect<T>(\n\t\tselector: (state: AppDisplayState | null) => T,\n\t\tcallback: (value: T, previous: T | undefined) => void,\n\t\topts?: ObserveOptions<T>,\n\t): () => void;\n}\n\n/**\n * Build the `subscribeKey`/`select` observation primitives over a host.\n * `Object.is` is a sound default equality for `subscribeKey`: state crosses\n * the frame boundary by structured clone, so the real client restores each\n * structurally unchanged key's previous reference before notifying (the test\n * mock shares references in memory to begin with). For `select`, whose\n * selector typically MINTS a new object per call, pass a custom `equals`\n * (or select a primitive/signature string) when projecting objects.\n */\nexport function createStateObservation(\n\thost: BridgeOperationsHost,\n): StateObservation {\n\tfunction select<T>(\n\t\tselector: (state: AppDisplayState | null) => T,\n\t\tcallback: (value: T, previous: T | undefined) => void,\n\t\topts?: ObserveOptions<T>,\n\t): () => void {\n\t\tconst equals = opts?.equals ?? Object.is;\n\t\tlet previous = selector(host.getState());\n\t\tif (opts?.emitInitial === true) {\n\t\t\tcallback(previous, undefined);\n\t\t}\n\t\treturn host.subscribe(() => {\n\t\t\tconst next = selector(host.getState());\n\t\t\tif (equals(next, previous)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst before = previous;\n\t\t\tprevious = next;\n\t\t\tcallback(next, before);\n\t\t});\n\t}\n\n\tfunction subscribeKey<K extends keyof AppDisplayState>(\n\t\tkey: K,\n\t\tcallback: (\n\t\t\tvalue: AppDisplayState[K] | undefined,\n\t\t\tprevious: AppDisplayState[K] | undefined,\n\t\t) => void,\n\t\topts?: ObserveOptions<AppDisplayState[K] | undefined>,\n\t): () => void {\n\t\treturn select((state) => state?.[key], callback, opts);\n\t}\n\n\treturn { subscribeKey, select };\n}\n\nexport interface BridgeOperationsBundle {\n\toperations: BridgeOperations;\n\t/** Release the layer's own subscriptions and timers (client teardown). */\n\tdispose(): void;\n}\n\nexport function createBridgeOperations(\n\trawHost: BridgeOperationsHost,\n): BridgeOperationsBundle {\n\tlet disposed = false;\n\n\t// Disposal must actually stop the layer: after `dispose()` no helper may\n\t// send another event or take a new subscription (the client's transport\n\t// is gone). Guarding the host once covers every helper below.\n\tconst host: BridgeOperationsHost = {\n\t\tgetState: () => rawHost.getState(),\n\t\tsubscribe: (callback) =>\n\t\t\tdisposed\n\t\t\t\t? () => {\n\t\t\t\t\t\t// nothing was subscribed\n\t\t\t\t\t}\n\t\t\t\t: rawHost.subscribe(callback),\n\t\tsendEvent: (event, ...args) => {\n\t\t\tif (!disposed) {\n\t\t\t\trawHost.sendEvent(event, ...args);\n\t\t\t}\n\t\t},\n\t};\n\n\tconst liveSettleAborts = new Set<(error: Error) => void>();\n\tconst lifecycle: SettleLifecycle = {\n\t\tisDisposed: () => disposed,\n\t\tregister: (abort) => {\n\t\t\tliveSettleAborts.add(abort);\n\t\t},\n\t\tunregister: (abort) => {\n\t\t\tliveSettleAborts.delete(abort);\n\t\t},\n\t};\n\n\t// markRead accumulator (mirrors the reference app's useReadTracking).\n\tlet readMax = \"\";\n\tlet readReported = \"\";\n\tlet readTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet readConversationId: string | null = null;\n\tlet readResetUnsubscribe: (() => void) | null = null;\n\n\tfunction resetReadAccumulator(): void {\n\t\treadMax = \"\";\n\t\treadReported = \"\";\n\t\tif (readTimer !== null) {\n\t\t\tclearTimeout(readTimer);\n\t\t\treadTimer = null;\n\t\t}\n\t}\n\n\t// Cursor spaces are per conversation: when the pin changes, drop the\n\t// accumulator AND any pending debounced report so a cursor collected in\n\t// conversation A can never be committed against B.\n\tfunction ensureReadResetSubscription(): void {\n\t\tif (readResetUnsubscribe) {\n\t\t\treturn;\n\t\t}\n\t\tlet previousPin = host.getState()?.[\"chat.conversationId\"] ?? null;\n\t\treadResetUnsubscribe = host.subscribe(() => {\n\t\t\tconst pin = host.getState()?.[\"chat.conversationId\"] ?? null;\n\t\t\tif (pin !== previousPin) {\n\t\t\t\tpreviousPin = pin;\n\t\t\t\tresetReadAccumulator();\n\t\t\t\treadConversationId = pin;\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction scheduleReadReport(): void {\n\t\tif (readTimer !== null) {\n\t\t\treturn;\n\t\t}\n\t\treadTimer = setTimeout(() => {\n\t\t\treadTimer = null;\n\t\t\tif (readMax > readReported) {\n\t\t\t\treadReported = readMax;\n\t\t\t\thost.sendEvent(\"chat.messages.read\", {\n\t\t\t\t\tcursor: readMax,\n\t\t\t\t\t...(readConversationId ? { conversationId: readConversationId } : {}),\n\t\t\t\t});\n\t\t\t}\n\t\t}, READ_REPORT_DEBOUNCE_MS);\n\t}\n\n\tlet newConversationBlockedUntil = 0;\n\n\tconst operations: BridgeOperations = {\n\t\tsendMessage(body, opts) {\n\t\t\tconst trimmed = typeof body === \"string\" ? body.trim() : \"\";\n\t\t\tconst secret = opts?.secret === true;\n\t\t\tconst tempMessageUuid = mintUuid();\n\t\t\tlet errorBaseline: string | null = null;\n\t\t\tlet errorSeqBaseline: number | null = null;\n\t\t\tif (trimmed) {\n\t\t\t\tconst preSend = host.getState();\n\t\t\t\terrorBaseline = preSend?.[\"chat.error\"] ?? null;\n\t\t\t\terrorSeqBaseline = preSend?.[\"chat.error.seq\"] ?? null;\n\t\t\t\thost.sendEvent(\"chat.message.send\", {\n\t\t\t\t\tbody: trimmed,\n\t\t\t\t\tmessageType: secret ? \"secret\" : \"text\",\n\t\t\t\t\ttempMessageUuid,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttempMessageUuid,\n\t\t\t\tsettled(settleOpts) {\n\t\t\t\t\tif (!trimmed) {\n\t\t\t\t\t\treturn Promise.reject(\n\t\t\t\t\t\t\tnew Error(\"sendMessage requires a non-empty body.\"),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (secret) {\n\t\t\t\t\t\t// No optimistic row exists for a secret send by design, so\n\t\t\t\t\t\t// there is nothing to resolve with. Time-bound or skip; the\n\t\t\t\t\t\t// wait is still bounded by client destroy.\n\t\t\t\t\t\treturn settleFromState<Message>(\n\t\t\t\t\t\t\thost,\n\t\t\t\t\t\t\tlifecycle,\n\t\t\t\t\t\t\t() => undefined,\n\t\t\t\t\t\t\tsettleOpts,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn settleFromState<Message>(\n\t\t\t\t\t\thost,\n\t\t\t\t\t\tlifecycle,\n\t\t\t\t\t\t(state) => {\n\t\t\t\t\t\t\tconst messages = state?.[\"chat.messages\"] ?? [];\n\t\t\t\t\t\t\tconst match = messages.find(\n\t\t\t\t\t\t\t\t(message) => message.clientKey === tempMessageUuid,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\treturn { value: match };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = state?.[\"chat.error\"] ?? null;\n\t\t\t\t\t\t\t// A core that stamps `chat.error.clientKey` (always present,\n\t\t\t\t\t\t\t// null when unattributed) attributes every pre-row send\n\t\t\t\t\t\t\t// failure to the send's own key, so the correlation is exact:\n\t\t\t\t\t\t\t// only THIS send's error rejects, and an unrelated failure\n\t\t\t\t\t\t\t// landing between the send and its optimistic row (the seq\n\t\t\t\t\t\t\t// fallback's false-rejection window) is ignored.\n\t\t\t\t\t\t\tif (state !== null && \"chat.error.clientKey\" in state) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\terror !== null &&\n\t\t\t\t\t\t\t\t\tstate[\"chat.error.clientKey\"] === tempMessageUuid\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn { error: new Error(error) };\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst errorSeq = state?.[\"chat.error.seq\"];\n\t\t\t\t\t\t\tif (typeof errorSeq === \"number\") {\n\t\t\t\t\t\t\t\tif (errorSeqBaseline === null) {\n\t\t\t\t\t\t\t\t\t// The send raced state hydration (no state yet when the\n\t\t\t\t\t\t\t\t\t// baseline was captured): latch the first observed seq\n\t\t\t\t\t\t\t\t\t// and treat the current error as pre-existing.\n\t\t\t\t\t\t\t\t\terrorSeqBaseline = errorSeq;\n\t\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// The seq advances on EVERY error set — including a repeat\n\t\t\t\t\t\t\t\t// of the identical text, which the fallback below cannot\n\t\t\t\t\t\t\t\t// see. `!==` rather than `>`, so a counter restarted by a\n\t\t\t\t\t\t\t\t// full core reset still rejects.\n\t\t\t\t\t\t\t\tif (error !== null && errorSeq !== errorSeqBaseline) {\n\t\t\t\t\t\t\t\t\treturn { error: new Error(error) };\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Core document predates chat.error.seq: fall back to text\n\t\t\t\t\t\t\t// transitions (a repeated identical error is invisible here).\n\t\t\t\t\t\t\tif (error === null) {\n\t\t\t\t\t\t\t\terrorBaseline = null;\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (error !== errorBaseline) {\n\t\t\t\t\t\t\t\treturn { error: new Error(error) };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsettleOpts,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\tretryMessage(messageId) {\n\t\t\thost.sendEvent(\"chat.message.retry\", {\n\t\t\t\tmessage: { id: messageId, type: \"text\" },\n\t\t\t});\n\t\t},\n\n\t\tcanRetryMessage(message) {\n\t\t\tconst state = host.getState();\n\t\t\treturn (\n\t\t\t\tmessage.type === \"text\" &&\n\t\t\t\tmessage.isRetryable === true &&\n\t\t\t\tstate?.[\"zendesk.sdk.disconnected\"] !== true &&\n\t\t\t\t(message.conversationId == null ||\n\t\t\t\t\tmessage.conversationId === (state?.[\"chat.conversationId\"] ?? null))\n\t\t\t);\n\t\t},\n\n\t\tselectQuickReply(reply, messageId) {\n\t\t\thost.sendEvent(\"chat.quickReply.select\", {\n\t\t\t\tlabel: reply.label,\n\t\t\t\ttarget: reply.target,\n\t\t\t\tbuttonType: reply.buttonType,\n\t\t\t\t...(reply.value !== undefined ? { value: reply.value } : {}),\n\t\t\t\tmessageId,\n\t\t\t});\n\t\t},\n\n\t\tselectOption(option, messageId) {\n\t\t\thost.sendEvent(\"chat.option.select\", {\n\t\t\t\toptionId: option.id,\n\t\t\t\toptionLabel: option.label || option.id,\n\t\t\t\tmessageId,\n\t\t\t});\n\t\t},\n\n\t\tsubmitListSelection(selectedIds) {\n\t\t\tconst listSelection =\n\t\t\t\thost.getState()?.[\"conversation.stateData.listSelection\"];\n\t\t\tif (!listSelection) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thost.sendEvent(\"chat.selectableList.submit\", {\n\t\t\t\tselectedIds,\n\t\t\t\tdataId: listSelection.dataId,\n\t\t\t});\n\t\t},\n\n\t\tcancelListSelection(label) {\n\t\t\tconst listSelection =\n\t\t\t\thost.getState()?.[\"conversation.stateData.listSelection\"];\n\t\t\t// Core drops a cancel without a response id, so don't send one.\n\t\t\tif (!listSelection?.cancelCaptureResponseId) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thost.sendEvent(\"listSelection.cancel\", {\n\t\t\t\tcancelResponseId: listSelection.cancelCaptureResponseId,\n\t\t\t\tcancelLabel: label ?? listSelection.cancelCaptureLabel,\n\t\t\t\tdataId: listSelection.dataId,\n\t\t\t});\n\t\t},\n\n\t\tsubmitCapture(value) {\n\t\t\tconst capture = host.getState()?.[\"conversation.stateData.capture\"];\n\t\t\tif (!capture) {\n\t\t\t\treturn {\n\t\t\t\t\tsettled: () =>\n\t\t\t\t\t\tPromise.reject(new Error(\"No capture field is active.\")),\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst dataId = capture.dataId;\n\t\t\tconst submitId = mintUuid();\n\t\t\thost.sendEvent(\"capture.submit\", { value, dataId, submitId });\n\t\t\t// The verdict rides the capture object itself: `isSubmitting` is\n\t\t\t// core-owned, flips true while the submit is in flight, and the\n\t\t\t// falling edge carries `validationErrorMessage`; an ACCEPTED value\n\t\t\t// removes the whole object (or replaces it with the next block).\n\t\t\t// Every failure or drop WITHOUT a server validation verdict — a\n\t\t\t// pre-flight drop that raises and settles `isSubmitting` inside one\n\t\t\t// broadcast batch, a transport failure, a duplicate submit dropped\n\t\t\t// while another is in flight — lands on `lastSubmitFailure` keyed\n\t\t\t// with this call's own `submitId`, so the correlation is exact and\n\t\t\t// the global `chat.error` is never consulted.\n\t\t\tlet sawSubmitting = false;\n\t\t\treturn {\n\t\t\t\tsettled: (settleOpts) =>\n\t\t\t\t\tsettleFromState<OperationResult>(\n\t\t\t\t\t\thost,\n\t\t\t\t\t\tlifecycle,\n\t\t\t\t\t\t(state) => {\n\t\t\t\t\t\t\tconst current = state?.[\"conversation.stateData.capture\"] ?? null;\n\t\t\t\t\t\t\tif (!current || current.dataId !== dataId) {\n\t\t\t\t\t\t\t\treturn { value: { ok: true } };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Keyed failure BEFORE the in-flight latch: a duplicate\n\t\t\t\t\t\t\t// submit's drop is stamped while the OTHER submit still holds\n\t\t\t\t\t\t\t// `isSubmitting`, and latching that flag here would make this\n\t\t\t\t\t\t\t// handle inherit the other submit's verdict.\n\t\t\t\t\t\t\tconst failure = current.lastSubmitFailure;\n\t\t\t\t\t\t\tif (failure && failure.submitId === submitId) {\n\t\t\t\t\t\t\t\treturn { value: { ok: false, message: failure.message } };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (current.isSubmitting === true) {\n\t\t\t\t\t\t\t\tsawSubmitting = true;\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (sawSubmitting) {\n\t\t\t\t\t\t\t\treturn current.validationErrorMessage\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tvalue: {\n\t\t\t\t\t\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: current.validationErrorMessage,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: { value: { ok: true } };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsettleOpts,\n\t\t\t\t\t),\n\t\t\t};\n\t\t},\n\n\t\tcancelCapture(label) {\n\t\t\tconst capture = host.getState()?.[\"conversation.stateData.capture\"];\n\t\t\tif (!capture?.cancelCaptureResponseId) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thost.sendEvent(\"capture.cancel\", {\n\t\t\t\tcancelResponseId: capture.cancelCaptureResponseId,\n\t\t\t\tcancelLabel: label ?? capture.cancelCaptureLabel,\n\t\t\t\tdataId: capture.dataId,\n\t\t\t});\n\t\t},\n\n\t\taddReaction(messageId, reaction) {\n\t\t\thost.sendEvent(\"chat.message.reaction.add\", { messageId, reaction });\n\t\t},\n\n\t\treportLinkClick(messageId, opts) {\n\t\t\thost.sendEvent(\"chat.link.click\", {\n\t\t\t\tmessageId,\n\t\t\t\t...(opts?.navigateInHost !== undefined\n\t\t\t\t\t? { navigateInHost: opts.navigateInHost }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t},\n\n\t\tmarkRead(cursor, opts) {\n\t\t\tif (typeof cursor !== \"string\" || cursor === \"\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tensureReadResetSubscription();\n\t\t\tconst conversationId =\n\t\t\t\topts?.conversationId ??\n\t\t\t\thost.getState()?.[\"chat.conversationId\"] ??\n\t\t\t\tnull;\n\t\t\tif (conversationId !== readConversationId) {\n\t\t\t\tresetReadAccumulator();\n\t\t\t\treadConversationId = conversationId;\n\t\t\t}\n\t\t\tif (cursor > readMax) {\n\t\t\t\treadMax = cursor;\n\t\t\t\tscheduleReadReport();\n\t\t\t}\n\t\t},\n\n\t\tloadOlderMessages() {\n\t\t\tconst state = host.getState();\n\t\t\tif (\n\t\t\t\tstate?.[\"chat.canLoadMore\"] !== true ||\n\t\t\t\tstate[\"chat.loadingPreviousMessages\"] === true\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thost.sendEvent(\"chat.logs.loadMore\");\n\t\t},\n\n\t\tonHistoryLoaded(callback) {\n\t\t\tlet previous = host.getState()?.[\"chat.recentMessagesLoadSeq\"] ?? 0;\n\t\t\treturn host.subscribe(() => {\n\t\t\t\tconst seq = host.getState()?.[\"chat.recentMessagesLoadSeq\"] ?? 0;\n\t\t\t\tif (seq > previous) {\n\t\t\t\t\tprevious = seq;\n\t\t\t\t\tcallback(seq);\n\t\t\t\t} else if (seq < previous) {\n\t\t\t\t\t// Returned to 0 on a chat clear; re-arm without firing.\n\t\t\t\t\tprevious = seq;\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tsubmitCsat(data, opts) {\n\t\t\tconst { surveyType, partial, conversationId } = opts;\n\t\t\tconst baselineSeq = host.getState()?.[\"csat.submitSuccess.seq\"] ?? 0;\n\t\t\tlet errorBaseline = host.getState()?.[\"csat.submitError\"] ?? null;\n\t\t\tlet errorSeqBaseline = host.getState()?.[\"csat.submitError.seq\"] ?? null;\n\t\t\t// `null` means \"omitted\" on the wire AND in correlation: core's\n\t\t\t// resolver treats a falsy id as absent and reports the SESSION's id\n\t\t\t// back, so a null-scoped predicate could never match it and the\n\t\t\t// handle would hang on a fully successful submit.\n\t\t\thost.sendEvent(\"csat.submit\", {\n\t\t\t\tdata,\n\t\t\t\tsurveyType,\n\t\t\t\t...(partial !== undefined ? { partial } : {}),\n\t\t\t\t...(conversationId != null ? { conversationId } : {}),\n\t\t\t});\n\t\t\t// When the caller omitted conversationId (End Chat / proactive),\n\t\t\t// core resolves the conversation itself, so match on the type +\n\t\t\t// seq alone; a caller-scoped survey matches its exact conversation.\n\t\t\tconst matchesConversation = (\n\t\t\t\tcandidate: string | null | undefined,\n\t\t\t): boolean =>\n\t\t\t\tconversationId == null || (candidate ?? null) === conversationId;\n\t\t\treturn {\n\t\t\t\tsettled: (settleOpts) =>\n\t\t\t\t\tsettleFromState<OperationResult>(\n\t\t\t\t\t\thost,\n\t\t\t\t\t\tlifecycle,\n\t\t\t\t\t\t(state) => {\n\t\t\t\t\t\t\tconst seq = state?.[\"csat.submitSuccess.seq\"] ?? 0;\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tseq > baselineSeq &&\n\t\t\t\t\t\t\t\tstate?.[\"csat.submitSuccess.surveyType\"] === surveyType &&\n\t\t\t\t\t\t\t\tmatchesConversation(\n\t\t\t\t\t\t\t\t\tstate?.[\"csat.submitSuccess.conversationId\"],\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn { value: { ok: true } };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = state?.[\"csat.submitError\"] ?? null;\n\t\t\t\t\t\t\tconst errorSeq = state?.[\"csat.submitError.seq\"];\n\t\t\t\t\t\t\tif (typeof errorSeq === \"number\") {\n\t\t\t\t\t\t\t\t// The seq advances on EVERY error set — including a repeat\n\t\t\t\t\t\t\t\t// of the identical text (the same submit dropped twice at a\n\t\t\t\t\t\t\t\t// core gate), which the text transition below cannot see.\n\t\t\t\t\t\t\t\tif (errorSeqBaseline === null) {\n\t\t\t\t\t\t\t\t\t// The submit raced state hydration: latch the first\n\t\t\t\t\t\t\t\t\t// observed seq and treat the current error as\n\t\t\t\t\t\t\t\t\t// pre-existing.\n\t\t\t\t\t\t\t\t\terrorSeqBaseline = errorSeq;\n\t\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\terror !== null &&\n\t\t\t\t\t\t\t\t\terrorSeq !== errorSeqBaseline &&\n\t\t\t\t\t\t\t\t\tstate?.[\"csat.submitErrorSurveyType\"] === surveyType &&\n\t\t\t\t\t\t\t\t\tmatchesConversation(state?.[\"csat.submitErrorConversationId\"])\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn { value: { ok: false, message: error } };\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Core document predates csat.submitError.seq: fall back to\n\t\t\t\t\t\t\t// text transitions.\n\t\t\t\t\t\t\tif (error === null) {\n\t\t\t\t\t\t\t\t// Core clears the error at the start of every\n\t\t\t\t\t\t\t\t// non-partial submit; from here any error is ours.\n\t\t\t\t\t\t\t\terrorBaseline = null;\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\terror !== errorBaseline &&\n\t\t\t\t\t\t\t\tstate?.[\"csat.submitErrorSurveyType\"] === surveyType &&\n\t\t\t\t\t\t\t\tmatchesConversation(state?.[\"csat.submitErrorConversationId\"])\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn { value: { ok: false, message: error } };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsettleOpts,\n\t\t\t\t\t),\n\t\t\t};\n\t\t},\n\n\t\ttrackCsatShown(surveyType, conversationId) {\n\t\t\thost.sendEvent(\"csat.shown\", {\n\t\t\t\tsurveyType,\n\t\t\t\t...(conversationId != null ? { conversationId } : {}),\n\t\t\t});\n\t\t},\n\n\t\tcheckEndChatEligibility(opts) {\n\t\t\tconst baselineSeq = host.getState()?.[\"csat.endChatEligibility.seq\"] ?? 0;\n\t\t\thost.sendEvent(\"csat.checkEndChatEligibility\");\n\t\t\treturn settleFromState<EndChatEligibilityResult>(\n\t\t\t\thost,\n\t\t\t\tlifecycle,\n\t\t\t\t(state) => {\n\t\t\t\t\tconst seq = state?.[\"csat.endChatEligibility.seq\"] ?? 0;\n\t\t\t\t\tif (seq <= baselineSeq) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: {\n\t\t\t\t\t\t\teligible: state?.[\"csat.endChatEligible\"] ?? null,\n\t\t\t\t\t\t\tsurveyTarget: state?.[\"csat.endChatSurveyTarget\"] ?? null,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\topts,\n\t\t\t);\n\t\t},\n\n\t\tskipCsatAndEndChat() {\n\t\t\thost.sendEvent(\"endChat.skipCsat\");\n\t\t},\n\n\t\tstartNewConversation(opts) {\n\t\t\tconst now = Date.now();\n\t\t\tif (now < newConversationBlockedUntil) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tnewConversationBlockedUntil =\n\t\t\t\tnow + (opts?.cooldownMs ?? START_NEW_CONVERSATION_COOLDOWN_MS);\n\t\t\thost.sendEvent(\"app.startNewConversation\");\n\t\t\treturn true;\n\t\t},\n\n\t\tresetApp() {\n\t\t\thost.sendEvent(\"app.reset\");\n\t\t},\n\n\t\tnotifyComposerChanged(opts) {\n\t\t\t// Even a content-free typing edge is a signal that a secret is\n\t\t\t// being typed, so it stays suppressed at the source.\n\t\t\tif (\n\t\t\t\topts?.secret === true ||\n\t\t\t\thost.getState()?.[\"ui.secretMessage.isOpen\"] === true\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thost.sendEvent(\"ui.composer.textChanged\");\n\t\t},\n\n\t\ttoggleSecretMessage(open) {\n\t\t\thost.sendEvent(\"ui.secretMessage.toggle\", { open });\n\t\t},\n\n\t\tstartFileUpload(file) {\n\t\t\tconst uploadId = mintUuid();\n\t\t\thost.sendEvent(\"file.upload.start\", { file, uploadId });\n\t\t\treturn {\n\t\t\t\tuploadId,\n\t\t\t\tretry() {\n\t\t\t\t\thost.sendEvent(\"file.upload.retry\", { file, uploadId });\n\t\t\t\t},\n\t\t\t\tdismissError() {\n\t\t\t\t\thost.sendEvent(\"file.upload.errorDismiss\");\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\tsetLanguage(language) {\n\t\t\thost.sendEvent(\"settings.language.change\", { language });\n\t\t},\n\n\t\temailTranscript(email, opts) {\n\t\t\t// Core drops an empty email silently — no status transition ever —\n\t\t\t// so validate here and reject immediately instead of hanging.\n\t\t\tconst trimmed = typeof email === \"string\" ? email.trim() : \"\";\n\t\t\tif (!trimmed) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew Error(\"emailTranscript requires a non-empty email.\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\thost.sendEvent(\"settings.transcript.email.send\", { email: trimmed });\n\t\t\treturn transcriptSettled(\"transcript.email.status\", opts);\n\t\t},\n\n\t\tdownloadTranscript(opts) {\n\t\t\thost.sendEvent(\"settings.transcript.download.request\");\n\t\t\treturn transcriptSettled(\"transcript.download.status\", opts);\n\t\t},\n\n\t\tsetAlertSound(enabled) {\n\t\t\thost.sendEvent(\"settings.alertSound.toggle\", { enabled });\n\t\t},\n\n\t\trequestNotificationPermission() {\n\t\t\thost.sendEvent(\"settings.notifications.requestPermission\");\n\t\t},\n\n\t\tsetTheme(theme) {\n\t\t\thost.sendEvent(\"appearance.setTheme\", { theme });\n\t\t},\n\n\t\tsetTextSize(size) {\n\t\t\thost.sendEvent(\"appearance.setTextSize\", { textSize: size });\n\t\t},\n\n\t\tclose() {\n\t\t\thost.sendEvent(\"ui.closeButton.click\");\n\t\t},\n\n\t\tminimize() {\n\t\t\thost.sendEvent(\"ui.minimizeButton.click\");\n\t\t},\n\n\t\tdismissToast() {\n\t\t\thost.sendEvent(\"ui.toast.dismiss\");\n\t\t},\n\n\t\tdismissError() {\n\t\t\thost.sendEvent(\"chat.error.dismiss\");\n\t\t},\n\n\t\tsetHostViewportExpanded(expanded) {\n\t\t\thost.sendEvent(\"ui.hostViewport.set\", { expanded });\n\t\t},\n\n\t\tretryAfterOutage() {\n\t\t\thost.sendEvent(\"outage.retry\");\n\t\t},\n\n\t\treportAppError(error, componentStack) {\n\t\t\tconst err = error instanceof Error ? error : null;\n\t\t\thost.sendEvent(\"app.error.report\", {\n\t\t\t\tmessage: err?.message ?? String(error),\n\t\t\t\tstack: err?.stack ?? \"\",\n\t\t\t\tcomponentStack: componentStack ?? \"\",\n\t\t\t});\n\t\t},\n\t};\n\n\tfunction transcriptSettled(\n\t\tkey: \"transcript.email.status\" | \"transcript.download.status\",\n\t\topts?: SettleOptions,\n\t): Promise<void> {\n\t\tconst seqKey = `${key}.seq` as\n\t\t\t| \"transcript.email.status.seq\"\n\t\t\t| \"transcript.download.status.seq\";\n\t\tlet previous = host.getState()?.[key] ?? \"idle\";\n\t\tlet seqBaseline = host.getState()?.[seqKey] ?? null;\n\t\treturn settleFromState<void>(\n\t\t\thost,\n\t\t\tlifecycle,\n\t\t\t(state) => {\n\t\t\t\tconst status = state?.[key] ?? \"idle\";\n\t\t\t\tconst seq = state?.[seqKey];\n\t\t\t\tif (typeof seq === \"number\") {\n\t\t\t\t\t// The seq advances on every status write, so a request core\n\t\t\t\t\t// drops straight to a terminal status (never `pending` in a\n\t\t\t\t\t// broadcast snapshot) and a repeat of the previous terminal\n\t\t\t\t\t// status both stay observable.\n\t\t\t\t\tif (seqBaseline === null) {\n\t\t\t\t\t\t// The request raced state hydration: latch the first observed\n\t\t\t\t\t\t// seq and treat the current status as pre-existing.\n\t\t\t\t\t\tseqBaseline = seq;\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\tif (seq === seqBaseline) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\tif (status === \"success\") {\n\t\t\t\t\t\treturn { value: undefined };\n\t\t\t\t\t}\n\t\t\t\t\tif (status === \"error\") {\n\t\t\t\t\t\treturn { error: new Error(`${key} reported an error.`) };\n\t\t\t\t\t}\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\t// Core document predates the seq key: fall back to the\n\t\t\t\t// pending -> terminal edge.\n\t\t\t\tconst was = previous;\n\t\t\t\tprevious = status;\n\t\t\t\tif (was !== \"pending\") {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\tif (status === \"success\") {\n\t\t\t\t\treturn { value: undefined };\n\t\t\t\t}\n\t\t\t\tif (status === \"error\") {\n\t\t\t\t\treturn { error: new Error(`${key} reported an error.`) };\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t\topts,\n\t\t);\n\t}\n\n\treturn {\n\t\toperations,\n\t\tdispose() {\n\t\t\tif (disposed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdisposed = true;\n\t\t\tresetReadAccumulator();\n\t\t\treadResetUnsubscribe?.();\n\t\t\treadResetUnsubscribe = null;\n\t\t\t// Reject every in-flight settled() promise: the transport is gone,\n\t\t\t// so no state transition can ever answer it. Snapshot first — each\n\t\t\t// abort unregisters itself from the live set.\n\t\t\tfor (const abort of [...liveSettleAborts]) {\n\t\t\t\tabort(new BridgeClientDestroyedError());\n\t\t\t}\n\t\t\tliveSettleAborts.clear();\n\t\t},\n\t};\n}\n","import type { BridgeClient } from \"./bridge-client\";\nimport { createBridgeOperations, createStateObservation } from \"./operations\";\nimport type { AppDisplayState, AppEvents } from \"./types\";\n\n/** One recorded `sendEvent` call. */\nexport interface SentBridgeEvent<K extends keyof AppEvents = keyof AppEvents> {\n\tevent: K;\n\tpayload: AppEvents[K] | undefined;\n}\n\n/**\n * A scriptable, in-memory {@link BridgeClient} for unit tests.\n *\n * No postMessage, no origin checks, no network: tests drive state through\n * `setState`/`updateState` and assert on the events the component under test\n * sent through `sentEvents`. The `operations`, `subscribeKey`, and `select`\n * surfaces are the REAL implementations running over this in-memory state:\n * every operation records its events in `sentEvents`, and a correlated\n * handle (`sendMessage().settled()`, `submitCsat().settled()`, ...) resolves\n * when the test scripts the answering state transition via `updateState`.\n */\nexport interface MockBridgeClient extends BridgeClient {\n\t/** Replace the state snapshot and notify subscribers. */\n\tsetState(state: AppDisplayState | null): void;\n\t/**\n\t * Shallow-merge a partial snapshot over the current state (or over an empty\n\t * object when no state is set) and notify subscribers. Seeds\n\t * `\"chat.error.clientKey\": null` when the key is absent — production core\n\t * always publishes it (null when no keyed send owns the error), and\n\t * `operations` send handles take the keyed correlation path only when it is\n\t * present. Script a `chat.error` fixture with the send's `tempMessageUuid`\n\t * as `chat.error.clientKey` to reject that handle; use `setState` to script\n\t * an older core document without the key.\n\t */\n\tupdateState(partial: Partial<AppDisplayState>): void;\n\t/**\n\t * Every event sent through the client, in order. Each read returns a fresh\n\t * copy, so a captured array survives `clearSentEvents()`.\n\t */\n\treadonly sentEvents: readonly SentBridgeEvent[];\n\t/** Forget recorded events without touching state or subscribers. */\n\tclearSentEvents(): void;\n\t/** Number of active subscribers. */\n\treadonly subscriberCount: number;\n\t/** True once `destroy()` has been called. */\n\treadonly destroyed: boolean;\n}\n\n/**\n * Create a {@link MockBridgeClient}, optionally seeded with an initial state\n * snapshot. Mirrors the real client's teardown semantics: after `destroy()`,\n * state is `null`, subscribers are cleared, `sendEvent` is a no-op, and every\n * in-flight `operations` `settled()` promise rejects with a\n * `BridgeClientDestroyedError`. Notification also mirrors the real client:\n * a throwing subscriber is caught and logged, never aborting the pass.\n */\nexport function createMockBridgeClient(\n\tinitialState: AppDisplayState | null = null,\n): MockBridgeClient {\n\tlet state = initialState;\n\tlet destroyed = false;\n\tconst subscribers = new Set<() => void>();\n\tconst sentEvents: SentBridgeEvent[] = [];\n\n\tfunction notify(): void {\n\t\t// Mirrors the real client's notify(): one throwing subscriber must not\n\t\t// abort the pass (a settled() promise resolved from a later subscriber\n\t\t// would otherwise hang) or skip subscribers that unsubscribe mid-pass.\n\t\tfor (const callback of [...subscribers]) {\n\t\t\ttry {\n\t\t\t\tcallback();\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"[AdaMessaging] Bridge subscriber threw:\", err);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction getState(): AppDisplayState | null {\n\t\treturn state;\n\t}\n\n\tfunction subscribe(callback: () => void): () => void {\n\t\tsubscribers.add(callback);\n\t\treturn () => {\n\t\t\tsubscribers.delete(callback);\n\t\t};\n\t}\n\n\tfunction sendEvent<K extends keyof AppEvents>(\n\t\tevent: K,\n\t\t...args: AppEvents[K] extends undefined ? [] : [payload: AppEvents[K]]\n\t): void {\n\t\tif (destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tsentEvents.push({ event, payload: args[0] });\n\t}\n\n\tconst host = { getState, subscribe, sendEvent };\n\tconst observation = createStateObservation(host);\n\tconst operationsBundle = createBridgeOperations(host);\n\n\treturn {\n\t\tgetState,\n\t\tsubscribe,\n\t\tsendEvent,\n\t\tsubscribeKey: observation.subscribeKey,\n\t\tselect: observation.select,\n\t\toperations: operationsBundle.operations,\n\n\t\tdestroy(): void {\n\t\t\tdestroyed = true;\n\t\t\toperationsBundle.dispose();\n\t\t\tstate = null;\n\t\t\tsubscribers.clear();\n\t\t},\n\n\t\tsetState(next: AppDisplayState | null): void {\n\t\t\tstate = next;\n\t\t\tnotify();\n\t\t},\n\n\t\tupdateState(partial: Partial<AppDisplayState>): void {\n\t\t\t// Production core always publishes `chat.error.clientKey` (null when\n\t\t\t// unattributed), and `operations` feature-detects on its presence.\n\t\t\t// Seed it so a scripted failure exercises the keyed path production\n\t\t\t// takes; override it in `partial` (or use setState) to script an\n\t\t\t// older core document.\n\t\t\tstate = {\n\t\t\t\t\"chat.error.clientKey\": null,\n\t\t\t\t...(state ?? {}),\n\t\t\t\t...partial,\n\t\t\t} as AppDisplayState;\n\t\t\tnotify();\n\t\t},\n\n\t\tget sentEvents(): readonly SentBridgeEvent[] {\n\t\t\treturn [...sentEvents];\n\t\t},\n\n\t\tclearSentEvents(): void {\n\t\t\tsentEvents.length = 0;\n\t\t},\n\n\t\tget subscriberCount(): number {\n\t\t\treturn subscribers.size;\n\t\t},\n\n\t\tget destroyed(): boolean {\n\t\t\treturn destroyed;\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|