@sphoro/bugcatch-web-sdk 0.13.1

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/src/index.d.ts ADDED
@@ -0,0 +1,684 @@
1
+ /**
2
+ * Who is using your app. `email` is the one field with behaviour attached —
3
+ * see identify().
4
+ */
5
+ export interface BugCatchUser { id?: string; email?: string; name?: string; }
6
+
7
+ /**
8
+ * What a bug *report* may carry. Narrower than what may be uploaded: a report's
9
+ * attachments are typed SCREENSHOT or VIDEO, so a document has no honest place
10
+ * on one — it belongs on a message, where it keeps its own type.
11
+ */
12
+ export type AttachmentMime =
13
+ | 'image/png' | 'image/jpeg' | 'image/webp' | 'video/webm' | 'video/mp4';
14
+
15
+ /** What a *message* may carry: the above, plus voice notes and documents. */
16
+ export type MessageMime =
17
+ | AttachmentMime | 'image/gif'
18
+ | 'audio/webm' | 'audio/ogg' | 'audio/mpeg' | 'audio/mp4' | 'audio/wav'
19
+ | 'application/pdf' | 'text/plain' | 'text/csv' | 'application/json'
20
+ | 'application/zip' | 'application/msword'
21
+ | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
22
+ | 'application/vnd.ms-excel'
23
+ | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
24
+
25
+ /**
26
+ * A report this project has chosen to say is already known, as a reporter may
27
+ * read it.
28
+ *
29
+ * Four fields, and the absences are the design. No severity: how bad the team
30
+ * thinks something is, published to everyone who opens the widget, is an
31
+ * internal judgement that reads as a promise. No assignee, no description, no
32
+ * analysis. The question this answers is "is this the thing I came here about".
33
+ */
34
+ export interface KnownIssue {
35
+ id: string;
36
+ title: string;
37
+ status: 'OPEN' | 'IN_PROGRESS';
38
+ /** How many people have said it is happening to them. Never who. */
39
+ affectedCount: number;
40
+ /** When it was first reported, ISO-8601. */
41
+ since: string;
42
+ /** Whether the person asking has already joined this one. */
43
+ joined: boolean;
44
+ }
45
+
46
+ /** What came back from saying an issue is happening to you too. */
47
+ export interface JoinedIssue {
48
+ /** False when this browser had already joined — a second press, not a second person. */
49
+ joined: boolean;
50
+ affectedCount: number;
51
+ /** Held by the SDK, and what lets this browser hear that the bug was fixed. */
52
+ watchToken?: string | null;
53
+ }
54
+
55
+ export interface BugCatchConfig {
56
+ apiBase?: string;
57
+ apiKey: string;
58
+ projectId: string;
59
+ consoleLimit?: number;
60
+ networkLimit?: number;
61
+ captureConsole?: boolean;
62
+ captureNetwork?: boolean;
63
+ captureErrors?: boolean;
64
+ widget?: boolean;
65
+ widgetPosition?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
66
+ /** 'auto' follows the visitor's system setting. Default 'auto'. */
67
+ theme?: 'auto' | 'light' | 'dark';
68
+ /** Any CSS colour. Default '#4F46E5'. */
69
+ accent?: string;
70
+ /** Offer screenshot / recording / file attach in the widget. Default true. */
71
+ attachments?: boolean;
72
+ /**
73
+ * Show what this project already knows is broken before the report form, and
74
+ * offer "this is happening to me too" instead of a second report. Default
75
+ * true — but the list is empty, and nothing is drawn, until the project turns
76
+ * the feature on and somebody lists an individual report.
77
+ */
78
+ knownIssues?: boolean;
79
+ /** Default 5. */
80
+ maxAttachments?: number;
81
+ /** Default 25 MB. */
82
+ maxAttachmentBytes?: number;
83
+ /** Hard cap on a single recording. Default 120000 (2 minutes). */
84
+ maxRecordingMs?: number;
85
+ /** Return false to cancel the report, or a payload object to replace it. */
86
+ beforeSend?: (payload: Record<string, any>) => Record<string, any> | false | void;
87
+ user?: BugCatchUser | null;
88
+ /**
89
+ * Tell the reporter, live and in your app, when a bug they filed changes
90
+ * status. Only ever connects once this browser has actually filed one.
91
+ * Default true.
92
+ */
93
+ updates?: boolean;
94
+ /**
95
+ * Show the built-in notification on an update. Default true.
96
+ *
97
+ * It stays up until the reporter marks it read, across a reload included —
98
+ * nothing here is on a timer, because the point of telling somebody their bug
99
+ * was fixed is that they find out. Turn it off to render your own, and mark
100
+ * them read with `markUpdateRead`.
101
+ */
102
+ updateToast?: boolean;
103
+ /** Called on every update, whether or not the built-in notification is shown. */
104
+ onUpdate?: ((update: ReportUpdate) => void) | null;
105
+ /**
106
+ * Calling, in both directions, where the workspace has sphoro.voice
107
+ * configured. Default true.
108
+ *
109
+ * Outgoing: a call is offered when a report is picked up. Incoming: a card
110
+ * appears when somebody on the team rings about a report filed from this
111
+ * browser. Neither dials anything and neither asks for a microphone until the
112
+ * person presses the button — see `startCall` and `onIncomingCall`.
113
+ *
114
+ * `false` switches off both. A host application that does not want its
115
+ * customers rung sets it, and no card is ever drawn.
116
+ */
117
+ voice?: boolean;
118
+ /**
119
+ * Called as a call of either kind moves between states. `detail.live` marks
120
+ * the ones about a call to a person; their states come from LiveCallState.
121
+ */
122
+ onCallState?: ((
123
+ state: CallState | LiveCallState,
124
+ detail: { bugId: string; live?: boolean; reason?: string },
125
+ ) => void) | null;
126
+ /** Called with each line of a call, as it is recognised. */
127
+ onCallTranscript?: ((line: CallTranscriptLine) => void) | null;
128
+ /**
129
+ * Called when somebody on the team rings this page about a report filed from
130
+ * it — the one call this SDK does not start.
131
+ *
132
+ * The card is already on screen by the time this runs. It is for a host
133
+ * application that wants to duck its own audio or pause a video, not a hook
134
+ * that can suppress a call the person can see ringing. `voice: false`
135
+ * switches the whole thing off, card included.
136
+ */
137
+ onIncomingCall?: ((call: IncomingCall) => void) | null;
138
+ }
139
+
140
+ /**
141
+ * A member of the team, ringing this page.
142
+ *
143
+ * Everything on it is what somebody has about five seconds to read before
144
+ * deciding whether to pick up. `recorded` says so before Answer rather than
145
+ * after, which is the only honest moment: on a peer-to-peer call the copy that
146
+ * gets kept is uplinked by this browser, so consent that arrives afterwards is
147
+ * consent to something that already happened.
148
+ */
149
+ export interface IncomingCall {
150
+ bugId: string;
151
+ callId: string;
152
+ /** Who is calling, by name. */
153
+ from: string;
154
+ /** Which product the report was filed against. */
155
+ project: string;
156
+ /** The report's short reference. */
157
+ reference: string;
158
+ /** When the ringing stops, ISO-8601. */
159
+ ringsUntil: string;
160
+ /** Whether this call would be recorded or transcribed. */
161
+ recorded: boolean;
162
+ }
163
+
164
+ /**
165
+ * What happened to a call the team placed to this page.
166
+ *
167
+ * `ringing` when the card appears, then exactly one of the rest. `missed` means
168
+ * nobody pressed anything before the deadline; `declined` means they did.
169
+ */
170
+ export type IncomingCallState =
171
+ 'ringing' | 'answered' | 'declined' | 'missed' | 'failed';
172
+
173
+ /**
174
+ * Where a call is. `listening` means the reporter's turn, `speaking` the
175
+ * agent's; `ended` and `failed` are both final and release the microphone.
176
+ */
177
+ export type CallState = 'connecting' | 'listening' | 'speaking' | 'ended' | 'failed';
178
+
179
+ /**
180
+ * Where a *live* call is — one to a person on the team rather than to the
181
+ * agent. A separate vocabulary because the two have almost nothing in common:
182
+ * an agent call is never `ringing` because a model does not keep anybody
183
+ * waiting, and a live call is never `listening` or `speaking`, because nobody
184
+ * is transcribing it to know whose turn it is.
185
+ *
186
+ * `missed` is final and ordinary: the ringing stopped before anybody picked up.
187
+ */
188
+ export type LiveCallState =
189
+ 'ringing' | 'connecting' | 'connected' | 'ended' | 'missed' | 'failed';
190
+
191
+ export interface CallTranscriptLine {
192
+ bugId: string;
193
+ /** Who said it. `agent` lines arrive as deltas; `final` marks the end of a turn. */
194
+ role: 'reporter' | 'agent';
195
+ text: string;
196
+ final: boolean;
197
+ }
198
+
199
+ /** A call in progress. */
200
+ /** Which kind of call a deployment offers. One agent has one mode, so never both. */
201
+ export type CallKind = 'agent' | 'live';
202
+
203
+ export interface CallHandle {
204
+ /** End it. Safe to call twice, and after it has already ended. */
205
+ hangUp(): void;
206
+ /** The report this call is about, whether it was named or filed on the way in. */
207
+ bugId: string;
208
+ /** What was filed, when the call is the reason the report exists. */
209
+ filed?: { id: string; watchToken?: string } | null;
210
+ }
211
+
212
+ export interface LiveCallHandle {
213
+ /** End it. Safe to call twice, and after it has already ended. */
214
+ hangUp(): void;
215
+ /**
216
+ * Mute or unmute the microphone, returning what it now is.
217
+ *
218
+ * The track is disabled rather than the stream stopped: stopping it would
219
+ * drop the sender and renegotiate the call, and unmuting would then take a
220
+ * second and a half of silence to come back.
221
+ */
222
+ setMuted(muted: boolean): boolean;
223
+ isMuted(): boolean;
224
+ /** The report this call is about. */
225
+ bugId: string;
226
+ /** sphoro.voice's id for the call, as the team's dashboard also knows it. */
227
+ callId: string;
228
+ live: true;
229
+ }
230
+
231
+ export interface Attachment {
232
+ type: 'SCREENSHOT' | 'VIDEO';
233
+ storageKey: string;
234
+ mime: AttachmentMime;
235
+ }
236
+
237
+ export interface ReportInput {
238
+ title: string;
239
+ description?: string;
240
+ priority?: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
241
+ metadata?: Record<string, any>;
242
+ /** Already uploaded via uploadAttachment(). Entries without a key are dropped. */
243
+ attachments?: Attachment[];
244
+ }
245
+
246
+ /**
247
+ * How long a report is likely to take, as a reporter may be shown it.
248
+ *
249
+ * A band and a sentence, never a single figure: "about two days" is heard as a
250
+ * promise about Thursday, and "two to five days" is heard as what it is. The
251
+ * basis, the sample size and the past bugs it was drawn from are all real and
252
+ * all deliberately absent — they are the workings of a triage queue the person
253
+ * reading this is not part of.
254
+ *
255
+ * Null whenever the project has switched estimates off, has decided its
256
+ * reporters do not see them, or has nothing closed to base one on. Draw nothing
257
+ * in that case: a confirmation that admits it cannot answer is worse than one
258
+ * that never raises the question.
259
+ */
260
+ export interface ReportEstimate {
261
+ /** The optimistic end of the band, in hours from filing. */
262
+ lowHours: number;
263
+ highHours: number;
264
+ /** The band as a phrase — "2–5 days", "about 6 hours". Ready to print. */
265
+ label: string;
266
+ }
267
+
268
+ export interface ReportResult {
269
+ id: string;
270
+ redactionsApplied: number;
271
+ /**
272
+ * Returned only when identify() supplied an email, so the report could be
273
+ * attributed. The SDK stores it and uses it to watch for status changes —
274
+ * you do not need to handle it yourself.
275
+ */
276
+ watchToken?: string | null;
277
+ /**
278
+ * Computed on this same request rather than a follow-up, so it can be shown
279
+ * on the confirmation the reporter is still looking at.
280
+ */
281
+ estimate?: ReportEstimate | null;
282
+ }
283
+
284
+ /** One row of the reporter's own history. */
285
+ export interface MyReport {
286
+ id: string;
287
+ /** The short handle shown on screen and in their confirmation. */
288
+ reference: string;
289
+ title: string;
290
+ status: 'OPEN' | 'IN_PROGRESS' | 'RESOLVED' | 'CLOSED';
291
+ priority: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
292
+ createdAt: string;
293
+ updatedAt: string;
294
+ project: string;
295
+ workspace: string;
296
+ attachments: number;
297
+ }
298
+
299
+ /** A report of this browser's changed status. */
300
+ export type ReportUpdateKind = 'status' | 'severity' | 'assignment' | 'reply';
301
+
302
+ export interface ReportUpdate {
303
+ /**
304
+ * Stable for the life of this update, and the same after a reload — what
305
+ * `markUpdateRead` takes, and what a host app keys its own list on.
306
+ */
307
+ id: string;
308
+ bugId: string;
309
+ title: string;
310
+ /** What changed. Anything unrecognised should be treated as a generic update. */
311
+ kind: ReportUpdateKind;
312
+ /** A line written for the reporter, ready to show. Prefer this over building your own. */
313
+ summary: string | null;
314
+ status: 'OPEN' | 'IN_PROGRESS' | 'RESOLVED' | 'CLOSED' | null;
315
+ /** Present on a severity change. */
316
+ priority: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' | null;
317
+ /**
318
+ * Present on an assignment change: whether it now has an owner. Never who —
319
+ * a workspace's staff are not part of what was reported.
320
+ */
321
+ assigned: boolean | null;
322
+ /** Present on a reply the team chose to share. Internal comments never appear. */
323
+ reply: ReportMessage | null;
324
+ /**
325
+ * Present on an assignment when the report may be talked through. An offer,
326
+ * never a call: `startCall` is what starts one.
327
+ */
328
+ call: { offered: boolean } | null;
329
+ at: string;
330
+ }
331
+
332
+ /** A file on a message: an image, a screen recording, a voice note, a document. */
333
+ export interface MessageAttachment {
334
+ id: string;
335
+ type: 'SCREENSHOT' | 'VIDEO' | 'AUDIO' | 'FILE';
336
+ mime: string;
337
+ /** As the sender's machine had it, when they had one. */
338
+ name: string | null;
339
+ /** Bytes, as reported at upload. Advisory. */
340
+ size: number | null;
341
+ url: string;
342
+ }
343
+
344
+ /** What was said, and what came with it. */
345
+ export interface ReportMessage {
346
+ body: string;
347
+ /** Who signed it. Null on the reporter's own messages — that is "You". */
348
+ author: string | null;
349
+ attachments?: MessageAttachment[];
350
+ }
351
+
352
+ /**
353
+ * One step in a report's history, in the same vocabulary as ReportUpdate.
354
+ *
355
+ * Entries carrying `from` are messages in the conversation; the rest are
356
+ * things that happened to the report.
357
+ */
358
+ export interface ReportTimelineEntry {
359
+ id: string;
360
+ kind: 'filed' | 'message' | ReportUpdateKind;
361
+ summary: string;
362
+ at: string;
363
+ status?: string;
364
+ priority?: string;
365
+ assigned?: boolean;
366
+ reply?: ReportMessage;
367
+ /** Which side wrote it. Absent on entries that are not messages. */
368
+ from?: 'team' | 'reporter';
369
+ }
370
+
371
+ export interface ReportDetail {
372
+ id: string;
373
+ reference: string;
374
+ title: string;
375
+ description: string | null;
376
+ status: 'OPEN' | 'IN_PROGRESS' | 'RESOLVED' | 'CLOSED';
377
+ priority: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
378
+ url: string | null;
379
+ tags: string[];
380
+ createdAt: string;
381
+ updatedAt: string;
382
+ project: string;
383
+ workspace: string;
384
+ attachments: { type: string; mime: string; createdAt: string; url: string | null }[];
385
+ /** Every step from filing to now, oldest first, both sides' messages included. */
386
+ timeline: ReportTimelineEntry[];
387
+ /**
388
+ * The same figure they were given when they filed it, so coming back to check
389
+ * is not a worse experience than not checking. Null on a settled report — the
390
+ * question stops being asked once it is answered.
391
+ */
392
+ estimate?: ReportEstimate | null;
393
+ /**
394
+ * Whether this reader can write back — true for the SDK, which holds a watch
395
+ * token. The widget hides its composer when false rather than offering a box
396
+ * whose send button would fail.
397
+ */
398
+ canReply: boolean;
399
+ }
400
+
401
+ /**
402
+ * What a project asks of the person reporting.
403
+ *
404
+ * 'off' — no sign-in. An address only reaches a report because
405
+ * identify() supplied one.
406
+ * 'optional' — the widget offers a sign-in that may be skipped.
407
+ * 'required' — nothing can be filed until they have signed in.
408
+ *
409
+ * A project's own setting, changed in BugCatch under Project Settings →
410
+ * General. It is not a config option here: a widget that could opt out of a
411
+ * required sign-in would not be a requirement.
412
+ */
413
+ export type SdkLoginMode = 'off' | 'optional' | 'required';
414
+
415
+ export interface SdkLoginState {
416
+ mode: SdkLoginMode;
417
+ /** Who this browser is signed in as on this project, or null. */
418
+ signedIn: { email: string; name: string | null } | null;
419
+ /**
420
+ * Why a session that was held is no longer good — expired, or ended by this
421
+ * address signing in somewhere else. Null in every ordinary case, including
422
+ * having never signed in.
423
+ */
424
+ signedOutReason: string | null;
425
+ }
426
+
427
+ export declare class BugCatchClient {
428
+ init(config: BugCatchConfig): this;
429
+ /**
430
+ * Listen for updates on this browser's reports. Returns an unsubscribe.
431
+ * Several listeners can coexist, unlike the single `onUpdate` config hook.
432
+ */
433
+ on(event: 'update', handler: (update: ReportUpdate) => void): () => void;
434
+ on(
435
+ event: 'call',
436
+ handler: (detail: {
437
+ bugId: string; state: CallState | LiveCallState; live?: boolean; reason?: string;
438
+ }) => void,
439
+ ): () => void;
440
+ on(event: 'call-transcript', handler: (line: CallTranscriptLine) => void): () => void;
441
+ /**
442
+ * A call the team placed to this page, as it moves. Separate from `call`,
443
+ * which is the conversation once one is connected — this is the telephone
444
+ * before anybody picks it up.
445
+ */
446
+ on(
447
+ event: 'incoming-call',
448
+ handler: (detail: {
449
+ bugId: string; callId: string; state: IncomingCallState; error?: Error;
450
+ }) => void,
451
+ ): () => void;
452
+ /**
453
+ * How much is waiting to be read, after every arrival and every mark-read.
454
+ * The number for your own bell, whether or not the built-in cards are on.
455
+ */
456
+ on(
457
+ event: 'unread',
458
+ handler: (state: { count: number; updates: ReportUpdate[] }) => void,
459
+ ): () => void;
460
+ /** Someone signed in through the widget, or through signIn()/confirmSignIn(). */
461
+ on(
462
+ event: 'signin',
463
+ handler: (who: { email: string; name: string | null }) => void,
464
+ ): () => void;
465
+ /** Someone signed out. `email` is who it was, where this browser knew. */
466
+ on(event: 'signout', handler: (who: { email: string | null }) => void): () => void;
467
+ off(event: 'update', handler: (update: ReportUpdate) => void): this;
468
+ off(
469
+ event: 'call' | 'call-transcript' | 'unread' | 'incoming-call' | 'signin' | 'signout',
470
+ handler: (payload: any) => void,
471
+ ): this;
472
+ /**
473
+ * What this project asks of the reporter, and who this browser is signed in
474
+ * as. The built-in widget asks this itself every time it opens; it is here
475
+ * for integrations running with `widget: false`.
476
+ */
477
+ loginState(): Promise<SdkLoginState>;
478
+ /**
479
+ * Who this browser is signed in as, from storage alone — no request, and
480
+ * therefore only as current as the last one. A session ended elsewhere still
481
+ * reads as signed in here until something asks. Use loginState() where that
482
+ * matters.
483
+ */
484
+ signedInAs(): { email: string; name: string | null } | null;
485
+ /**
486
+ * Step one of signing in: mail a code to any address. There is no list of
487
+ * people who may report a bug — the code is what proves the address, and
488
+ * proving it is all a reply-to needs.
489
+ */
490
+ signIn(email: string, options?: { name?: string }): Promise<{ sent: true; email: string }>;
491
+ /**
492
+ * Step two: spend the code. Signs this address out of this project in every
493
+ * other browser — one live session per address per project.
494
+ */
495
+ confirmSignIn(email: string, code: string): Promise<{ email: string; name: string | null }>;
496
+ /**
497
+ * Sign out of this project, here and on the server, so the token cannot be
498
+ * replayed from a copy of this browser's storage.
499
+ */
500
+ signOut(): Promise<{ signedOut: boolean }>;
501
+ /**
502
+ * Updates nobody has read yet, oldest first. Survives a reload — the stream
503
+ * replays nothing when it reconnects, so an update that arrived while the tab
504
+ * was closed would otherwise be lost.
505
+ */
506
+ unreadUpdates(): ReportUpdate[];
507
+ /**
508
+ * Mark one update read: it stops being shown, now and on the next load.
509
+ * Read is something the reporter says rather than something a timer decides,
510
+ * so an integration rendering its own notifications has to say when.
511
+ */
512
+ markUpdateRead(id: string): this;
513
+ /**
514
+ * Mark everything about one report read. Opening the report is reading its
515
+ * updates — the built-in widget calls this when a thread opens.
516
+ */
517
+ markReportRead(bugId: string): this;
518
+ /** Mark everything read. */
519
+ markUpdatesRead(): this;
520
+ /**
521
+ * Whether a report can be talked through right now: false without
522
+ * sphoro.voice configured, on a workspace that has not switched calling on,
523
+ * and on a report that has had its allowance of calls.
524
+ */
525
+ canCall(bugId: string): Promise<boolean>;
526
+ /**
527
+ * Whether this visitor can report something by talking about it instead of
528
+ * filling the form in. False unless the project has calling configured *and*
529
+ * `identify()` has supplied an email — a call with nobody to attribute has no
530
+ * way to follow up afterwards.
531
+ */
532
+ canCallNow(): Promise<boolean>;
533
+ /**
534
+ * Which kind of call this visitor would get, or null for none.
535
+ *
536
+ * `'agent'` is the triage agent: a model answers instantly and asks the
537
+ * questions the form did not. `'live'` rings the team and a colleague picks
538
+ * up. A deployment offers one or the other and never both — the agent behind
539
+ * it has exactly one mode — so this is what to word a button from.
540
+ *
541
+ * Remembered for the life of the page: it describes the deployment's
542
+ * configuration, not this visitor.
543
+ */
544
+ callKind(): Promise<CallKind | null>;
545
+ /**
546
+ * Talk a report through. Asks for the microphone, places the call, and
547
+ * resolves once it is up.
548
+ *
549
+ * Lands on whichever kind of call this deployment offers, which is decided by
550
+ * the mode of its agent rather than by the method you called: against a
551
+ * person-to-person agent this rings the team, and the handle comes back with
552
+ * `live: true`. Ask `callKind()` first if the wording around your own button
553
+ * has to say which.
554
+ *
555
+ * Rejects if the microphone is refused, if calling is not available on this
556
+ * report, or if a call is already running — there is deliberately no way to
557
+ * start a second.
558
+ */
559
+ startCall(bugId: string): Promise<CallHandle | LiveCallHandle>;
560
+ /**
561
+ * The same, with no report yet: files one from the captured context and calls
562
+ * about it. The handle carries the id it was filed as. A rejection with
563
+ * `.filed` set means the report was written and only the call failed.
564
+ */
565
+ startCall(): Promise<CallHandle | LiveCallHandle>;
566
+ /**
567
+ * Whether this report can be talked through with a person on the team.
568
+ *
569
+ * Never true at the same time as `canCall`: one agent has one mode, so a
570
+ * deployment answers for the triage agent or for a live call, not both.
571
+ */
572
+ canCallTeam(bugId: string): Promise<boolean>;
573
+ /**
574
+ * Ring the team about a report, and hand back the call.
575
+ *
576
+ * Resolves once it is *ringing*, not once somebody answers — follow the rest
577
+ * through `onCallState` or the `call` event. The audio goes directly between
578
+ * the two browsers over WebRTC, so a live call is not recorded and not
579
+ * transcribed; the report's thread gets a note saying it happened.
580
+ *
581
+ * A report id is required, unlike `startCall`: ringing a room about a report
582
+ * nobody can read would ask somebody to answer a call they know nothing
583
+ * about.
584
+ *
585
+ * Rejects if the microphone is refused, if live calling is not available on
586
+ * this report, or if a call of either kind is already running — this page has
587
+ * one microphone.
588
+ */
589
+ startLiveCall(bugId: string): Promise<LiveCallHandle>;
590
+ /**
591
+ * End the call in progress, of either kind. Does nothing when there is not
592
+ * one — there is a single slot, because there is a single microphone.
593
+ */
594
+ endCall(): this;
595
+ /** One report with its full timeline, or null if this browser cannot prove it filed it. */
596
+ myReport(bugId: string): Promise<ReportDetail | null>;
597
+ /**
598
+ * Write back on a report this browser filed. The widget's own conversation
599
+ * view uses this; it is public so an integration rendering its own can too.
600
+ *
601
+ * Body or attachments — at least one, or the API refuses it.
602
+ */
603
+ replyToReport(
604
+ bugId: string,
605
+ message: {
606
+ body?: string;
607
+ attachments?: {
608
+ storageKey: string;
609
+ mime: MessageMime;
610
+ name?: string;
611
+ size?: number;
612
+ }[];
613
+ },
614
+ ): Promise<{
615
+ id: string;
616
+ from: 'reporter';
617
+ body: string;
618
+ attachments: MessageAttachment[];
619
+ at: string;
620
+ }>;
621
+ /**
622
+ * Attach the signed-in user. `email` threads their reports together in the
623
+ * dashboard and is where the "your issue is resolved" email goes — your app
624
+ * has already authenticated them, so the address is treated as reachable.
625
+ * Pass null on sign-out.
626
+ */
627
+ identify(user: BugCatchUser | null): this;
628
+ report(input: ReportInput): Promise<ReportResult | { cancelled: true }>;
629
+ /** Uploads straight to storage and resolves the key a report references. */
630
+ uploadAttachment(
631
+ blob: Blob,
632
+ mime?: string,
633
+ onProgress?: (fraction: number) => void,
634
+ ): Promise<{ storageKey: string; mime: AttachmentMime }>;
635
+ /**
636
+ * What this project is already known to be having trouble with, most
637
+ * affected first.
638
+ *
639
+ * The built-in widget asks this itself when it opens; it is here for an
640
+ * integration running with `widget: false`. Resolves to an empty list on
641
+ * every project that has not switched the feature on, which is the default —
642
+ * draw nothing rather than an empty heading.
643
+ */
644
+ knownIssues(): Promise<KnownIssue[]>;
645
+ /**
646
+ * The known issues that look like what somebody is describing.
647
+ *
648
+ * Pass an AbortSignal when calling this on a keystroke: without one a fast
649
+ * typist renders whichever response happens to land last, which is not
650
+ * necessarily the one matching what is now in the box.
651
+ */
652
+ matchKnownIssues(query: string, options?: { signal?: AbortSignal }): Promise<KnownIssue[]>;
653
+ /**
654
+ * Say a known issue is happening to you too, instead of filing a second
655
+ * report about it.
656
+ *
657
+ * Sends this browser's console, network and environment, so the team gets one
658
+ * report carrying everybody's environment rather than fourteen reports of the
659
+ * same thing. Everyone who joins is told when it is resolved, exactly as the
660
+ * person who filed it is.
661
+ *
662
+ * Needs an address, which is what that promise rests on: a signed-in widget
663
+ * has one and `identify()` supplies one otherwise, so pass `email` only when
664
+ * neither did. Rejects when there is none.
665
+ */
666
+ meToo(bugId: string, who?: { email?: string; name?: string }): Promise<JoinedIssue>;
667
+ snapshot(): { console: any[]; network: any[]; environment: Record<string, any> };
668
+ /**
669
+ * Everything this browser has reported, newest first, with each one's
670
+ * status. Resolves to an empty history when nothing has been filed here.
671
+ */
672
+ myReports(): Promise<{ reporter: { email: string; name: string | null } | null; reports: MyReport[] }>;
673
+ /** True when this browser has filed something — for hiding an entry point. */
674
+ hasReports(): boolean;
675
+ /** Open the update stream by hand. Idempotent; init() and report() call it. */
676
+ startWatching(): this;
677
+ /** Forget this browser's reports — call on sign-out. */
678
+ forgetReports(): this;
679
+ clear(): void;
680
+ destroy(): void;
681
+ }
682
+
683
+ declare const BugCatch: BugCatchClient & { Client: typeof BugCatchClient };
684
+ export default BugCatch;