quick-bug-reporter-react 1.2.0 → 1.3.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 +225 -117
- package/dist/index.cjs +1486 -1391
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -207
- package/dist/index.d.ts +4 -207
- package/dist/index.js +1486 -1392
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Drop-in bug reporter for React apps — screenshot capture, video recording, ann
|
|
|
8
8
|
- **Video recording** — Screen + microphone via `MediaRecorder` API
|
|
9
9
|
- **Annotation** — Drag-to-highlight on captured screenshots
|
|
10
10
|
- **Network logging** — Automatic fetch interception during capture
|
|
11
|
+
- **Console capture** — Automatic console log and JS error capture
|
|
11
12
|
- **Integrations** — Linear and Jira (direct API or backend proxy)
|
|
12
13
|
- **Zero config UI** — Floating bug button + modal wizard, ships its own styles
|
|
13
14
|
|
|
@@ -56,18 +57,20 @@ import {
|
|
|
56
57
|
BugReporterProvider,
|
|
57
58
|
FloatingBugButton,
|
|
58
59
|
BugReporterModal,
|
|
59
|
-
|
|
60
|
+
JiraIntegration,
|
|
60
61
|
} from "quick-bug-reporter-react";
|
|
61
62
|
|
|
62
|
-
const
|
|
63
|
-
|
|
63
|
+
const jira = new JiraIntegration({
|
|
64
|
+
createIssueProxyEndpoint: "/api/jira/create-issue",
|
|
65
|
+
uploadAttachmentProxyEndpoint: "/api/jira/upload-attachment",
|
|
66
|
+
projectKey: "BUG",
|
|
64
67
|
});
|
|
65
68
|
|
|
66
69
|
export default function App({ children }) {
|
|
67
70
|
return (
|
|
68
71
|
<BugReporterProvider
|
|
69
|
-
integrations={{
|
|
70
|
-
defaultProvider="
|
|
72
|
+
integrations={{ jira }}
|
|
73
|
+
defaultProvider="jira"
|
|
71
74
|
>
|
|
72
75
|
{children}
|
|
73
76
|
<FloatingBugButton />
|
|
@@ -81,32 +84,41 @@ That's it — a floating "Report Bug" button appears in the bottom-right corner.
|
|
|
81
84
|
|
|
82
85
|
## Integrations
|
|
83
86
|
|
|
84
|
-
|
|
87
|
+
### Jira
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { JiraIntegration } from "quick-bug-reporter-react";
|
|
91
|
+
|
|
92
|
+
// ✅ Recommended: split proxy endpoints (parallel uploads, fastest)
|
|
93
|
+
const jira = new JiraIntegration({
|
|
94
|
+
createIssueProxyEndpoint: "/api/jira/create-issue",
|
|
95
|
+
uploadAttachmentProxyEndpoint: "/api/jira/upload-attachment",
|
|
96
|
+
projectKey: "BUG",
|
|
97
|
+
issueType: "Bug", // optional, defaults to "Bug"
|
|
98
|
+
});
|
|
99
|
+
```
|
|
85
100
|
|
|
86
|
-
|
|
|
87
|
-
|
|
88
|
-
|
|
|
89
|
-
|
|
|
101
|
+
| Option | Description |
|
|
102
|
+
|--------|-------------|
|
|
103
|
+
| `projectKey` | Jira project key (e.g. `"BUG"`) |
|
|
104
|
+
| `issueType` | Issue type name, defaults to `"Bug"` |
|
|
105
|
+
| `createIssueProxyEndpoint` | Proxy for issue creation (recommended) |
|
|
106
|
+
| `uploadAttachmentProxyEndpoint` | Proxy for attachment uploads (recommended) |
|
|
107
|
+
| `submitProxyEndpoint` | Single endpoint fallback (slower — see [below](#legacy-single-endpoint)) |
|
|
108
|
+
| `baseUrl` | Jira instance URL (direct mode only — not for browser SPAs) |
|
|
109
|
+
| `email` | Jira email (direct mode only) |
|
|
110
|
+
| `apiToken` | Jira API token (direct mode only) |
|
|
111
|
+
| `fetchImpl` | Custom fetch implementation |
|
|
90
112
|
|
|
91
113
|
### Linear
|
|
92
114
|
|
|
93
115
|
```ts
|
|
94
116
|
import { LinearIntegration } from "quick-bug-reporter-react";
|
|
95
117
|
|
|
96
|
-
// ✅ Recommended:
|
|
97
|
-
const linear = new LinearIntegration({
|
|
98
|
-
submitProxyEndpoint: "/api/bug-report",
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Or split proxy endpoints for finer control:
|
|
118
|
+
// ✅ Recommended: split proxy endpoints (parallel uploads, fastest)
|
|
102
119
|
const linear = new LinearIntegration({
|
|
103
120
|
createIssueProxyEndpoint: "/api/linear/create-issue",
|
|
104
121
|
uploadProxyEndpoint: "/api/linear/upload",
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// ⚠️ Direct API — server-side / Next.js API routes only
|
|
108
|
-
const linear = new LinearIntegration({
|
|
109
|
-
apiKey: "lin_api_...",
|
|
110
122
|
teamId: "TEAM_ID",
|
|
111
123
|
projectId: "PROJECT_ID", // optional — assigns issues to a Linear project
|
|
112
124
|
});
|
|
@@ -114,101 +126,62 @@ const linear = new LinearIntegration({
|
|
|
114
126
|
|
|
115
127
|
| Option | Description |
|
|
116
128
|
|--------|-------------|
|
|
117
|
-
| `apiKey` | Linear API key (direct mode only) |
|
|
118
129
|
| `teamId` | Linear team ID |
|
|
119
130
|
| `projectId` | Linear project ID — optional, assigns every issue to this project |
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
|
|
125
|
-
### Jira
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
import { JiraIntegration } from "quick-bug-reporter-react";
|
|
129
|
-
|
|
130
|
-
// ✅ Recommended: Backend proxy
|
|
131
|
-
const jira = new JiraIntegration({
|
|
132
|
-
submitProxyEndpoint: "/api/bug-report",
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Or split proxy endpoints:
|
|
136
|
-
const jira = new JiraIntegration({
|
|
137
|
-
createIssueProxyEndpoint: "/api/jira/create-issue",
|
|
138
|
-
uploadAttachmentProxyEndpoint: "/api/jira/upload-attachment",
|
|
139
|
-
projectKey: "BUG",
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// ⚠️ Direct API — server-side only (CORS + exposes credentials)
|
|
143
|
-
const jira = new JiraIntegration({
|
|
144
|
-
baseUrl: "https://your-domain.atlassian.net",
|
|
145
|
-
email: "you@example.com",
|
|
146
|
-
apiToken: "...",
|
|
147
|
-
projectKey: "BUG",
|
|
148
|
-
issueType: "Bug", // optional, defaults to "Bug"
|
|
149
|
-
});
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
| Option | Description |
|
|
153
|
-
|--------|-------------|
|
|
154
|
-
| `baseUrl` | Jira instance URL (direct mode only) |
|
|
155
|
-
| `email` | Jira email (direct mode only) |
|
|
156
|
-
| `apiToken` | Jira API token (direct mode only) |
|
|
157
|
-
| `projectKey` | Jira project key (e.g. `"BUG"`) |
|
|
158
|
-
| `issueType` | Issue type name, defaults to `"Bug"` |
|
|
159
|
-
| `submitProxyEndpoint` | Single endpoint that handles the entire submission |
|
|
160
|
-
| `createIssueProxyEndpoint` | Proxy for issue creation only |
|
|
161
|
-
| `uploadAttachmentProxyEndpoint` | Proxy for attachment uploads only |
|
|
131
|
+
| `createIssueProxyEndpoint` | Proxy for issue creation (recommended) |
|
|
132
|
+
| `uploadProxyEndpoint` | Proxy for file uploads (recommended) |
|
|
133
|
+
| `submitProxyEndpoint` | Single endpoint fallback (slower — see [below](#legacy-single-endpoint)) |
|
|
134
|
+
| `apiKey` | Linear API key (direct mode only — not for browser SPAs) |
|
|
162
135
|
| `fetchImpl` | Custom fetch implementation |
|
|
163
136
|
|
|
164
137
|
---
|
|
165
138
|
|
|
166
|
-
### Proxy endpoint contract
|
|
139
|
+
### Proxy endpoint contract (split endpoints)
|
|
167
140
|
|
|
168
|
-
|
|
141
|
+
With split endpoints the library handles formatting and **uploads attachments in parallel** — your proxy only needs to forward auth. This is ~3× faster than a single endpoint.
|
|
169
142
|
|
|
170
|
-
####
|
|
143
|
+
#### `createIssueProxyEndpoint`
|
|
171
144
|
|
|
172
|
-
|
|
173
|
-
|-------|------|:-----------:|-------------|
|
|
174
|
-
| `provider` | string | Yes | `"linear"` or `"jira"` |
|
|
175
|
-
| `title` | string | Yes | Issue title |
|
|
176
|
-
| `description` | string | Yes | **Pre-formatted** issue description (ready to use as-is) |
|
|
177
|
-
| `issueType` | string | Jira only | Issue type (e.g. `"Bug"`) |
|
|
178
|
-
| `projectKey` | string | Jira only | Jira project key (if configured) |
|
|
179
|
-
| `teamId` | string | Linear only | Linear team ID (if configured) |
|
|
180
|
-
| `projectId` | string | Linear only | Linear project ID (if configured) |
|
|
181
|
-
| `screenshotFile` | File | If screenshot | `bug-screenshot.png` |
|
|
182
|
-
| `screenRecordingFile` | File | If video | `bug-recording.webm` |
|
|
183
|
-
| `networkLogsFile` | File | Yes | `network-logs.txt` |
|
|
184
|
-
| `clientMetadataFile` | File | Yes | `client-metadata.json` |
|
|
185
|
-
| `consoleLogsFile` | File | If present | `console-logs.txt` (JS errors + console output) |
|
|
145
|
+
Receives a JSON POST:
|
|
186
146
|
|
|
187
|
-
#### Expected response
|
|
188
|
-
|
|
189
|
-
**Jira proxy:**
|
|
190
147
|
```json
|
|
191
148
|
{
|
|
192
|
-
"
|
|
193
|
-
"
|
|
149
|
+
"summary": "Bug title", // Jira
|
|
150
|
+
"title": "Bug title", // Linear
|
|
151
|
+
"description": "Pre-formatted description (ready to use as-is)",
|
|
152
|
+
"issueType": "Bug", // Jira only
|
|
153
|
+
"projectKey": "BUG", // Jira only
|
|
154
|
+
"teamId": "...", // Linear only
|
|
155
|
+
"projectId": "..." // Linear only (if configured)
|
|
194
156
|
}
|
|
195
157
|
```
|
|
196
158
|
|
|
197
|
-
|
|
159
|
+
Must return:
|
|
160
|
+
|
|
198
161
|
```json
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
162
|
+
// Jira
|
|
163
|
+
{ "id": "10001", "key": "BUG-42", "url": "https://you.atlassian.net/browse/BUG-42" }
|
|
164
|
+
|
|
165
|
+
// Linear
|
|
166
|
+
{ "id": "...", "identifier": "ENG-123", "url": "https://linear.app/..." }
|
|
203
167
|
```
|
|
204
168
|
|
|
205
|
-
####
|
|
169
|
+
#### `uploadAttachmentProxyEndpoint` (Jira)
|
|
170
|
+
|
|
171
|
+
Receives `FormData` with `issueKey` (string) + `file` (File). Returns `{ "ok": true }`.
|
|
172
|
+
|
|
173
|
+
#### `uploadProxyEndpoint` (Linear)
|
|
174
|
+
|
|
175
|
+
Receives `FormData` with `file` (File) + `filename` (string) + `contentType` (string). Returns `{ "assetUrl": "https://..." }`.
|
|
176
|
+
|
|
177
|
+
#### Example: Jira proxy (Node.js / Express)
|
|
206
178
|
|
|
207
179
|
```ts
|
|
208
|
-
|
|
209
|
-
|
|
180
|
+
// POST /api/jira/create-issue — forward issue creation with auth
|
|
181
|
+
app.post("/api/jira/create-issue", express.json(), async (req, res) => {
|
|
182
|
+
const { summary, description, issueType, projectKey } = req.body;
|
|
210
183
|
|
|
211
|
-
//
|
|
184
|
+
// Convert plain-text description to Jira ADF
|
|
212
185
|
const adf = {
|
|
213
186
|
type: "doc", version: 1,
|
|
214
187
|
content: description.split(/\n{2,}/).filter(Boolean).map(chunk => ({
|
|
@@ -217,8 +190,7 @@ app.post("/api/bug-report", upload.any(), async (req, res) => {
|
|
|
217
190
|
})),
|
|
218
191
|
};
|
|
219
192
|
|
|
220
|
-
|
|
221
|
-
const issue = await fetch(`${JIRA_BASE}/rest/api/3/issue`, {
|
|
193
|
+
const jiraRes = await fetch(`${JIRA_BASE}/rest/api/3/issue`, {
|
|
222
194
|
method: "POST",
|
|
223
195
|
headers: {
|
|
224
196
|
Authorization: `Basic ${btoa(`${JIRA_EMAIL}:${JIRA_TOKEN}`)}`,
|
|
@@ -227,38 +199,173 @@ app.post("/api/bug-report", upload.any(), async (req, res) => {
|
|
|
227
199
|
body: JSON.stringify({
|
|
228
200
|
fields: {
|
|
229
201
|
project: { key: projectKey },
|
|
230
|
-
summary
|
|
202
|
+
summary,
|
|
231
203
|
description: adf,
|
|
232
204
|
issuetype: { name: issueType || "Bug" },
|
|
233
205
|
},
|
|
234
206
|
}),
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const data = await jiraRes.json();
|
|
210
|
+
res.status(jiraRes.ok ? 201 : jiraRes.status).json(
|
|
211
|
+
jiraRes.ok
|
|
212
|
+
? { id: data.id, key: data.key, url: `${JIRA_BASE}/browse/${data.key}` }
|
|
213
|
+
: { error: data.errorMessages?.join("; ") || "Jira issue creation failed" }
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// POST /api/jira/upload-attachment — forward file upload with auth
|
|
218
|
+
app.post("/api/jira/upload-attachment", upload.single("file"), async (req, res) => {
|
|
219
|
+
const issueKey = req.body.issueKey;
|
|
220
|
+
const form = new FormData();
|
|
221
|
+
form.append("file", req.file.buffer, req.file.originalname);
|
|
222
|
+
|
|
223
|
+
const jiraRes = await fetch(`${JIRA_BASE}/rest/api/3/issue/${issueKey}/attachments`, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: {
|
|
226
|
+
Authorization: `Basic ${btoa(`${JIRA_EMAIL}:${JIRA_TOKEN}`)}`,
|
|
227
|
+
"X-Atlassian-Token": "no-check",
|
|
228
|
+
},
|
|
229
|
+
body: form,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
res.status(jiraRes.ok ? 200 : jiraRes.status).json(
|
|
233
|
+
jiraRes.ok ? { ok: true } : { error: "Upload failed" }
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Example: Linear proxy (Node.js / Express)
|
|
239
|
+
|
|
240
|
+
Linear uses a **GraphQL API**. Your proxy forwards mutations to `https://api.linear.app/graphql` with the API key server-side.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const LINEAR_API = "https://api.linear.app/graphql";
|
|
244
|
+
const LINEAR_API_KEY = process.env.LINEAR_API_KEY; // e.g. "lin_api_..."
|
|
245
|
+
|
|
246
|
+
// Helper: execute a Linear GraphQL mutation
|
|
247
|
+
async function linearGql(query: string, variables: Record<string, unknown>) {
|
|
248
|
+
const res = await fetch(LINEAR_API, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: {
|
|
251
|
+
Authorization: LINEAR_API_KEY,
|
|
252
|
+
"Content-Type": "application/json",
|
|
253
|
+
},
|
|
254
|
+
body: JSON.stringify({ query, variables }),
|
|
255
|
+
});
|
|
256
|
+
const body = await res.json();
|
|
257
|
+
if (!res.ok || body.errors?.length) {
|
|
258
|
+
throw new Error(body.errors?.[0]?.message || `Linear API error (${res.status})`);
|
|
259
|
+
}
|
|
260
|
+
return body.data;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// POST /api/linear/create-issue — create issue via GraphQL
|
|
264
|
+
app.post("/api/linear/create-issue", express.json(), async (req, res) => {
|
|
265
|
+
const { title, description, teamId, projectId } = req.body;
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const data = await linearGql(
|
|
269
|
+
`mutation IssueCreate($input: IssueCreateInput!) {
|
|
270
|
+
issueCreate(input: $input) {
|
|
271
|
+
success
|
|
272
|
+
issue { id identifier url }
|
|
273
|
+
}
|
|
274
|
+
}`,
|
|
275
|
+
{
|
|
276
|
+
input: {
|
|
277
|
+
title,
|
|
278
|
+
description,
|
|
279
|
+
teamId,
|
|
280
|
+
...(projectId ? { projectId } : {}),
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const issue = data.issueCreate.issue;
|
|
286
|
+
res.status(201).json({
|
|
287
|
+
id: issue.id,
|
|
288
|
+
identifier: issue.identifier,
|
|
289
|
+
url: issue.url,
|
|
248
290
|
});
|
|
291
|
+
} catch (err) {
|
|
292
|
+
res.status(500).json({ error: err.message });
|
|
249
293
|
}
|
|
294
|
+
});
|
|
250
295
|
|
|
251
|
-
|
|
296
|
+
// POST /api/linear/upload — upload file via GraphQL fileUpload + PUT
|
|
297
|
+
app.post("/api/linear/upload", upload.single("file"), async (req, res) => {
|
|
298
|
+
const file = req.file;
|
|
299
|
+
const contentType = req.body.contentType || file.mimetype;
|
|
300
|
+
const filename = req.body.filename || file.originalname;
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
// 1. Request an upload URL from Linear
|
|
304
|
+
const data = await linearGql(
|
|
305
|
+
`mutation FileUpload($contentType: String!, $filename: String!, $size: Int!) {
|
|
306
|
+
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
307
|
+
success
|
|
308
|
+
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
309
|
+
}
|
|
310
|
+
}`,
|
|
311
|
+
{ contentType, filename, size: file.size }
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const { uploadUrl, assetUrl, headers } = data.fileUpload.uploadFile;
|
|
315
|
+
|
|
316
|
+
// 2. PUT the file to the upload URL with Linear's signed headers
|
|
317
|
+
const uploadHeaders: Record<string, string> = {};
|
|
318
|
+
for (const h of headers) uploadHeaders[h.key] = h.value;
|
|
319
|
+
|
|
320
|
+
const uploadRes = await fetch(uploadUrl, {
|
|
321
|
+
method: "PUT",
|
|
322
|
+
headers: uploadHeaders,
|
|
323
|
+
body: file.buffer,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (!uploadRes.ok) throw new Error(`Upload failed (${uploadRes.status})`);
|
|
327
|
+
|
|
328
|
+
res.json({ assetUrl });
|
|
329
|
+
} catch (err) {
|
|
330
|
+
res.status(500).json({ error: err.message });
|
|
331
|
+
}
|
|
252
332
|
});
|
|
253
333
|
```
|
|
254
334
|
|
|
255
|
-
|
|
335
|
+
> **Key points for Linear proxy implementers:**
|
|
336
|
+
> - The `create-issue` endpoint receives `{ title, description, teamId, projectId }` — use these directly in the `IssueCreateInput` GraphQL mutation. The `description` is already pre-formatted by the library.
|
|
337
|
+
> - The `upload` endpoint must: (1) call `fileUpload` mutation to get a signed upload URL, (2) `PUT` the file binary to that URL with the returned headers, (3) return the `assetUrl`.
|
|
338
|
+
> - `projectId` is optional — only include it in the mutation input if it's present in the request body.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### Legacy: single endpoint
|
|
343
|
+
|
|
344
|
+
`submitProxyEndpoint` bundles everything into one request. The proxy must create the issue **and** upload all attachments server-side, which means uploads happen sequentially and are **~3× slower**. Use split endpoints above instead when possible.
|
|
345
|
+
|
|
346
|
+
<details>
|
|
347
|
+
<summary>Single endpoint FormData fields (click to expand)</summary>
|
|
348
|
+
|
|
349
|
+
| Field | Type | Always sent | Description |
|
|
350
|
+
|-------|------|:-----------:|-------------|
|
|
351
|
+
| `provider` | string | Yes | `"linear"` or `"jira"` |
|
|
352
|
+
| `title` | string | Yes | Issue title |
|
|
353
|
+
| `description` | string | Yes | **Pre-formatted** issue description (ready to use as-is) |
|
|
354
|
+
| `issueType` | string | Jira only | Issue type (e.g. `"Bug"`) |
|
|
355
|
+
| `projectKey` | string | Jira only | Jira project key (if configured) |
|
|
356
|
+
| `teamId` | string | Linear only | Linear team ID (if configured) |
|
|
357
|
+
| `projectId` | string | Linear only | Linear project ID (if configured) |
|
|
358
|
+
| `screenshotFile` | File | If screenshot | `bug-screenshot.png` |
|
|
359
|
+
| `screenRecordingFile` | File | If video | `bug-recording.webm` |
|
|
360
|
+
| `networkLogsFile` | File | Yes | `network-logs.txt` |
|
|
361
|
+
| `clientMetadataFile` | File | Yes | `client-metadata.json` |
|
|
362
|
+
| `consoleLogsFile` | File | If present | `console-logs.txt` (JS errors + console output) |
|
|
363
|
+
|
|
364
|
+
**Jira response:** `{ "jira": { "id": "...", "key": "BUG-42", "url": "..." }, "warnings": [] }`
|
|
256
365
|
|
|
257
|
-
|
|
366
|
+
**Linear response:** `{ "linear": { "id": "...", "identifier": "ENG-123", "url": "..." }, "warnings": [] }`
|
|
258
367
|
|
|
259
|
-
|
|
260
|
-
- **`uploadAttachmentProxyEndpoint`** (Jira) — receives `FormData` with `issueKey` + `file`, returns `{ ok: true }`
|
|
261
|
-
- **`uploadProxyEndpoint`** (Linear) — receives `FormData` with `file` + `filename` + `contentType`, returns `{ assetUrl }`
|
|
368
|
+
</details>
|
|
262
369
|
|
|
263
370
|
### Advanced: Custom fetch
|
|
264
371
|
|
|
@@ -291,6 +398,7 @@ const linear = new LinearIntegration({
|
|
|
291
398
|
- `ScreenshotCapturer` — HTML-to-canvas screenshot engine
|
|
292
399
|
- `ScreenRecorder` — Screen + mic recording via MediaRecorder
|
|
293
400
|
- `NetworkLogger` — Fetch interception logger
|
|
401
|
+
- `ConsoleCapture` — Console log and JS error capture
|
|
294
402
|
|
|
295
403
|
### Integrations
|
|
296
404
|
|