@arvoretech/bug-jar 0.3.2 → 0.6.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 +74 -29
- package/dist/index.d.mts +21 -7
- package/dist/index.d.ts +21 -7
- package/dist/index.global.js +72 -279
- package/dist/index.js +72 -279
- package/dist/index.mjs +71 -278
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# 🪲 Bug Jar
|
|
2
2
|
|
|
3
|
-
Capture everything for non-technical people to help debug. One
|
|
3
|
+
Capture everything for non-technical people to help debug. One debug link, zero config.
|
|
4
4
|
|
|
5
|
-
Bug Jar is a lightweight JavaScript library that silently records all browser activity (network requests, console logs, errors, user actions, performance metrics, and more) and lets
|
|
5
|
+
Bug Jar is a lightweight JavaScript library that silently records all browser activity (network requests, console logs, errors, user actions, performance metrics, and more) and lets you capture a complete, encrypted debug session from a one-time debug link.
|
|
6
6
|
|
|
7
7
|
## Why?
|
|
8
8
|
|
|
9
|
-
Because asking someone to "open DevTools and check the Network tab" doesn't scale. Bug Jar gives your support team
|
|
9
|
+
Because asking someone to "open DevTools and check the Network tab" doesn't scale. Bug Jar gives your support team and QA a link they can send to anyone to capture everything developers need to reproduce and fix bugs.
|
|
10
10
|
|
|
11
11
|
## What it captures
|
|
12
12
|
|
|
@@ -26,30 +26,19 @@ Because asking someone to "open DevTools and check the Network tab" doesn't scal
|
|
|
26
26
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Bug Jar runs as a **debug session**: staff generates a one-time link, the user
|
|
30
|
+
opens it in the real app, and the encrypted package is uploaded to S3. See
|
|
31
|
+
[Debug Session Mode](#debug-session-mode-encrypted-upload-to-s3) below.
|
|
30
32
|
|
|
31
33
|
```html
|
|
32
34
|
<script src="https://unpkg.com/@arvoretech/bug-jar"></script>
|
|
33
35
|
<script>
|
|
34
|
-
BugJar.init(
|
|
36
|
+
BugJar.init({
|
|
37
|
+
debugSessionEndpoint: 'https://api.arvore.com.br/debug-sessions',
|
|
38
|
+
})
|
|
35
39
|
</script>
|
|
36
40
|
```
|
|
37
41
|
|
|
38
|
-
### NPM
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm install @arvoretech/bug-jar
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import { init } from '@arvoretech/bug-jar'
|
|
46
|
-
|
|
47
|
-
init({
|
|
48
|
-
endpoint: 'https://your-api.com/bug-reports',
|
|
49
|
-
uiPosition: 'bottom-right',
|
|
50
|
-
})
|
|
51
|
-
```
|
|
52
|
-
|
|
53
42
|
## Configuration
|
|
54
43
|
|
|
55
44
|
```typescript
|
|
@@ -74,16 +63,12 @@ init({
|
|
|
74
63
|
// Privacy — fields containing these strings are redacted
|
|
75
64
|
sensitiveFields: ['password', 'token', 'secret', 'authorization', 'cookie', 'session', 'credit_card', 'cvv', 'ssn', 'cpf'],
|
|
76
65
|
|
|
77
|
-
// Where to send reports (if not set, downloads as JSON file)
|
|
78
|
-
endpoint: 'https://your-api.com/bug-reports',
|
|
79
|
-
|
|
80
66
|
// Callback when report is generated
|
|
81
67
|
onCapture: (report) => console.log(report),
|
|
82
68
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
uiLabel: 'Reportar Bug',
|
|
69
|
+
// Debug session
|
|
70
|
+
debugSessionEndpoint: 'https://api.arvore.com.br/debug-sessions',
|
|
71
|
+
// debugToken is read automatically from the ?debug=<token> query param
|
|
87
72
|
})
|
|
88
73
|
```
|
|
89
74
|
|
|
@@ -92,7 +77,7 @@ init({
|
|
|
92
77
|
```typescript
|
|
93
78
|
import { BugJar } from '@arvoretech/bug-jar'
|
|
94
79
|
|
|
95
|
-
const jar = new BugJar(
|
|
80
|
+
const jar = new BugJar()
|
|
96
81
|
jar.start()
|
|
97
82
|
|
|
98
83
|
// Later, capture a report programmatically
|
|
@@ -105,12 +90,72 @@ await fetch('/api/bugs', {
|
|
|
105
90
|
})
|
|
106
91
|
```
|
|
107
92
|
|
|
93
|
+
## Debug Session Mode (encrypted upload to S3)
|
|
94
|
+
|
|
95
|
+
For non-technical users, staff can generate a one-time debug link. The user opens
|
|
96
|
+
the link in the real app, reproduces the bug, and Bug Jar records the whole flow,
|
|
97
|
+
encrypts the package end-to-end in the browser, and uploads it straight to a
|
|
98
|
+
private S3 bucket via a presigned URL. No AWS credentials ever reach the browser.
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<script src="https://unpkg.com/@arvoretech/bug-jar"></script>
|
|
102
|
+
<script>
|
|
103
|
+
BugJar.init({
|
|
104
|
+
debugSessionEndpoint: 'https://api.arvore.com.br/debug-sessions',
|
|
105
|
+
// debugToken is read automatically from the ?debug=<token> query param
|
|
106
|
+
})
|
|
107
|
+
</script>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Flow:
|
|
111
|
+
|
|
112
|
+
1. Staff (admin) calls `POST /debug-sessions` with the target app URL and gets back
|
|
113
|
+
a `debugLink` (`https://app.../?debug=<token>`), the server public key, and an expiry.
|
|
114
|
+
2. The user opens the link. Bug Jar detects `?debug=<token>`, persists the token in
|
|
115
|
+
a `.arvore.com.br` cookie, fetches the session, starts screen recording, and
|
|
116
|
+
shows an animated **session border** (no button, no banner).
|
|
117
|
+
3. The user just uses the app. The session survives navigation across apps
|
|
118
|
+
(app-v2 → legacy → reader) by re-hydrating the token from the cookie on each
|
|
119
|
+
page. Each page streams **encrypted chunks** to S3 in real time:
|
|
120
|
+
- **data** (network/console/errors/actions/storage) every 5s as JSONL
|
|
121
|
+
- **video** chunks straight from `MediaRecorder` as they are produced
|
|
122
|
+
Chunks are uploaded to `debug/<token>/<pageId>/<stream>/<seq>.bin` via per-chunk
|
|
123
|
+
presigned PUT URLs (`POST /debug-sessions/:token/chunk-url`). Page metadata goes
|
|
124
|
+
to `debug/<token>/<pageId>/meta.json` (`POST /debug-sessions/:token/meta`).
|
|
125
|
+
4. Staff reconstructs the session offline:
|
|
126
|
+
```bash
|
|
127
|
+
BUG_JAR_API=https://api.arvore.com.br/debug-sessions \
|
|
128
|
+
BUG_JAR_API_TOKEN=<admin-jwt> \
|
|
129
|
+
node scripts/decrypt-session.cjs <token> ./out
|
|
130
|
+
```
|
|
131
|
+
This reads the X25519 private key from Secrets Manager (`bug-jar/keypair`),
|
|
132
|
+
downloads all chunks via `GET /debug-sessions/:token/download`, decrypts each
|
|
133
|
+
per-page stream, and writes `<pageId>/data.jsonl`, `<pageId>/video.webm`,
|
|
134
|
+
`<pageId>/meta.json`.
|
|
135
|
+
|
|
136
|
+
## Encryption
|
|
137
|
+
|
|
138
|
+
Each stream (data and video, per page) is encrypted with libsodium
|
|
139
|
+
`crypto_secretstream_xchacha20poly1305`: a random symmetric key drives the stream,
|
|
140
|
+
and that key is sealed with `crypto_box_seal` (X25519) to the server public key.
|
|
141
|
+
The first chunk of every stream is `[magic][version][sealedKeyLen][sealedKey][header]`;
|
|
142
|
+
subsequent chunks are the encrypted stream messages, the last carrying `TAG_FINAL`.
|
|
143
|
+
Chunks leave the browser already unreadable; only the holder of the X25519 private
|
|
144
|
+
key can open them. `libsodium-wrappers` is loaded dynamically, so it only ships
|
|
145
|
+
when debug mode is actually used.
|
|
146
|
+
|
|
147
|
+
> Cross-app continuity works because all apps live under `*.arvore.com.br` and
|
|
148
|
+
> share the session cookie. Screen recording restarts per app (the browser does
|
|
149
|
+
> not allow `getDisplayMedia` to persist across full page loads / origins), so the
|
|
150
|
+
> video is reconstructed per page rather than as one continuous file.
|
|
151
|
+
|
|
152
|
+
|
|
108
153
|
## Privacy & Security
|
|
109
154
|
|
|
110
155
|
- Sensitive fields are automatically redacted (passwords, tokens, secrets, etc.)
|
|
111
156
|
- Password inputs are never captured
|
|
112
157
|
- You control what gets captured via configuration
|
|
113
|
-
- No data leaves the browser unless
|
|
158
|
+
- No data leaves the browser unless a debug session is active
|
|
114
159
|
- All processing happens client-side
|
|
115
160
|
|
|
116
161
|
## Browser Support
|
package/dist/index.d.mts
CHANGED
|
@@ -15,11 +15,8 @@ interface BugJarConfig {
|
|
|
15
15
|
sensitiveFields: string[];
|
|
16
16
|
endpoint?: string;
|
|
17
17
|
onCapture?: (report: BugReport) => void;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
uiLabel: string;
|
|
21
|
-
hideOnMobile?: boolean;
|
|
22
|
-
uiCompactOnMobile?: boolean;
|
|
18
|
+
debugToken?: string;
|
|
19
|
+
debugSessionEndpoint?: string;
|
|
23
20
|
}
|
|
24
21
|
interface NetworkEntry {
|
|
25
22
|
id: string;
|
|
@@ -136,6 +133,12 @@ declare function generateSummary(report: BugReport): string;
|
|
|
136
133
|
|
|
137
134
|
declare function exportAsZip(report: BugReport, videoBlob: Blob | null): Promise<Blob>;
|
|
138
135
|
|
|
136
|
+
interface DebugSessionPayload {
|
|
137
|
+
token: string;
|
|
138
|
+
publicKey: string;
|
|
139
|
+
expiresAt: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
139
142
|
declare class BugJar {
|
|
140
143
|
private config;
|
|
141
144
|
private network;
|
|
@@ -148,7 +151,13 @@ declare class BugJar {
|
|
|
148
151
|
private screenshot;
|
|
149
152
|
private featureFlags;
|
|
150
153
|
private screenRecorder;
|
|
151
|
-
private
|
|
154
|
+
private sessionBorder;
|
|
155
|
+
private debugSession;
|
|
156
|
+
private dataUploader;
|
|
157
|
+
private videoUploader;
|
|
158
|
+
private pageId;
|
|
159
|
+
private dataInterval;
|
|
160
|
+
private finalized;
|
|
152
161
|
private started;
|
|
153
162
|
constructor(config?: Partial<BugJarConfig>);
|
|
154
163
|
start(): void;
|
|
@@ -158,7 +167,12 @@ declare class BugJar {
|
|
|
158
167
|
capture(userDescription?: string): Promise<BugReport>;
|
|
159
168
|
captureAndExport(userDescription?: string): Promise<BugReport>;
|
|
160
169
|
getReport(): Promise<BugReport>;
|
|
170
|
+
private startDebugSession;
|
|
171
|
+
private startDataStreaming;
|
|
172
|
+
private registerFlushHandlers;
|
|
173
|
+
private finalizePage;
|
|
174
|
+
endDebugSession(): void;
|
|
161
175
|
}
|
|
162
176
|
declare function init(config?: Partial<BugJarConfig>): BugJar;
|
|
163
177
|
|
|
164
|
-
export { BugJar, type BugJarConfig, type BugReport, BugJar as default, exportAsZip, generateSummary, init };
|
|
178
|
+
export { BugJar, type BugJarConfig, type BugReport, type DebugSessionPayload, BugJar as default, exportAsZip, generateSummary, init };
|
package/dist/index.d.ts
CHANGED
|
@@ -15,11 +15,8 @@ interface BugJarConfig {
|
|
|
15
15
|
sensitiveFields: string[];
|
|
16
16
|
endpoint?: string;
|
|
17
17
|
onCapture?: (report: BugReport) => void;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
uiLabel: string;
|
|
21
|
-
hideOnMobile?: boolean;
|
|
22
|
-
uiCompactOnMobile?: boolean;
|
|
18
|
+
debugToken?: string;
|
|
19
|
+
debugSessionEndpoint?: string;
|
|
23
20
|
}
|
|
24
21
|
interface NetworkEntry {
|
|
25
22
|
id: string;
|
|
@@ -136,6 +133,12 @@ declare function generateSummary(report: BugReport): string;
|
|
|
136
133
|
|
|
137
134
|
declare function exportAsZip(report: BugReport, videoBlob: Blob | null): Promise<Blob>;
|
|
138
135
|
|
|
136
|
+
interface DebugSessionPayload {
|
|
137
|
+
token: string;
|
|
138
|
+
publicKey: string;
|
|
139
|
+
expiresAt: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
139
142
|
declare class BugJar {
|
|
140
143
|
private config;
|
|
141
144
|
private network;
|
|
@@ -148,7 +151,13 @@ declare class BugJar {
|
|
|
148
151
|
private screenshot;
|
|
149
152
|
private featureFlags;
|
|
150
153
|
private screenRecorder;
|
|
151
|
-
private
|
|
154
|
+
private sessionBorder;
|
|
155
|
+
private debugSession;
|
|
156
|
+
private dataUploader;
|
|
157
|
+
private videoUploader;
|
|
158
|
+
private pageId;
|
|
159
|
+
private dataInterval;
|
|
160
|
+
private finalized;
|
|
152
161
|
private started;
|
|
153
162
|
constructor(config?: Partial<BugJarConfig>);
|
|
154
163
|
start(): void;
|
|
@@ -158,7 +167,12 @@ declare class BugJar {
|
|
|
158
167
|
capture(userDescription?: string): Promise<BugReport>;
|
|
159
168
|
captureAndExport(userDescription?: string): Promise<BugReport>;
|
|
160
169
|
getReport(): Promise<BugReport>;
|
|
170
|
+
private startDebugSession;
|
|
171
|
+
private startDataStreaming;
|
|
172
|
+
private registerFlushHandlers;
|
|
173
|
+
private finalizePage;
|
|
174
|
+
endDebugSession(): void;
|
|
161
175
|
}
|
|
162
176
|
declare function init(config?: Partial<BugJarConfig>): BugJar;
|
|
163
177
|
|
|
164
|
-
export { BugJar, type BugJarConfig, type BugReport, BugJar as default, exportAsZip, generateSummary, init };
|
|
178
|
+
export { BugJar, type BugJarConfig, type BugReport, type DebugSessionPayload, BugJar as default, exportAsZip, generateSummary, init };
|