@sphoro/bugcatch-web-sdk 0.13.1 → 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/README.md +44 -74
- package/dist/bugcatch.es.js +1752 -1685
- package/dist/bugcatch.es.js.map +1 -1
- package/dist/bugcatch.umd.js +154 -110
- package/dist/bugcatch.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/drafts.js +32 -0
- package/src/index.d.ts +114 -75
- package/src/index.js +147 -164
- package/src/references.js +172 -0
- package/src/updates.js +45 -74
- package/src/widget.js +320 -105
- package/src/voice.js +0 -372
package/src/updates.js
CHANGED
|
@@ -297,32 +297,51 @@ export async function callAvailableForProject({ apiBase, apiKey, projectId }) {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
/**
|
|
300
|
-
* Which kind of call this project can take: '
|
|
300
|
+
* Which kind of call this project can take: 'live', or null for none.
|
|
301
301
|
*
|
|
302
302
|
* The same round trip as above, answering the question that actually matters.
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* deployment whose agent had been switched to person-to-person: pressing it filed
|
|
307
|
-
* the report and then failed the call, so the reporter got a ticket they had not
|
|
308
|
-
* asked for and nobody's dashboard rang.
|
|
303
|
+
* Asking only "can it call" is what drew a call button on a deployment that did
|
|
304
|
+
* not have one: pressing it filed the report and then failed the call, so the
|
|
305
|
+
* reporter got a ticket they had not asked for and nobody's dashboard rang.
|
|
309
306
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
307
|
+
* There is one kind of call now — the team's dashboards ring and a colleague
|
|
308
|
+
* picks up — so this has one non-null answer. The shape is kept because callers
|
|
309
|
+
* are written against it and because a deployment answering something else is a
|
|
310
|
+
* deployment this SDK must not place a call on.
|
|
313
311
|
*/
|
|
314
312
|
export async function callKindForProject({ apiBase, apiKey, projectId }) {
|
|
313
|
+
const kinds = await callKindsForProject({ apiBase, apiKey, projectId });
|
|
314
|
+
return kinds.live ? 'live' : null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Which kinds of call this project can take, as booleans.
|
|
319
|
+
*
|
|
320
|
+
* `agent` is always false and is still here because the API still sends it: it
|
|
321
|
+
* was a triage agent that answered instantly instead of a person, and it is
|
|
322
|
+
* gone. Reading it rather than dropping it keeps this honest about what the
|
|
323
|
+
* response actually said.
|
|
324
|
+
*
|
|
325
|
+
* An older API answers `available` and a bare `kind` with no `kinds` object.
|
|
326
|
+
* Anything that is not 'live' there is a kind this SDK can no longer place, so
|
|
327
|
+
* it reads as no call at all rather than as a call that would fail on connect.
|
|
328
|
+
*/
|
|
329
|
+
export async function callKindsForProject({ apiBase, apiKey, projectId }) {
|
|
330
|
+
const none = { agent: false, live: false };
|
|
315
331
|
try {
|
|
316
332
|
const res = await fetch(
|
|
317
333
|
`${apiBase}/ingest/calls?projectId=${encodeURIComponent(projectId)}`,
|
|
318
334
|
{ headers: { 'X-BugCatch-Key': apiKey } },
|
|
319
335
|
);
|
|
320
|
-
if (!res.ok) return
|
|
336
|
+
if (!res.ok) return none;
|
|
321
337
|
const body = await res.json();
|
|
322
|
-
if (!body?.available) return
|
|
323
|
-
|
|
338
|
+
if (!body?.available) return none;
|
|
339
|
+
if (body.kinds && typeof body.kinds === 'object') {
|
|
340
|
+
return { agent: false, live: Boolean(body.kinds.live) };
|
|
341
|
+
}
|
|
342
|
+
return { agent: false, live: body.kind === 'live' };
|
|
324
343
|
} catch {
|
|
325
|
-
return
|
|
344
|
+
return none;
|
|
326
345
|
}
|
|
327
346
|
}
|
|
328
347
|
|
|
@@ -369,7 +388,7 @@ export async function reportByCall({ apiBase, apiKey, token, payload }) {
|
|
|
369
388
|
}
|
|
370
389
|
|
|
371
390
|
/**
|
|
372
|
-
* Whether this report can be talked through with the
|
|
391
|
+
* Whether this report can be talked through with a person on the team.
|
|
373
392
|
*
|
|
374
393
|
* Asked before anything is rendered, so a button never appears on a deployment
|
|
375
394
|
* with no sphoro.voice configured, on a workspace that has not switched calling
|
|
@@ -377,65 +396,6 @@ export async function reportByCall({ apiBase, apiKey, token, payload }) {
|
|
|
377
396
|
* failure too: a call is an extra, and an extra that cannot be offered quietly
|
|
378
397
|
* is better than an error about a feature nobody asked for.
|
|
379
398
|
*/
|
|
380
|
-
export async function callAvailable({ apiBase, bugId }) {
|
|
381
|
-
const token = readToken();
|
|
382
|
-
if (!token) return false;
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
const res = await fetch(
|
|
386
|
-
`${apiBase}/ingest/watch/reports/${encodeURIComponent(bugId)}/voice-call`,
|
|
387
|
-
{ headers: { Authorization: `Bearer ${token}` } },
|
|
388
|
-
);
|
|
389
|
-
if (!res.ok) return false;
|
|
390
|
-
const body = await res.json();
|
|
391
|
-
return Boolean(body?.offered);
|
|
392
|
-
} catch {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Ask for a call, and get back the socket to hold it on.
|
|
399
|
-
*
|
|
400
|
-
* The response carries a URL with a short-lived credential in it and nothing
|
|
401
|
-
* else — no key ever reaches this page. Which is also why the URL is passed
|
|
402
|
-
* straight to the caller and never logged or stored.
|
|
403
|
-
*/
|
|
404
|
-
export async function requestCall({ apiBase, bugId }) {
|
|
405
|
-
const token = readToken();
|
|
406
|
-
if (!token) throw new Error('[BugCatch] nothing filed from this browser yet.');
|
|
407
|
-
|
|
408
|
-
const res = await fetch(
|
|
409
|
-
`${apiBase}/ingest/watch/reports/${encodeURIComponent(bugId)}/voice-call`,
|
|
410
|
-
{ method: 'POST', headers: { Authorization: `Bearer ${token}` } },
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
if (res.status === 401) {
|
|
414
|
-
clearToken();
|
|
415
|
-
throw new Error('[BugCatch] this browser can no longer prove it filed that report.');
|
|
416
|
-
}
|
|
417
|
-
if (!res.ok) {
|
|
418
|
-
// The API's own message is the one worth showing: "you have already talked
|
|
419
|
-
// this report through" is something the reader can act on, and a status
|
|
420
|
-
// code is not.
|
|
421
|
-
let detail = '';
|
|
422
|
-
try {
|
|
423
|
-
const problem = await res.json();
|
|
424
|
-
detail = Array.isArray(problem.message) ? problem.message.join(', ') : problem.message || '';
|
|
425
|
-
} catch { /* non-JSON error */ }
|
|
426
|
-
throw new Error(detail || `[BugCatch] the call could not be started (HTTP ${res.status}).`);
|
|
427
|
-
}
|
|
428
|
-
return res.json();
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Whether this report can be talked through with a *person*.
|
|
433
|
-
*
|
|
434
|
-
* A separate question from `callAvailable` and separately switched on, so the
|
|
435
|
-
* widget cannot infer either answer from the other: a workspace may run the
|
|
436
|
-
* triage agent, or a live line to its team, or both, or neither — and neither
|
|
437
|
-
* is the common case.
|
|
438
|
-
*/
|
|
439
399
|
export async function liveCallAvailable({ apiBase, bugId }) {
|
|
440
400
|
const token = readToken();
|
|
441
401
|
if (!token) return false;
|
|
@@ -1060,3 +1020,14 @@ async function readJoinError(res) {
|
|
|
1060
1020
|
} catch { /* non-JSON error */ }
|
|
1061
1021
|
return `Could not add you to this issue (HTTP ${res.status})`;
|
|
1062
1022
|
}
|
|
1023
|
+
|
|
1024
|
+
export async function verifyFix({ apiBase, bugId, ...feedback }) {
|
|
1025
|
+
const token = readToken();
|
|
1026
|
+
if (!token) throw new Error('This browser cannot access that report.');
|
|
1027
|
+
const res = await fetch(`${apiBase}/ingest/watch/reports/${encodeURIComponent(bugId)}/verification`, {
|
|
1028
|
+
method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify(feedback),
|
|
1029
|
+
});
|
|
1030
|
+
const data = await res.json().catch(() => ({}));
|
|
1031
|
+
if (!res.ok) throw new Error(data.message || 'Could not record feedback. Please refresh and try again.');
|
|
1032
|
+
return data;
|
|
1033
|
+
}
|