@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
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { AppDisplayState, AppEvents, CsatSubmitData, Message, OptionItem, QuickReply } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The minimal client surface the operations layer builds on. Satisfied by the
|
|
4
|
+
* real `BridgeClient` and by the npm test mock.
|
|
5
|
+
*/
|
|
6
|
+
export interface BridgeOperationsHost {
|
|
7
|
+
getState(): AppDisplayState | null;
|
|
8
|
+
subscribe(callback: () => void): () => void;
|
|
9
|
+
sendEvent<K extends keyof AppEvents>(event: K, ...args: AppEvents[K] extends undefined ? [] : [payload: AppEvents[K]]): void;
|
|
10
|
+
}
|
|
11
|
+
/** Outcome of a correlated submit: accepted, or rejected with a message. */
|
|
12
|
+
export type OperationResult = {
|
|
13
|
+
ok: true;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
export interface SettleOptions {
|
|
19
|
+
/** Reject with an `Error` if the operation has not settled in time. */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Rejection every in-flight `settled()` promise receives when the client is
|
|
24
|
+
* destroyed, so teardown is the layer's bound of last resort even without a
|
|
25
|
+
* `timeoutMs`. The class identity does not survive the npm/CDN module split,
|
|
26
|
+
* so detect it by `error.name === "BridgeClientDestroyedError"` (or via
|
|
27
|
+
* {@link BridgeClientDestroyedError.is}), never by `instanceof` alone.
|
|
28
|
+
*/
|
|
29
|
+
export declare class BridgeClientDestroyedError extends Error {
|
|
30
|
+
constructor(message?: string);
|
|
31
|
+
static is(error: unknown): error is BridgeClientDestroyedError;
|
|
32
|
+
}
|
|
33
|
+
export interface SendHandle {
|
|
34
|
+
/**
|
|
35
|
+
* The uuid put on the wire as `tempMessageUuid`. Core seeds the optimistic
|
|
36
|
+
* message's `clientKey` from it, so the sent message is findable in
|
|
37
|
+
* `chat.messages` by `clientKey === tempMessageUuid`.
|
|
38
|
+
*/
|
|
39
|
+
readonly tempMessageUuid: string;
|
|
40
|
+
/**
|
|
41
|
+
* Resolves with the message once it appears in `chat.messages` (the
|
|
42
|
+
* optimistic row; the durable server echo keeps the same `clientKey`).
|
|
43
|
+
* Rejects when core reports THIS send's error before the row appears —
|
|
44
|
+
* rate limit, over-length, session not ready, dispatch failure —
|
|
45
|
+
* correlated exactly on `chat.error.clientKey` matching the minted
|
|
46
|
+
* `tempMessageUuid`, so an unrelated failure in the same window never
|
|
47
|
+
* falsely rejects a delivered send. On a core document that predates
|
|
48
|
+
* `chat.error.clientKey`, falls back to the positional
|
|
49
|
+
* `chat.error.seq`-advance detection, and before that to `chat.error`
|
|
50
|
+
* text transitions. A secret send NEVER resolves — core deliberately
|
|
51
|
+
* writes no transcript row for it — so pass `timeoutMs` or skip
|
|
52
|
+
* `settled()` for secret sends. Destroying the client rejects every
|
|
53
|
+
* pending `settled()` with a {@link BridgeClientDestroyedError}.
|
|
54
|
+
*/
|
|
55
|
+
settled(opts?: SettleOptions): Promise<Message>;
|
|
56
|
+
}
|
|
57
|
+
export interface CaptureHandle {
|
|
58
|
+
/**
|
|
59
|
+
* Resolves on the verdict for THIS capture submit: `{ ok: true }` when the
|
|
60
|
+
* value was accepted (core removes the capture block), or
|
|
61
|
+
* `{ ok: false, message }` when it was rejected — a server validation
|
|
62
|
+
* rejection (observed on the `isSubmitting` falling edge, with the
|
|
63
|
+
* validation error text), or a failure/drop core stamps on the capture's
|
|
64
|
+
* `lastSubmitFailure` keyed to this submit's own minted `submitId` (no
|
|
65
|
+
* session to round-trip, value over the length cap, transport failure, a
|
|
66
|
+
* duplicate submit while one is in flight, a host-driven reset). The
|
|
67
|
+
* correlation is exact: the handle never reads the global `chat.error`,
|
|
68
|
+
* so an unrelated failure in the same window can never masquerade as this
|
|
69
|
+
* capture's verdict. On a core document that predates `lastSubmitFailure`,
|
|
70
|
+
* those failures are not capture-attributable at all and the handle stays
|
|
71
|
+
* pending — pass `timeoutMs` when targeting older cores.
|
|
72
|
+
*/
|
|
73
|
+
settled(opts?: SettleOptions): Promise<OperationResult>;
|
|
74
|
+
}
|
|
75
|
+
export interface CsatSubmitHandle {
|
|
76
|
+
/**
|
|
77
|
+
* Resolves when core reports this survey's outcome, correlated on the
|
|
78
|
+
* success seq advance x surveyType x conversationId triple (and the
|
|
79
|
+
* mirror-image error triple). A `partial` submit reports no terminal
|
|
80
|
+
* outcome, so `settled()` on one only settles via `timeoutMs`.
|
|
81
|
+
*/
|
|
82
|
+
settled(opts?: SettleOptions): Promise<OperationResult>;
|
|
83
|
+
}
|
|
84
|
+
export interface FileUploadHandle {
|
|
85
|
+
/** The minted upload id sent with `file.upload.start`. */
|
|
86
|
+
readonly uploadId: string;
|
|
87
|
+
/**
|
|
88
|
+
* Re-send the EXACT same `{ file, uploadId }` pair. Core correlates the
|
|
89
|
+
* retry to the failed attempt by that pair, so a fresh pair would orphan
|
|
90
|
+
* the original error state.
|
|
91
|
+
*/
|
|
92
|
+
retry(): void;
|
|
93
|
+
/** Dismiss a reported upload error (`file.uploadError`). */
|
|
94
|
+
dismissError(): void;
|
|
95
|
+
}
|
|
96
|
+
export interface EndChatEligibilityResult {
|
|
97
|
+
/** `csat.endChatEligible` at the moment the answer arrived. */
|
|
98
|
+
eligible: boolean | null;
|
|
99
|
+
/** Which survey End Chat should present, when eligible. */
|
|
100
|
+
surveyTarget: "bot" | "agent" | null;
|
|
101
|
+
}
|
|
102
|
+
export interface ObserveOptions<T> {
|
|
103
|
+
/** Change detector; defaults to `Object.is`. */
|
|
104
|
+
equals?: (a: T, b: T) => boolean;
|
|
105
|
+
/** Also invoke the callback immediately with the current value. */
|
|
106
|
+
emitInitial?: boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Typed helpers over the bridge event/state contract. Obtain via
|
|
110
|
+
* `client.operations`. Fire-and-forget helpers return `void`; correlated
|
|
111
|
+
* helpers return a handle or Promise that settles from state transitions
|
|
112
|
+
* (there is no request/response channel — core answers through state).
|
|
113
|
+
*/
|
|
114
|
+
export interface BridgeOperations {
|
|
115
|
+
/**
|
|
116
|
+
* Send a chat message. The body is trimmed; an empty result sends nothing
|
|
117
|
+
* (the returned handle then rejects). `secret: true` sends a masked
|
|
118
|
+
* message: the raw body never enters the transcript and the handle never
|
|
119
|
+
* resolves. While `ui.secretMessage.isOpen` is true you likely want
|
|
120
|
+
* `secret: true` to match the composer's mode.
|
|
121
|
+
*/
|
|
122
|
+
sendMessage(body: string, opts?: {
|
|
123
|
+
secret?: boolean;
|
|
124
|
+
}): SendHandle;
|
|
125
|
+
/** Retry a failed send by message id (see {@link canRetryMessage}). */
|
|
126
|
+
retryMessage(messageId: string): void;
|
|
127
|
+
/**
|
|
128
|
+
* Whether the retry affordance should show for a message: a `text`
|
|
129
|
+
* message core marked `isRetryable`, no disconnected Zendesk SDK, and the
|
|
130
|
+
* message belongs to the current conversation (or predates stamping).
|
|
131
|
+
*/
|
|
132
|
+
canRetryMessage(message: Message): boolean;
|
|
133
|
+
/** Answer a quick-reply chip. Pass the chip and its message id verbatim. */
|
|
134
|
+
selectQuickReply(reply: QuickReply, messageId: string): void;
|
|
135
|
+
/** Answer an options block. The label falls back to the option id. */
|
|
136
|
+
selectOption(option: OptionItem, messageId: string): void;
|
|
137
|
+
/**
|
|
138
|
+
* Submit the active selectable list (`conversation.stateData.listSelection`
|
|
139
|
+
* supplies the `dataId`). No-op when no list selection is active.
|
|
140
|
+
*/
|
|
141
|
+
submitListSelection(selectedIds: string[]): void;
|
|
142
|
+
/**
|
|
143
|
+
* Cancel the active selectable list. Uses the block's own cancel response
|
|
144
|
+
* and label (`label` overrides the visible label). No-op when no list
|
|
145
|
+
* selection is active or it has no cancel response.
|
|
146
|
+
*/
|
|
147
|
+
cancelListSelection(label?: string): void;
|
|
148
|
+
/**
|
|
149
|
+
* Submit the active capture field (`conversation.stateData.capture`
|
|
150
|
+
* supplies the `dataId`). Each call mints its own `submitId`, so the
|
|
151
|
+
* handle settles only on ITS OWN submit's verdict or failure — a second
|
|
152
|
+
* submit sent while one is in flight is dropped by core and its handle
|
|
153
|
+
* resolves `{ ok: false }` on its own terms rather than inheriting the
|
|
154
|
+
* in-flight submit's verdict.
|
|
155
|
+
*/
|
|
156
|
+
submitCapture(value: string): CaptureHandle;
|
|
157
|
+
/**
|
|
158
|
+
* Cancel the active capture field. `label` overrides the block's own
|
|
159
|
+
* cancel label. No-op when no capture is active or it has no cancel
|
|
160
|
+
* response.
|
|
161
|
+
*/
|
|
162
|
+
cancelCapture(label?: string): void;
|
|
163
|
+
/** React to a bot message with a thumbs vote. */
|
|
164
|
+
addReaction(messageId: string, reaction: 1 | -1): void;
|
|
165
|
+
/**
|
|
166
|
+
* Report that the user activated a `link` message. Core resolves the
|
|
167
|
+
* stored message for tracking and, with `navigateInHost`, validated
|
|
168
|
+
* same-tab host navigation.
|
|
169
|
+
*/
|
|
170
|
+
reportLinkClick(messageId: string, opts?: {
|
|
171
|
+
navigateInHost?: boolean;
|
|
172
|
+
}): void;
|
|
173
|
+
/**
|
|
174
|
+
* Advance the persisted read watermark. Reports are debounced 400 ms, the
|
|
175
|
+
* watermark is a monotonic lexical max over `_id` cursor strings (calling
|
|
176
|
+
* with an older cursor never lowers it), and the accumulator resets when
|
|
177
|
+
* `chat.conversationId` changes so a pending cursor from conversation A
|
|
178
|
+
* can never commit against B. Call with `message.cursor` for each message
|
|
179
|
+
* that scrolls into view; messages stamped with a DIFFERENT
|
|
180
|
+
* `conversationId` must be excluded by the caller (the transcript
|
|
181
|
+
* preserves the previous conversation's rows).
|
|
182
|
+
*/
|
|
183
|
+
markRead(cursor: string, opts?: {
|
|
184
|
+
conversationId?: string;
|
|
185
|
+
}): void;
|
|
186
|
+
/**
|
|
187
|
+
* Request an older page of history. No-op unless `chat.canLoadMore` is
|
|
188
|
+
* true and no load is already in flight.
|
|
189
|
+
*/
|
|
190
|
+
loadOlderMessages(): void;
|
|
191
|
+
/**
|
|
192
|
+
* Invoke `callback` each time restored/baseline history (re)loads
|
|
193
|
+
* (`chat.recentMessagesLoadSeq` advances) — the one-shot trigger for
|
|
194
|
+
* "scroll to the bottom of restored history" and for seeding an unread
|
|
195
|
+
* divider. Returns an unsubscribe function.
|
|
196
|
+
*/
|
|
197
|
+
onHistoryLoaded(callback: (seq: number) => void): () => void;
|
|
198
|
+
/**
|
|
199
|
+
* Submit a CSAT survey. Pass `conversationId: message.conversationId` for
|
|
200
|
+
* a survey rendered IN the transcript (the row can outlive its
|
|
201
|
+
* conversation); omit it — or pass `null`, which is treated as omitted on
|
|
202
|
+
* the wire and in correlation — for End Chat / proactive flows, where
|
|
203
|
+
* core resolves the conversation itself.
|
|
204
|
+
*/
|
|
205
|
+
submitCsat(data: CsatSubmitData, opts: {
|
|
206
|
+
surveyType: string;
|
|
207
|
+
partial?: boolean;
|
|
208
|
+
conversationId?: string | null;
|
|
209
|
+
}): CsatSubmitHandle;
|
|
210
|
+
/** Analytics: report that a survey was displayed. Fire-and-forget. */
|
|
211
|
+
trackCsatShown(surveyType: string, conversationId?: string | null): void;
|
|
212
|
+
/**
|
|
213
|
+
* Ask core whether End Chat should present a survey. Correlated by
|
|
214
|
+
* latching `csat.endChatEligibility.seq` and resolving when it advances
|
|
215
|
+
* (there is no request id; a `ready -> ready` re-check changes no other
|
|
216
|
+
* observable state).
|
|
217
|
+
*/
|
|
218
|
+
checkEndChatEligibility(opts?: SettleOptions): Promise<EndChatEligibilityResult>;
|
|
219
|
+
/** End the chat without presenting a survey. */
|
|
220
|
+
skipCsatAndEndChat(): void;
|
|
221
|
+
/**
|
|
222
|
+
* Start a new conversation. Applies a local cooldown (default 5 s) that
|
|
223
|
+
* swallows rapid re-clicks; returns `false` while cooling down.
|
|
224
|
+
*/
|
|
225
|
+
startNewConversation(opts?: {
|
|
226
|
+
cooldownMs?: number;
|
|
227
|
+
}): boolean;
|
|
228
|
+
/** Reset the app frame's conversation state. */
|
|
229
|
+
resetApp(): void;
|
|
230
|
+
/**
|
|
231
|
+
* Report a content-free composer-changed edge (drives live-agent typing
|
|
232
|
+
* indicators). The event is payload-free by contract — composer text must
|
|
233
|
+
* never cross the frame boundary — and this helper suppresses it entirely
|
|
234
|
+
* in secret mode (pass `secret: true`, or leave it to the
|
|
235
|
+
* `ui.secretMessage.isOpen` state check): even a content-free typing edge
|
|
236
|
+
* is a signal about the secret being typed.
|
|
237
|
+
*/
|
|
238
|
+
notifyComposerChanged(opts?: {
|
|
239
|
+
secret?: boolean;
|
|
240
|
+
}): void;
|
|
241
|
+
/** Open or close the masked (secret) composer mode. */
|
|
242
|
+
toggleSecretMessage(open: boolean): void;
|
|
243
|
+
/**
|
|
244
|
+
* Start a live-agent file upload. Mints and retains the `{ file,
|
|
245
|
+
* uploadId }` pair so `retry()` replays exactly it. Progress and errors
|
|
246
|
+
* land on `file.isUploading`, `file.transfer`, and `file.uploadError`
|
|
247
|
+
* (core can report an error without ever setting `isUploading`).
|
|
248
|
+
*/
|
|
249
|
+
startFileUpload(file: File): FileUploadHandle;
|
|
250
|
+
/** Change the conversation language (BCP 47 tag the bot offers). */
|
|
251
|
+
setLanguage(language: string): void;
|
|
252
|
+
/**
|
|
253
|
+
* Email the transcript. Resolves/rejects when `transcript.email.status`
|
|
254
|
+
* reaches `success | error` with its `.seq` advanced past this call's
|
|
255
|
+
* baseline — a request core drops without ever broadcasting `pending`
|
|
256
|
+
* (a host-driven reset in progress) still rejects. On a core document
|
|
257
|
+
* that predates the seq, falls back to the `pending -> terminal` edge.
|
|
258
|
+
* The email is trimmed; an empty result rejects immediately without
|
|
259
|
+
* sending (core drops an empty email silently, so nothing would ever
|
|
260
|
+
* settle it).
|
|
261
|
+
*/
|
|
262
|
+
emailTranscript(email: string, opts?: SettleOptions): Promise<void>;
|
|
263
|
+
/**
|
|
264
|
+
* Download the transcript. Same settle contract as
|
|
265
|
+
* {@link BridgeOperations.emailTranscript}, on
|
|
266
|
+
* `transcript.download.status`.
|
|
267
|
+
*/
|
|
268
|
+
downloadTranscript(opts?: SettleOptions): Promise<void>;
|
|
269
|
+
/** Toggle the new-message alert sound. */
|
|
270
|
+
setAlertSound(enabled: boolean): void;
|
|
271
|
+
/**
|
|
272
|
+
* Ask the HOST page to request Web Notification permission (permission is
|
|
273
|
+
* per-origin, so the widget frame's own value is not the governing one).
|
|
274
|
+
* Result lands on `chatter.notificationPermission`.
|
|
275
|
+
*/
|
|
276
|
+
requestNotificationPermission(): void;
|
|
277
|
+
/** Set the user's in-widget theme override. */
|
|
278
|
+
setTheme(theme: "light" | "dark" | "auto"): void;
|
|
279
|
+
/** Set the user's in-widget text-size override. */
|
|
280
|
+
setTextSize(size: "small" | "default" | "large"): void;
|
|
281
|
+
/** The header close affordance (ends the chat when `endChat.canEnd`). */
|
|
282
|
+
close(): void;
|
|
283
|
+
/** Minimize the widget. */
|
|
284
|
+
minimize(): void;
|
|
285
|
+
/** Dismiss the visible toast. */
|
|
286
|
+
dismissToast(): void;
|
|
287
|
+
/**
|
|
288
|
+
* Clear `chat.error` in core. Clear core state rather than hiding the
|
|
289
|
+
* error locally, or the session's second error renders nothing.
|
|
290
|
+
*/
|
|
291
|
+
dismissError(): void;
|
|
292
|
+
/** Ask the SDK host to expand or restore the app's viewport surface. */
|
|
293
|
+
setHostViewportExpanded(expanded: boolean): void;
|
|
294
|
+
/** Retry after a response outage banner. */
|
|
295
|
+
retryAfterOutage(): void;
|
|
296
|
+
/**
|
|
297
|
+
* Report a fatal app error to core (renders the fallback UI and reports
|
|
298
|
+
* telemetry). Callable from anywhere with a client reference — including
|
|
299
|
+
* a class error boundary outside any React provider.
|
|
300
|
+
*/
|
|
301
|
+
reportAppError(error: unknown, componentStack?: string): void;
|
|
302
|
+
}
|
|
303
|
+
interface StateObservation {
|
|
304
|
+
subscribeKey<K extends keyof AppDisplayState>(key: K, callback: (value: AppDisplayState[K] | undefined, previous: AppDisplayState[K] | undefined) => void, opts?: ObserveOptions<AppDisplayState[K] | undefined>): () => void;
|
|
305
|
+
select<T>(selector: (state: AppDisplayState | null) => T, callback: (value: T, previous: T | undefined) => void, opts?: ObserveOptions<T>): () => void;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Build the `subscribeKey`/`select` observation primitives over a host.
|
|
309
|
+
* `Object.is` is a sound default equality for `subscribeKey`: state crosses
|
|
310
|
+
* the frame boundary by structured clone, so the real client restores each
|
|
311
|
+
* structurally unchanged key's previous reference before notifying (the test
|
|
312
|
+
* mock shares references in memory to begin with). For `select`, whose
|
|
313
|
+
* selector typically MINTS a new object per call, pass a custom `equals`
|
|
314
|
+
* (or select a primitive/signature string) when projecting objects.
|
|
315
|
+
*/
|
|
316
|
+
export declare function createStateObservation(host: BridgeOperationsHost): StateObservation;
|
|
317
|
+
export interface BridgeOperationsBundle {
|
|
318
|
+
operations: BridgeOperations;
|
|
319
|
+
/** Release the layer's own subscriptions and timers (client teardown). */
|
|
320
|
+
dispose(): void;
|
|
321
|
+
}
|
|
322
|
+
export declare function createBridgeOperations(rawHost: BridgeOperationsHost): BridgeOperationsBundle;
|
|
323
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface CsatFeedbackOption {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface CsatSurveySettings {
|
|
6
|
+
satisfactionIsEnabled: boolean;
|
|
7
|
+
satisfactionLabel: string;
|
|
8
|
+
satisfactionRange: number;
|
|
9
|
+
satisfactionStyle: string;
|
|
10
|
+
followUpFeedbackIsEnabled: boolean;
|
|
11
|
+
followUpFeedbackPositiveLabel: string;
|
|
12
|
+
followUpFeedbackNegativeLabel: string;
|
|
13
|
+
followUpFeedbackPositiveOptions: CsatFeedbackOption[];
|
|
14
|
+
followUpFeedbackNegativeOptions: CsatFeedbackOption[];
|
|
15
|
+
resolutionIsEnabled: boolean;
|
|
16
|
+
resolutionLabel: string;
|
|
17
|
+
openFeedbackIsEnabled: boolean;
|
|
18
|
+
openFeedbackLabel: string;
|
|
19
|
+
openFeedbackHint?: string;
|
|
20
|
+
customerEffortScoreIsEnabled: boolean;
|
|
21
|
+
customerEffortScoreRange: number;
|
|
22
|
+
customerEffortScoreLabel?: string;
|
|
23
|
+
netPromoterScoreIsEnabled: boolean;
|
|
24
|
+
netPromoterScoreLabel?: string;
|
|
25
|
+
order: string[];
|
|
26
|
+
required?: string[];
|
|
27
|
+
respondToNegativeFeedback?: boolean;
|
|
28
|
+
respondToPositiveFeedback?: boolean;
|
|
29
|
+
anytimeSurveyIsEnabled?: boolean;
|
|
30
|
+
postChatSurveyIsEnabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare const DEFAULT_CSAT_ORDER: string[];
|
|
33
|
+
export type TranslationMap = Record<string, string>;
|
|
34
|
+
export interface StoredCsatFeedbackOption extends CsatFeedbackOption {
|
|
35
|
+
translations?: TranslationMap;
|
|
36
|
+
}
|
|
37
|
+
export interface CsatLabelTranslations {
|
|
38
|
+
satisfactionLabel?: TranslationMap;
|
|
39
|
+
followUpFeedbackPositiveLabel?: TranslationMap;
|
|
40
|
+
followUpFeedbackNegativeLabel?: TranslationMap;
|
|
41
|
+
resolutionLabel?: TranslationMap;
|
|
42
|
+
openFeedbackLabel?: TranslationMap;
|
|
43
|
+
openFeedbackHint?: TranslationMap;
|
|
44
|
+
customerEffortScoreLabel?: TranslationMap;
|
|
45
|
+
netPromoterScoreLabel?: TranslationMap;
|
|
46
|
+
}
|
|
47
|
+
export interface StoredCsatSurveySettings extends CsatSurveySettings {
|
|
48
|
+
followUpFeedbackPositiveOptions: StoredCsatFeedbackOption[];
|
|
49
|
+
followUpFeedbackNegativeOptions: StoredCsatFeedbackOption[];
|
|
50
|
+
labelTranslations?: CsatLabelTranslations;
|
|
51
|
+
}
|
|
52
|
+
/** Preserve API translation maps so already-fetched surveys follow language changes. */
|
|
53
|
+
export declare function parseCsatSettings(
|
|
54
|
+
data: unknown,
|
|
55
|
+
language?: string,
|
|
56
|
+
): StoredCsatSurveySettings | null;
|
|
57
|
+
/** Strip internal maps while resolving the public app contract for this render. */
|
|
58
|
+
export declare function localizeCsatSettings(
|
|
59
|
+
settings: StoredCsatSurveySettings | null,
|
|
60
|
+
language: string,
|
|
61
|
+
): CsatSurveySettings | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type FileUploadErrorReason =
|
|
2
|
+
| "delivery_unknown"
|
|
3
|
+
| "durability_failed"
|
|
4
|
+
| "durability_unknown"
|
|
5
|
+
| "handoff_ended"
|
|
6
|
+
| "http_400"
|
|
7
|
+
| "http_413"
|
|
8
|
+
| "http_415"
|
|
9
|
+
| "http_error"
|
|
10
|
+
| "integration_error"
|
|
11
|
+
| "integration_frame_error"
|
|
12
|
+
| "integration_frame_load_error"
|
|
13
|
+
| "integration_result_timeout"
|
|
14
|
+
| "invalid_context"
|
|
15
|
+
| "invalid_response"
|
|
16
|
+
| "message_send_failed"
|
|
17
|
+
| "missing_platform"
|
|
18
|
+
| "missing_transfer_data"
|
|
19
|
+
| "network_error"
|
|
20
|
+
| "provider_rejected"
|
|
21
|
+
| "timeout"
|
|
22
|
+
| "unsupported_platform"
|
|
23
|
+
| "upload_not_enabled";
|
|
24
|
+
/** Typed bridge contract for file-upload failures shown to the chatter. */
|
|
25
|
+
export interface FileUploadErrorData {
|
|
26
|
+
reason?: FileUploadErrorReason;
|
|
27
|
+
deliveryConfirmed?: boolean;
|
|
28
|
+
indeterminate?: boolean;
|
|
29
|
+
/** Core owns whether replaying the retained operation is safe and meaningful. */
|
|
30
|
+
retryable?: boolean;
|
|
31
|
+
maxFileSizeInMB?: number;
|
|
32
|
+
status?: number | string;
|
|
33
|
+
integrationErrorCode?: string;
|
|
34
|
+
platform?: string;
|
|
35
|
+
details?: Record<string, unknown>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed constants for all bridge state keys.
|
|
3
|
+
*
|
|
4
|
+
* Using these constants prevents silent typos — a misspelled string literal
|
|
5
|
+
* compiles without error but returns `undefined` at runtime.
|
|
6
|
+
*
|
|
7
|
+
* This is a catalogue for consumers of the package, not a list of keys this
|
|
8
|
+
* repo happens to use: most entries have no in-repo reader, and a new one
|
|
9
|
+
* having none is expected rather than dead code.
|
|
10
|
+
*
|
|
11
|
+
* The `satisfies` clause is what makes that true. This map is a third
|
|
12
|
+
* hand-maintained mirror of the state contract, and an unused constant never
|
|
13
|
+
* typechecks on its own — without the clause a value that no longer names a
|
|
14
|
+
* real `AppDisplayState` key would compile clean and only fail at the first
|
|
15
|
+
* read. It also keeps the literal types, so `StateKey` stays a union of the
|
|
16
|
+
* exact strings rather than widening to `string`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* import { STATE } from "@ada-cx/messaging-bridge";
|
|
20
|
+
* const botName = state?.[STATE.CONFIG_BOT_NAME] ?? "Ada";
|
|
21
|
+
*/
|
|
22
|
+
export declare const STATE: {
|
|
23
|
+
readonly AGENT_ACTIVE_AGENT: "agent.activeAgent";
|
|
24
|
+
readonly APP_LOADING: "app.loading";
|
|
25
|
+
readonly CHAT_CAN_LOAD_MORE: "chat.canLoadMore";
|
|
26
|
+
readonly CHAT_ERROR: "chat.error";
|
|
27
|
+
readonly CHAT_ERROR_SEQ: "chat.error.seq";
|
|
28
|
+
readonly CHAT_ERROR_CLIENT_KEY: "chat.error.clientKey";
|
|
29
|
+
readonly CHAT_IS_GENERATING: "chat.isGenerating";
|
|
30
|
+
readonly CHAT_IS_UNIFIED_REASONER_GENERATING: "chat.isUnifiedReasonerGenerating";
|
|
31
|
+
readonly CHAT_IS_SENDING: "chat.isSending";
|
|
32
|
+
readonly CHAT_MESSAGES: "chat.messages";
|
|
33
|
+
readonly CHAT_COMPOSER_TEXT: "chat.composerText";
|
|
34
|
+
readonly CHAT_ANSWERED_INTERACTIVE_IDS: "chat.answeredInteractiveIds";
|
|
35
|
+
readonly CHAT_PLAYBOOK_STEP_EXECUTING: "chat.playbookStepExecuting";
|
|
36
|
+
readonly CHATTER_ALERT_SOUND_ENABLED: "chatter.alertSoundEnabled";
|
|
37
|
+
readonly CHATTER_DEVICE_NOTIFICATIONS_ENABLED: "chatter.deviceNotificationsEnabled";
|
|
38
|
+
readonly CHATTER_LANGUAGE: "chatter.language";
|
|
39
|
+
readonly CHATTER_SMS_NOTIFICATIONS_ENABLED: "chatter.smsNotificationsEnabled";
|
|
40
|
+
readonly CHATTER_TRANSLATIONS: "chatter.translations";
|
|
41
|
+
readonly CONFIG_ADVANCED_COLORS_ENABLED: "config.advancedColorsEnabled";
|
|
42
|
+
readonly CONFIG_ALLOWED_PROTOCOLS: "config.allowedProtocols";
|
|
43
|
+
readonly CONFIG_BOT_AVATAR_URL: "config.botAvatarUrl";
|
|
44
|
+
readonly CONFIG_BOT_DESCRIPTION: "config.botDescription";
|
|
45
|
+
readonly CONFIG_BOT_DESCRIPTION_ENABLED: "config.botDescriptionEnabled";
|
|
46
|
+
readonly CONFIG_BOT_NAME: "config.botName";
|
|
47
|
+
readonly CONFIG_BUTTON_LAYOUT_DESKTOP: "config.buttonLayoutDesktop";
|
|
48
|
+
readonly CONFIG_BUTTON_LAYOUT_MOBILE: "config.buttonLayoutMobile";
|
|
49
|
+
readonly CONFIG_CHAT_ENABLED: "config.chatEnabled";
|
|
50
|
+
readonly CONFIG_CUSTOM_REDACTIONS: "config.customRedactions";
|
|
51
|
+
readonly CONFIG_DOWNLOAD_TRANSCRIPT_ENABLED: "config.downloadTranscriptEnabled";
|
|
52
|
+
readonly CONFIG_EMAIL_TRANSCRIPT_ENABLED: "config.emailTranscriptEnabled";
|
|
53
|
+
readonly CONFIG_FALLBACK_UI: "config.fallbackUi";
|
|
54
|
+
readonly CONFIG_FEATURES: "config.features";
|
|
55
|
+
readonly CONFIG_HANDLE: "config.handle";
|
|
56
|
+
readonly CONFIG_HEADER_COLOR: "config.headerColor";
|
|
57
|
+
readonly CONFIG_HEADER_COLOR_ENABLED: "config.headerColorEnabled";
|
|
58
|
+
readonly CONFIG_HEADER_TEXT_COLOR: "config.headerTextColor";
|
|
59
|
+
readonly CONFIG_IS_MOBILE_VIEWPORT: "config.isMobileViewport";
|
|
60
|
+
readonly CONFIG_PRIVACY_LINK: "config.privacyLink";
|
|
61
|
+
readonly CONFIG_PRIVACY: "config.privacy";
|
|
62
|
+
readonly CONFIG_SDK_PLATFORM: "config.sdkPlatform";
|
|
63
|
+
readonly CONFIG_SDK_SUPPORTS_DOWNLOAD_LINK: "config.sdkSupportsDownloadLink";
|
|
64
|
+
readonly CONFIG_SHOW_ALL_QUICK_REPLIES: "config.showAllQuickReplies";
|
|
65
|
+
readonly CONFIG_SHOW_BRANDING: "config.showBranding";
|
|
66
|
+
readonly CONFIG_ADA_PARTNER_NAME: "config.adaPartnerName";
|
|
67
|
+
readonly CONFIG_STYLE: "config.style";
|
|
68
|
+
readonly CONFIG_TEXT_OVER_ACCENT_COLOR: "config.textOverAccentColor";
|
|
69
|
+
readonly CONFIG_TEXT_SIZE: "config.textSize";
|
|
70
|
+
readonly CONFIG_THEME: "config.theme";
|
|
71
|
+
readonly CONFIG_TINT_COLOR: "config.tintColor";
|
|
72
|
+
readonly CONFIG_TRANSLATED_LANGUAGES: "config.translatedLanguages";
|
|
73
|
+
readonly CONFIG_CLIENT_DEFAULT_LANGUAGE: "config.clientDefaultLanguage";
|
|
74
|
+
readonly CONVERSATION_IN_STATE: "conversation.inState";
|
|
75
|
+
readonly CONVERSATION_STATE_DATA_CAPTURE: "conversation.stateData.capture";
|
|
76
|
+
readonly CONVERSATION_STATE_DATA_LIST_SELECTION: "conversation.stateData.listSelection";
|
|
77
|
+
readonly CSAT_AUTO_TRIGGER: "csat.autoTrigger";
|
|
78
|
+
readonly FILE_TRANSFER: "file.transfer";
|
|
79
|
+
readonly FILE_UPLOAD_ERROR: "file.uploadError";
|
|
80
|
+
readonly LIVE_AGENT_IN_LIVE_CHAT: "liveAgent.inLiveChat";
|
|
81
|
+
readonly TRANSCRIPT_DOWNLOAD_STATUS: "transcript.download.status";
|
|
82
|
+
readonly TRANSCRIPT_EMAIL_STATUS: "transcript.email.status";
|
|
83
|
+
readonly UI_FATAL_HANDOFF_DIALOG_CANCEL_FAILED: "ui.fatalHandoffDialog.cancelFailed";
|
|
84
|
+
readonly UI_FATAL_HANDOFF_DIALOG_PENDING: "ui.fatalHandoffDialog.pending";
|
|
85
|
+
readonly UI_RETRY_DIALOG_PENDING: "ui.retryDialog.pending";
|
|
86
|
+
readonly UI_SECRET_MESSAGE_BUTTON_ENABLED: "ui.secretMessage.buttonEnabled";
|
|
87
|
+
readonly UI_SECRET_MESSAGE_IS_OPEN: "ui.secretMessage.isOpen";
|
|
88
|
+
readonly UI_SHOW_RETRY_DIALOG: "ui.showRetryDialog";
|
|
89
|
+
readonly UI_TYPING_INDICATOR_VISIBLE: "ui.typingIndicator.visible";
|
|
90
|
+
readonly SECURITY_CHALLENGE_ENABLED: "security.challengeEnabled";
|
|
91
|
+
readonly SECURITY_CHALLENGE_ERROR: "security.challengeError";
|
|
92
|
+
readonly SECURITY_TURNSTILE_SITE_KEY: "security.turnstileSiteKey";
|
|
93
|
+
readonly SAML_LOCKED: "saml.locked";
|
|
94
|
+
readonly SAML_URL: "saml.url";
|
|
95
|
+
readonly SAML_LOGIN_BUTTON: "saml.loginButton";
|
|
96
|
+
readonly VOICE_AUDIO_LEVEL: "voice.audioLevel";
|
|
97
|
+
readonly VOICE_CURRENT_TRANSCRIPT: "voice.currentTranscript";
|
|
98
|
+
readonly VOICE_ENABLED: "voice.enabled";
|
|
99
|
+
readonly VOICE_ERROR: "voice.error";
|
|
100
|
+
readonly VOICE_IS_FINAL: "voice.isFinal";
|
|
101
|
+
readonly VOICE_IS_RECORDING: "voice.isRecording";
|
|
102
|
+
readonly VOICE_MODE: "voice.mode";
|
|
103
|
+
readonly VOICE_TTS_ENABLED: "voice.ttsEnabled";
|
|
104
|
+
};
|
|
105
|
+
export type StateKey = (typeof STATE)[keyof typeof STATE];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { BridgeClient } from "./bridge-client.js";
|
|
2
|
+
import type { AppDisplayState, AppEvents } from "./types.js";
|
|
3
|
+
/** One recorded `sendEvent` call. */
|
|
4
|
+
export interface SentBridgeEvent<K extends keyof AppEvents = keyof AppEvents> {
|
|
5
|
+
event: K;
|
|
6
|
+
payload: AppEvents[K] | undefined;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A scriptable, in-memory {@link BridgeClient} for unit tests.
|
|
10
|
+
*
|
|
11
|
+
* No postMessage, no origin checks, no network: tests drive state through
|
|
12
|
+
* `setState`/`updateState` and assert on the events the component under test
|
|
13
|
+
* sent through `sentEvents`. The `operations`, `subscribeKey`, and `select`
|
|
14
|
+
* surfaces are the REAL implementations running over this in-memory state:
|
|
15
|
+
* every operation records its events in `sentEvents`, and a correlated
|
|
16
|
+
* handle (`sendMessage().settled()`, `submitCsat().settled()`, ...) resolves
|
|
17
|
+
* when the test scripts the answering state transition via `updateState`.
|
|
18
|
+
*/
|
|
19
|
+
export interface MockBridgeClient extends BridgeClient {
|
|
20
|
+
/** Replace the state snapshot and notify subscribers. */
|
|
21
|
+
setState(state: AppDisplayState | null): void;
|
|
22
|
+
/**
|
|
23
|
+
* Shallow-merge a partial snapshot over the current state (or over an empty
|
|
24
|
+
* object when no state is set) and notify subscribers. Seeds
|
|
25
|
+
* `"chat.error.clientKey": null` when the key is absent — production core
|
|
26
|
+
* always publishes it (null when no keyed send owns the error), and
|
|
27
|
+
* `operations` send handles take the keyed correlation path only when it is
|
|
28
|
+
* present. Script a `chat.error` fixture with the send's `tempMessageUuid`
|
|
29
|
+
* as `chat.error.clientKey` to reject that handle; use `setState` to script
|
|
30
|
+
* an older core document without the key.
|
|
31
|
+
*/
|
|
32
|
+
updateState(partial: Partial<AppDisplayState>): void;
|
|
33
|
+
/**
|
|
34
|
+
* Every event sent through the client, in order. Each read returns a fresh
|
|
35
|
+
* copy, so a captured array survives `clearSentEvents()`.
|
|
36
|
+
*/
|
|
37
|
+
readonly sentEvents: readonly SentBridgeEvent[];
|
|
38
|
+
/** Forget recorded events without touching state or subscribers. */
|
|
39
|
+
clearSentEvents(): void;
|
|
40
|
+
/** Number of active subscribers. */
|
|
41
|
+
readonly subscriberCount: number;
|
|
42
|
+
/** True once `destroy()` has been called. */
|
|
43
|
+
readonly destroyed: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a {@link MockBridgeClient}, optionally seeded with an initial state
|
|
47
|
+
* snapshot. Mirrors the real client's teardown semantics: after `destroy()`,
|
|
48
|
+
* state is `null`, subscribers are cleared, `sendEvent` is a no-op, and every
|
|
49
|
+
* in-flight `operations` `settled()` promise rejects with a
|
|
50
|
+
* `BridgeClientDestroyedError`. Notification also mirrors the real client:
|
|
51
|
+
* a throwing subscriber is caught and logged, never aborting the pass.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createMockBridgeClient(initialState?: AppDisplayState | null): MockBridgeClient;
|