@arvoretech/bug-jar 0.4.0 → 0.7.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 +56 -15
- package/dist/index.d.mts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.global.js +72 -46
- package/dist/index.js +73 -47
- package/dist/index.mjs +72 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ opens it in the real app, and the encrypted package is uploaded to S3. See
|
|
|
34
34
|
<script src="https://unpkg.com/@arvoretech/bug-jar"></script>
|
|
35
35
|
<script>
|
|
36
36
|
BugJar.init({
|
|
37
|
-
debugSessionEndpoint: 'https://
|
|
37
|
+
debugSessionEndpoint: 'https://livros.arvore.com.br/debug-sessions',
|
|
38
38
|
})
|
|
39
39
|
</script>
|
|
40
40
|
```
|
|
@@ -67,7 +67,7 @@ init({
|
|
|
67
67
|
onCapture: (report) => console.log(report),
|
|
68
68
|
|
|
69
69
|
// Debug session
|
|
70
|
-
debugSessionEndpoint: 'https://
|
|
70
|
+
debugSessionEndpoint: 'https://livros.arvore.com.br/debug-sessions',
|
|
71
71
|
// debugToken is read automatically from the ?debug=<token> query param
|
|
72
72
|
})
|
|
73
73
|
```
|
|
@@ -101,7 +101,7 @@ private S3 bucket via a presigned URL. No AWS credentials ever reach the browser
|
|
|
101
101
|
<script src="https://unpkg.com/@arvoretech/bug-jar"></script>
|
|
102
102
|
<script>
|
|
103
103
|
BugJar.init({
|
|
104
|
-
debugSessionEndpoint: 'https://
|
|
104
|
+
debugSessionEndpoint: 'https://livros.arvore.com.br/debug-sessions',
|
|
105
105
|
// debugToken is read automatically from the ?debug=<token> query param
|
|
106
106
|
})
|
|
107
107
|
</script>
|
|
@@ -110,18 +110,59 @@ private S3 bucket via a presigned URL. No AWS credentials ever reach the browser
|
|
|
110
110
|
Flow:
|
|
111
111
|
|
|
112
112
|
1. Staff (admin) calls `POST /debug-sessions` with the target app URL and gets back
|
|
113
|
-
a `debugLink` (`https://app.../?debug=<token>`), the
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
3. The user
|
|
118
|
-
(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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://livros.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
|
+
## Speed test
|
|
137
|
+
|
|
138
|
+
At the start of each page, Bug Jar runs a fast.com-style network speed test
|
|
139
|
+
against the Árvore backend and stores the result in that page's `meta.json`:
|
|
140
|
+
|
|
141
|
+
- **latency** — minimum and jitter from repeated `GET /speedtest/ping`
|
|
142
|
+
- **download (Mbps)** — 8 MB pulled across 4 parallel streams
|
|
143
|
+
(`GET /speedtest/download?bytes=N`)
|
|
144
|
+
- **upload (Mbps)** — 4 MB sent to `POST /speedtest/upload` (server discards it)
|
|
145
|
+
|
|
146
|
+
It measures the real end-to-end throughput between the user and our
|
|
147
|
+
infrastructure (which is exactly what we want when debugging "the app is slow").
|
|
148
|
+
The test runs once per page and is best-effort — failures leave the fields null
|
|
149
|
+
without blocking the session.
|
|
150
|
+
|
|
151
|
+
## Encryption
|
|
152
|
+
|
|
153
|
+
Each stream (data and video, per page) is encrypted with libsodium
|
|
154
|
+
`crypto_secretstream_xchacha20poly1305`: a random symmetric key drives the stream,
|
|
155
|
+
and that key is sealed with `crypto_box_seal` (X25519) to the server public key.
|
|
156
|
+
The first chunk of every stream is `[magic][version][sealedKeyLen][sealedKey][header]`;
|
|
157
|
+
subsequent chunks are the encrypted stream messages, the last carrying `TAG_FINAL`.
|
|
158
|
+
Chunks leave the browser already unreadable; only the holder of the X25519 private
|
|
159
|
+
key can open them. `libsodium-wrappers` is loaded dynamically, so it only ships
|
|
160
|
+
when debug mode is actually used.
|
|
161
|
+
|
|
162
|
+
> Cross-app continuity works because all apps live under `*.arvore.com.br` and
|
|
163
|
+
> share the session cookie. Screen recording restarts per app (the browser does
|
|
164
|
+
> not allow `getDisplayMedia` to persist across full page loads / origins), so the
|
|
165
|
+
> video is reconstructed per page rather than as one continuous file.
|
|
125
166
|
|
|
126
167
|
|
|
127
168
|
## Privacy & Security
|
package/dist/index.d.mts
CHANGED
|
@@ -17,6 +17,7 @@ interface BugJarConfig {
|
|
|
17
17
|
onCapture?: (report: BugReport) => void;
|
|
18
18
|
debugToken?: string;
|
|
19
19
|
debugSessionEndpoint?: string;
|
|
20
|
+
ignoreUrls?: string[];
|
|
20
21
|
}
|
|
21
22
|
interface NetworkEntry {
|
|
22
23
|
id: string;
|
|
@@ -136,8 +137,6 @@ declare function exportAsZip(report: BugReport, videoBlob: Blob | null): Promise
|
|
|
136
137
|
interface DebugSessionPayload {
|
|
137
138
|
token: string;
|
|
138
139
|
publicKey: string;
|
|
139
|
-
presignedPut: string;
|
|
140
|
-
reportId: string;
|
|
141
140
|
expiresAt: number;
|
|
142
141
|
}
|
|
143
142
|
|
|
@@ -153,8 +152,14 @@ declare class BugJar {
|
|
|
153
152
|
private screenshot;
|
|
154
153
|
private featureFlags;
|
|
155
154
|
private screenRecorder;
|
|
156
|
-
private
|
|
155
|
+
private sessionBorder;
|
|
157
156
|
private debugSession;
|
|
157
|
+
private dataUploader;
|
|
158
|
+
private videoUploader;
|
|
159
|
+
private pageId;
|
|
160
|
+
private dataInterval;
|
|
161
|
+
private lastFlushTs;
|
|
162
|
+
private finalized;
|
|
158
163
|
private started;
|
|
159
164
|
constructor(config?: Partial<BugJarConfig>);
|
|
160
165
|
start(): void;
|
|
@@ -165,7 +170,11 @@ declare class BugJar {
|
|
|
165
170
|
captureAndExport(userDescription?: string): Promise<BugReport>;
|
|
166
171
|
getReport(): Promise<BugReport>;
|
|
167
172
|
private startDebugSession;
|
|
168
|
-
private
|
|
173
|
+
private startDataStreaming;
|
|
174
|
+
private registerFlushHandlers;
|
|
175
|
+
private finalizePage;
|
|
176
|
+
private runSpeedtest;
|
|
177
|
+
endDebugSession(): void;
|
|
169
178
|
}
|
|
170
179
|
declare function init(config?: Partial<BugJarConfig>): BugJar;
|
|
171
180
|
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ interface BugJarConfig {
|
|
|
17
17
|
onCapture?: (report: BugReport) => void;
|
|
18
18
|
debugToken?: string;
|
|
19
19
|
debugSessionEndpoint?: string;
|
|
20
|
+
ignoreUrls?: string[];
|
|
20
21
|
}
|
|
21
22
|
interface NetworkEntry {
|
|
22
23
|
id: string;
|
|
@@ -136,8 +137,6 @@ declare function exportAsZip(report: BugReport, videoBlob: Blob | null): Promise
|
|
|
136
137
|
interface DebugSessionPayload {
|
|
137
138
|
token: string;
|
|
138
139
|
publicKey: string;
|
|
139
|
-
presignedPut: string;
|
|
140
|
-
reportId: string;
|
|
141
140
|
expiresAt: number;
|
|
142
141
|
}
|
|
143
142
|
|
|
@@ -153,8 +152,14 @@ declare class BugJar {
|
|
|
153
152
|
private screenshot;
|
|
154
153
|
private featureFlags;
|
|
155
154
|
private screenRecorder;
|
|
156
|
-
private
|
|
155
|
+
private sessionBorder;
|
|
157
156
|
private debugSession;
|
|
157
|
+
private dataUploader;
|
|
158
|
+
private videoUploader;
|
|
159
|
+
private pageId;
|
|
160
|
+
private dataInterval;
|
|
161
|
+
private lastFlushTs;
|
|
162
|
+
private finalized;
|
|
158
163
|
private started;
|
|
159
164
|
constructor(config?: Partial<BugJarConfig>);
|
|
160
165
|
start(): void;
|
|
@@ -165,7 +170,11 @@ declare class BugJar {
|
|
|
165
170
|
captureAndExport(userDescription?: string): Promise<BugReport>;
|
|
166
171
|
getReport(): Promise<BugReport>;
|
|
167
172
|
private startDebugSession;
|
|
168
|
-
private
|
|
173
|
+
private startDataStreaming;
|
|
174
|
+
private registerFlushHandlers;
|
|
175
|
+
private finalizePage;
|
|
176
|
+
private runSpeedtest;
|
|
177
|
+
endDebugSession(): void;
|
|
169
178
|
}
|
|
170
179
|
declare function init(config?: Partial<BugJarConfig>): BugJar;
|
|
171
180
|
|