@brandfine/client 0.13.0 → 0.14.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/CHANGELOG.md +19 -0
- package/README.md +11 -0
- package/dist/index.cjs +196 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -6
- package/dist/index.d.ts +92 -6
- package/dist/index.js +196 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ type ChatMessage = {
|
|
|
23
23
|
body: string;
|
|
24
24
|
sender: ChatMessageSender;
|
|
25
25
|
createdAt: string;
|
|
26
|
+
/** Monotonic per-conversation sequence — the realtime resume
|
|
27
|
+
* cursor. Optional: older APIs don't send it. */
|
|
28
|
+
eventId?: number;
|
|
26
29
|
};
|
|
27
30
|
type ConversationStatus = 'OPEN' | 'CLOSED';
|
|
28
31
|
type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR';
|
|
@@ -38,6 +41,10 @@ type LiveChatRuntimeConfig = {
|
|
|
38
41
|
offlineMessage: string | null;
|
|
39
42
|
theme: Record<string, string> | null;
|
|
40
43
|
online?: boolean;
|
|
44
|
+
/** Realtime endpoint (wss://…). Absent = poll (older API, or
|
|
45
|
+
* the transport is off). The session upgrades automatically
|
|
46
|
+
* when present; consumers never touch it. */
|
|
47
|
+
realtimeUrl?: string;
|
|
41
48
|
};
|
|
42
49
|
type LiveChatSessionSnapshot = {
|
|
43
50
|
messages: ChatMessage[];
|
|
@@ -64,10 +71,12 @@ type StartConversationResult = {
|
|
|
64
71
|
};
|
|
65
72
|
type LiveChatCreateSessionOptions = {
|
|
66
73
|
/** The server-half bootstrap (same object `install()` takes) —
|
|
67
|
-
* supplies the publishable key
|
|
74
|
+
* supplies the publishable key (and, when the API advertises it,
|
|
75
|
+
* the realtime endpoint — passed through wholesale). */
|
|
68
76
|
config: {
|
|
69
77
|
enabled: boolean;
|
|
70
78
|
publishableKey?: string;
|
|
79
|
+
realtimeUrl?: string;
|
|
71
80
|
};
|
|
72
81
|
/** Signed identity — SAME shape and rules as `install()`. A bad
|
|
73
82
|
* signature downgrades to anonymous server-side; it is never a
|
|
@@ -259,6 +268,14 @@ type CreateAppointmentRequestInput = {
|
|
|
259
268
|
requestedAt: string;
|
|
260
269
|
/** Optional cookie-derived session id from the consumer site. */
|
|
261
270
|
visitorSessionId?: string;
|
|
271
|
+
/** Signed host-app identity — the SAME payload shape and signature
|
|
272
|
+
* Live Chat accepts, so one `liveChat.identityToken(externalId)`
|
|
273
|
+
* call signs for both plugins. When present and valid, the booking
|
|
274
|
+
* is attributed to the person (verified name/email take precedence
|
|
275
|
+
* over the free-text fields above, and the booking links to their
|
|
276
|
+
* chat threads via the shared externalId). Invalid or absent →
|
|
277
|
+
* the booking proceeds anonymously — it is never rejected. */
|
|
278
|
+
visitor?: VerifiedVisitor;
|
|
262
279
|
};
|
|
263
280
|
type CreatedAppointmentRequest = {
|
|
264
281
|
id: string;
|
|
@@ -266,11 +283,40 @@ type CreatedAppointmentRequest = {
|
|
|
266
283
|
requestedAt: string;
|
|
267
284
|
durationMinutes: number;
|
|
268
285
|
status: 'PENDING';
|
|
286
|
+
/** True iff `visitor.identityToken` HMAC-verified — your signal
|
|
287
|
+
* that the booking landed attributed rather than anonymous. */
|
|
288
|
+
identityVerified: boolean;
|
|
269
289
|
/** Visitor's self-cancel token. Embed it in confirmation
|
|
270
290
|
* emails / on-page UI so the visitor can cancel without an
|
|
271
291
|
* account. One-time use; revoked once any party acts. */
|
|
272
292
|
cancellationToken: string | null;
|
|
273
293
|
};
|
|
294
|
+
type AppointmentStatus = 'PENDING' | 'CONFIRMED' | 'REJECTED' | 'CANCELLED';
|
|
295
|
+
/** One appointment as the identity-scoped read-back returns it —
|
|
296
|
+
* the visitor-safe projection ("where does my request stand?"). */
|
|
297
|
+
type Appointment = {
|
|
298
|
+
id: string;
|
|
299
|
+
status: AppointmentStatus;
|
|
300
|
+
/** UTC ISO 8601 of the requested slot start (reflects the current
|
|
301
|
+
* slot after a reschedule). */
|
|
302
|
+
requestedAt: string;
|
|
303
|
+
/** The confirmed slot — non-null only once CONFIRMED. */
|
|
304
|
+
scheduledAt: string | null;
|
|
305
|
+
durationMinutes: number;
|
|
306
|
+
/** Workspace's IANA timezone for local rendering. */
|
|
307
|
+
timezone: string;
|
|
308
|
+
/** Customer's note — populated only on REJECTED. */
|
|
309
|
+
declineReason: string | null;
|
|
310
|
+
rescheduleCount: number;
|
|
311
|
+
respondedAt: string | null;
|
|
312
|
+
createdAt: string;
|
|
313
|
+
};
|
|
314
|
+
/** Proof of identity for read-back calls: the same externalId +
|
|
315
|
+
* `liveChat.identityToken(externalId)` pair used when booking. */
|
|
316
|
+
type AppointmentIdentity = {
|
|
317
|
+
externalId: string;
|
|
318
|
+
identityToken: string;
|
|
319
|
+
};
|
|
274
320
|
type AppointmentsApi = {
|
|
275
321
|
/**
|
|
276
322
|
* Available slots for the workspace's booking window.
|
|
@@ -286,12 +332,37 @@ type AppointmentsApi = {
|
|
|
286
332
|
* the slot is still bookable; if it isn't, throws
|
|
287
333
|
* `BrandfineApiError` with status 404 / 409.
|
|
288
334
|
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
335
|
+
* Pass `visitor` (signed with `liveChat.identityToken()`) to book
|
|
336
|
+
* as a known person — that unlocks `list`/`get` read-back below.
|
|
337
|
+
* Anonymous bookings remain fully supported; their status flow
|
|
338
|
+
* stays email-driven via the cancellation-token link.
|
|
293
339
|
*/
|
|
294
340
|
createRequest: (input: CreateAppointmentRequestInput) => Promise<CreatedAppointmentRequest>;
|
|
341
|
+
/**
|
|
342
|
+
* Every appointment belonging to the verified person, newest
|
|
343
|
+
* first. Requires a valid identity signature — throws
|
|
344
|
+
* `BrandfineApiError` 401 on a bad one (reads need proof; there
|
|
345
|
+
* is no anonymous downgrade for reading history). Server-side
|
|
346
|
+
* only, like `identityToken()` itself.
|
|
347
|
+
*/
|
|
348
|
+
list: (identity: AppointmentIdentity) => Promise<Appointment[]>;
|
|
349
|
+
/**
|
|
350
|
+
* One appointment by id, identity-scoped. 404s when the id does
|
|
351
|
+
* not exist OR belongs to someone else — indistinguishable by
|
|
352
|
+
* design.
|
|
353
|
+
*/
|
|
354
|
+
get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>;
|
|
355
|
+
/**
|
|
356
|
+
* Spend the one-time `cancellationToken` from `createRequest` to
|
|
357
|
+
* cancel a still-PENDING request. The token IS the credential —
|
|
358
|
+
* no id or API key needed. Throws `BrandfineApiError` 400 when
|
|
359
|
+
* the request is no longer PENDING, 404 when the token is
|
|
360
|
+
* unknown/already spent.
|
|
361
|
+
*/
|
|
362
|
+
cancel: (cancellationToken: string) => Promise<{
|
|
363
|
+
id: string;
|
|
364
|
+
status: 'CANCELLED';
|
|
365
|
+
}>;
|
|
295
366
|
};
|
|
296
367
|
type AnalyticsConfig = {
|
|
297
368
|
enabled: false;
|
|
@@ -463,6 +534,12 @@ type LiveChatBootstrap = {
|
|
|
463
534
|
theme: Record<string, string> | null;
|
|
464
535
|
/** Widget bundle path relative to the API base URL. */
|
|
465
536
|
scriptPath: string;
|
|
537
|
+
/** Realtime endpoint (wss://…). OPTIONAL by design — this is
|
|
538
|
+
* what makes the upgrade non-breaking: an older SDK ignores
|
|
539
|
+
* it, a newer SDK against an older API sees it absent and
|
|
540
|
+
* polls. Consumers pass `config` through wholesale, so it
|
|
541
|
+
* reaches the browser with no change on their side. */
|
|
542
|
+
realtimeUrl?: string;
|
|
466
543
|
};
|
|
467
544
|
type LiveChatInstallResult = {
|
|
468
545
|
installed: false;
|
|
@@ -478,6 +555,13 @@ type LiveChatInstallResult = {
|
|
|
478
555
|
* the Brandfine API verifies the signature. An invalid or missing
|
|
479
556
|
* token silently downgrades the conversation to anonymous.
|
|
480
557
|
*/
|
|
558
|
+
/**
|
|
559
|
+
* A signed host-app identity payload. Named per-plugin below for
|
|
560
|
+
* discoverability, but it is ONE shape signed ONE way: a token from
|
|
561
|
+
* `liveChat.identityToken(externalId)` is accepted by Live Chat AND
|
|
562
|
+
* Appointments (`createRequest.visitor`, `list`/`get`).
|
|
563
|
+
*/
|
|
564
|
+
type VerifiedVisitor = LiveChatVisitor;
|
|
481
565
|
type LiveChatVisitor = {
|
|
482
566
|
/** Your app's stable id for this person (user id, lead reference…).
|
|
483
567
|
* Conversations sharing an externalId are the same person across
|
|
@@ -588,9 +672,11 @@ type LiveChatApi = {
|
|
|
588
672
|
}) => Promise<StartConversationResult>;
|
|
589
673
|
sendMessage: (conversationToken: string, input: {
|
|
590
674
|
body: string;
|
|
675
|
+
clientId?: string;
|
|
591
676
|
}) => Promise<ChatMessage>;
|
|
592
677
|
history: (conversationToken: string, opts?: {
|
|
593
678
|
after?: string;
|
|
679
|
+
afterEvent?: number;
|
|
594
680
|
}) => Promise<{
|
|
595
681
|
messages: ChatMessage[];
|
|
596
682
|
status: ConversationStatus;
|
|
@@ -612,4 +698,4 @@ declare function createBrandfineClient(config: BrandfineClientConfig): Brandfine
|
|
|
612
698
|
*/
|
|
613
699
|
declare const SDK_VERSION: "0.0.0";
|
|
614
700
|
|
|
615
|
-
export { type AnalyticsConfig, type AnalyticsInstallResult, type AnalyticsOverview, type AnalyticsOverviewRange, BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, type ChatMessage, type ChatMessageSender, type ConversationStatus, type CreateSubmissionInput, type InstallOptions, ListCategoriesOptions, ListPostsOptions, type LiveChatBootstrap, type LiveChatCreateSessionOptions, type LiveChatInstallOptions, type LiveChatInstallResult, type LiveChatRuntimeConfig, type LiveChatSession, type LiveChatSessionSnapshot, type LiveChatSessionState, type LiveChatVisitor, SDK_VERSION, type StartConversationResult, type Submission, createBrandfineClient };
|
|
701
|
+
export { type AnalyticsConfig, type AnalyticsInstallResult, type AnalyticsOverview, type AnalyticsOverviewRange, type Appointment, type AppointmentAvailability, type AppointmentIdentity, type AppointmentSlot, type AppointmentStatus, BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, type ChatMessage, type ChatMessageSender, type ConversationStatus, type CreateAppointmentRequestInput, type CreateSubmissionInput, type CreatedAppointmentRequest, type InstallOptions, ListCategoriesOptions, ListPostsOptions, type LiveChatBootstrap, type LiveChatCreateSessionOptions, type LiveChatInstallOptions, type LiveChatInstallResult, type LiveChatRuntimeConfig, type LiveChatSession, type LiveChatSessionSnapshot, type LiveChatSessionState, type LiveChatVisitor, SDK_VERSION, type StartConversationResult, type Submission, type VerifiedVisitor, createBrandfineClient };
|
package/dist/index.js
CHANGED
|
@@ -60,34 +60,63 @@ async function createLiveChatSession(wire, opts) {
|
|
|
60
60
|
const listeners = /* @__PURE__ */ new Set();
|
|
61
61
|
const seen = /* @__PURE__ */ new Set();
|
|
62
62
|
let cursor;
|
|
63
|
+
let lastEventId = 0;
|
|
63
64
|
let token;
|
|
64
65
|
let timer;
|
|
65
66
|
let closed = false;
|
|
66
67
|
let ticking = false;
|
|
67
68
|
const aborter = new AbortController();
|
|
69
|
+
let realtimeUrl = opts.config.realtimeUrl;
|
|
70
|
+
let ws;
|
|
71
|
+
let wsLive = false;
|
|
72
|
+
let wsFailures = 0;
|
|
73
|
+
let wsGivenUp = false;
|
|
74
|
+
let reconnectTimer;
|
|
75
|
+
let pingTimer;
|
|
68
76
|
function emit(patch) {
|
|
69
77
|
snapshot = { ...snapshot, ...patch };
|
|
70
78
|
for (const listener of listeners) listener(snapshot);
|
|
71
79
|
}
|
|
72
80
|
function appendMessages(incoming) {
|
|
73
81
|
const fresh = incoming.filter((m) => !seen.has(m.id));
|
|
74
|
-
if (fresh.length === 0)
|
|
75
|
-
|
|
82
|
+
if (fresh.length === 0) {
|
|
83
|
+
for (const m of incoming) {
|
|
84
|
+
if (typeof m.eventId === "number" && m.eventId > lastEventId) {
|
|
85
|
+
lastEventId = m.eventId;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
for (const m of fresh) {
|
|
91
|
+
seen.add(m.id);
|
|
92
|
+
if (typeof m.eventId === "number" && m.eventId > lastEventId) {
|
|
93
|
+
lastEventId = m.eventId;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
76
96
|
const messages = [...snapshot.messages, ...fresh];
|
|
77
97
|
cursor = messages[messages.length - 1].createdAt;
|
|
78
98
|
emit({ messages });
|
|
79
99
|
}
|
|
100
|
+
function markClosed() {
|
|
101
|
+
if (snapshot.status !== "CLOSED") emit({ status: "CLOSED" });
|
|
102
|
+
stopPolling();
|
|
103
|
+
teardownRealtime();
|
|
104
|
+
wsGivenUp = true;
|
|
105
|
+
}
|
|
80
106
|
async function tick() {
|
|
81
107
|
if (ticking || closed || !token) return;
|
|
82
108
|
ticking = true;
|
|
83
109
|
try {
|
|
84
|
-
const result = await wire.history(
|
|
110
|
+
const result = await wire.history(
|
|
111
|
+
token,
|
|
112
|
+
// The eventId cursor is exact (no equal-millisecond drop
|
|
113
|
+
// window); the timestamp cursor remains for old APIs that
|
|
114
|
+
// never sent eventIds.
|
|
115
|
+
lastEventId > 0 ? { afterEvent: lastEventId } : { after: cursor }
|
|
116
|
+
);
|
|
85
117
|
if (closed) return;
|
|
86
118
|
appendMessages(result.messages);
|
|
87
|
-
if (result.status === "CLOSED"
|
|
88
|
-
emit({ status: "CLOSED" });
|
|
89
|
-
stopPolling();
|
|
90
|
-
}
|
|
119
|
+
if (result.status === "CLOSED") markClosed();
|
|
91
120
|
} catch {
|
|
92
121
|
} finally {
|
|
93
122
|
ticking = false;
|
|
@@ -101,6 +130,119 @@ async function createLiveChatSession(wire, opts) {
|
|
|
101
130
|
if (timer !== void 0) clearInterval(timer);
|
|
102
131
|
timer = void 0;
|
|
103
132
|
}
|
|
133
|
+
function teardownRealtime() {
|
|
134
|
+
if (reconnectTimer !== void 0) clearTimeout(reconnectTimer);
|
|
135
|
+
reconnectTimer = void 0;
|
|
136
|
+
if (pingTimer !== void 0) clearInterval(pingTimer);
|
|
137
|
+
pingTimer = void 0;
|
|
138
|
+
wsLive = false;
|
|
139
|
+
if (ws) {
|
|
140
|
+
const socket = ws;
|
|
141
|
+
ws = void 0;
|
|
142
|
+
try {
|
|
143
|
+
socket.onopen = socket.onmessage = socket.onclose = socket.onerror = null;
|
|
144
|
+
socket.close();
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function scheduleReconnect() {
|
|
150
|
+
if (closed || wsGivenUp || reconnectTimer !== void 0) return;
|
|
151
|
+
if (wsFailures >= 5) {
|
|
152
|
+
wsGivenUp = true;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const base = Math.min(3e4, 1e3 * 2 ** wsFailures);
|
|
156
|
+
const delay = base / 2 + Math.random() * (base / 2);
|
|
157
|
+
reconnectTimer = setTimeout(() => {
|
|
158
|
+
reconnectTimer = void 0;
|
|
159
|
+
startRealtime();
|
|
160
|
+
}, delay);
|
|
161
|
+
}
|
|
162
|
+
function handleSocketDown() {
|
|
163
|
+
teardownRealtime();
|
|
164
|
+
if (closed || wsGivenUp || snapshot.status === "CLOSED") return;
|
|
165
|
+
startPolling();
|
|
166
|
+
wsFailures += 1;
|
|
167
|
+
scheduleReconnect();
|
|
168
|
+
}
|
|
169
|
+
function startRealtime() {
|
|
170
|
+
if (closed || wsGivenUp || wsLive || ws !== void 0 || !realtimeUrl || !token || !publishableKey || typeof WebSocket === "undefined" || snapshot.status === "CLOSED") {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
let socket;
|
|
174
|
+
try {
|
|
175
|
+
socket = new WebSocket(realtimeUrl);
|
|
176
|
+
} catch {
|
|
177
|
+
wsFailures += 1;
|
|
178
|
+
scheduleReconnect();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
ws = socket;
|
|
182
|
+
socket.onopen = () => {
|
|
183
|
+
try {
|
|
184
|
+
socket.send(
|
|
185
|
+
JSON.stringify({
|
|
186
|
+
type: "auth",
|
|
187
|
+
publishableKey,
|
|
188
|
+
conversationToken: token,
|
|
189
|
+
lastEventId
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
} catch {
|
|
193
|
+
handleSocketDown();
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
socket.onmessage = (event) => {
|
|
197
|
+
let frame;
|
|
198
|
+
try {
|
|
199
|
+
frame = JSON.parse(String(event.data));
|
|
200
|
+
} catch {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
switch (frame.type) {
|
|
204
|
+
case "ready": {
|
|
205
|
+
wsLive = true;
|
|
206
|
+
wsFailures = 0;
|
|
207
|
+
stopPolling();
|
|
208
|
+
if (pingTimer === void 0) {
|
|
209
|
+
pingTimer = setInterval(() => {
|
|
210
|
+
try {
|
|
211
|
+
socket.send(JSON.stringify({ type: "ping" }));
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}, 3e4);
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "message": {
|
|
219
|
+
const m = frame.message;
|
|
220
|
+
if (m && typeof m.id === "string") {
|
|
221
|
+
appendMessages([
|
|
222
|
+
{
|
|
223
|
+
...m,
|
|
224
|
+
eventId: typeof frame.eventId === "number" ? frame.eventId : m.eventId
|
|
225
|
+
}
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "status": {
|
|
231
|
+
if (frame.status === "CLOSED") markClosed();
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "error": {
|
|
235
|
+
wsGivenUp = true;
|
|
236
|
+
teardownRealtime();
|
|
237
|
+
if (!closed && snapshot.status !== "CLOSED") startPolling();
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
socket.onclose = () => handleSocketDown();
|
|
243
|
+
socket.onerror = () => {
|
|
244
|
+
};
|
|
245
|
+
}
|
|
104
246
|
async function connect(resumeToken) {
|
|
105
247
|
if (!publishableKey) {
|
|
106
248
|
emit({ status: "CLOSED" });
|
|
@@ -118,6 +260,7 @@ async function createLiveChatSession(wire, opts) {
|
|
|
118
260
|
offlineMessage: cfg.offlineMessage,
|
|
119
261
|
online: cfg.online !== false
|
|
120
262
|
});
|
|
263
|
+
if (cfg.realtimeUrl !== void 0) realtimeUrl = cfg.realtimeUrl;
|
|
121
264
|
} catch (error) {
|
|
122
265
|
if (closed) return;
|
|
123
266
|
emit({ status: "ERROR", error });
|
|
@@ -142,7 +285,10 @@ async function createLiveChatSession(wire, opts) {
|
|
|
142
285
|
if (started.online !== void 0) emit({ online: started.online });
|
|
143
286
|
emit({ status: "OPEN" });
|
|
144
287
|
await tick();
|
|
145
|
-
if (!closed)
|
|
288
|
+
if (!closed) {
|
|
289
|
+
startPolling();
|
|
290
|
+
startRealtime();
|
|
291
|
+
}
|
|
146
292
|
} catch (error) {
|
|
147
293
|
if (closed) return;
|
|
148
294
|
emit({ status: "ERROR", error });
|
|
@@ -169,7 +315,11 @@ async function createLiveChatSession(wire, opts) {
|
|
|
169
315
|
}
|
|
170
316
|
emit({ sending: true });
|
|
171
317
|
try {
|
|
172
|
-
const
|
|
318
|
+
const clientId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
|
|
319
|
+
const message = await wire.sendMessage(token, {
|
|
320
|
+
body: trimmed,
|
|
321
|
+
clientId
|
|
322
|
+
});
|
|
173
323
|
appendMessages([message]);
|
|
174
324
|
return message;
|
|
175
325
|
} finally {
|
|
@@ -180,13 +330,18 @@ async function createLiveChatSession(wire, opts) {
|
|
|
180
330
|
if (closed) return;
|
|
181
331
|
closed = true;
|
|
182
332
|
stopPolling();
|
|
333
|
+
teardownRealtime();
|
|
183
334
|
aborter.abort();
|
|
184
335
|
listeners.clear();
|
|
185
336
|
},
|
|
186
337
|
async reset() {
|
|
338
|
+
teardownRealtime();
|
|
339
|
+
wsFailures = 0;
|
|
340
|
+
wsGivenUp = false;
|
|
187
341
|
if (publishableKey) writeStoredToken(publishableKey, null);
|
|
188
342
|
token = void 0;
|
|
189
343
|
cursor = void 0;
|
|
344
|
+
lastEventId = 0;
|
|
190
345
|
seen.clear();
|
|
191
346
|
stopPolling();
|
|
192
347
|
emit({ messages: [], status: "CONNECTING", error: null });
|
|
@@ -462,7 +617,7 @@ function createBrandfineClient(config) {
|
|
|
462
617
|
history: (conversationToken, o = {}) => liveChatWireRequest(
|
|
463
618
|
void 0,
|
|
464
619
|
"GET",
|
|
465
|
-
`/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${o.after ? `?after=${encodeURIComponent(o.after)}` : ""}`
|
|
620
|
+
`/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${o.afterEvent !== void 0 ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}` : o.after ? `?after=${encodeURIComponent(o.after)}` : ""}`
|
|
466
621
|
)
|
|
467
622
|
};
|
|
468
623
|
return createLiveChatSession(wire, opts);
|
|
@@ -486,10 +641,11 @@ function createBrandfineClient(config) {
|
|
|
486
641
|
);
|
|
487
642
|
},
|
|
488
643
|
history(conversationToken, o = {}) {
|
|
644
|
+
const qs = o.afterEvent !== void 0 ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}` : o.after ? `?after=${encodeURIComponent(o.after)}` : "";
|
|
489
645
|
return liveChatWireRequest(
|
|
490
646
|
void 0,
|
|
491
647
|
"GET",
|
|
492
|
-
`/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${
|
|
648
|
+
`/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${qs}`
|
|
493
649
|
);
|
|
494
650
|
},
|
|
495
651
|
async identityToken(externalId, opts = {}) {
|
|
@@ -571,6 +727,35 @@ function createBrandfineClient(config) {
|
|
|
571
727
|
"/external/appointments/requests",
|
|
572
728
|
input
|
|
573
729
|
);
|
|
730
|
+
},
|
|
731
|
+
async list(identity) {
|
|
732
|
+
const res = await post(
|
|
733
|
+
"/external/appointments/requests/lookup",
|
|
734
|
+
identity
|
|
735
|
+
);
|
|
736
|
+
return res.appointments;
|
|
737
|
+
},
|
|
738
|
+
async get(id, identity) {
|
|
739
|
+
const res = await post(
|
|
740
|
+
"/external/appointments/requests/lookup",
|
|
741
|
+
{ ...identity, id }
|
|
742
|
+
);
|
|
743
|
+
const row = res.appointments[0];
|
|
744
|
+
if (!row) {
|
|
745
|
+
throw new BrandfineApiError({
|
|
746
|
+
status: 404,
|
|
747
|
+
statusText: "Not Found",
|
|
748
|
+
body: "Appointment not found.",
|
|
749
|
+
url: "/external/appointments/requests/lookup"
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
return row;
|
|
753
|
+
},
|
|
754
|
+
cancel(cancellationToken) {
|
|
755
|
+
return post(
|
|
756
|
+
`/external/appointments/requests/${encodeURIComponent(cancellationToken)}/cancel`,
|
|
757
|
+
{}
|
|
758
|
+
);
|
|
574
759
|
}
|
|
575
760
|
};
|
|
576
761
|
return {
|