@backtrack-js/browser 0.4.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/LICENSE +9 -0
- package/README.md +291 -0
- package/bin/cli.cjs +51 -0
- package/bin/cli.js +2 -0
- package/bin/symbolize.cjs +654 -0
- package/bin/viewer.cjs +143 -0
- package/dist/capturers/console.d.ts +18 -0
- package/dist/capturers/console.d.ts.map +1 -0
- package/dist/capturers/errors.d.ts +22 -0
- package/dist/capturers/errors.d.ts.map +1 -0
- package/dist/capturers/index.d.ts +8 -0
- package/dist/capturers/index.d.ts.map +1 -0
- package/dist/capturers/navigation.d.ts +20 -0
- package/dist/capturers/navigation.d.ts.map +1 -0
- package/dist/capturers/network.d.ts +30 -0
- package/dist/capturers/network.d.ts.map +1 -0
- package/dist/capturers/performance.d.ts +15 -0
- package/dist/capturers/performance.d.ts.map +1 -0
- package/dist/capturers/route-matcher.d.ts +10 -0
- package/dist/capturers/route-matcher.d.ts.map +1 -0
- package/dist/capturers/rrweb.d.ts +18 -0
- package/dist/capturers/rrweb.d.ts.map +1 -0
- package/dist/capturers/sanitizer.d.ts +37 -0
- package/dist/capturers/sanitizer.d.ts.map +1 -0
- package/dist/core/flight-recorder.d.ts +75 -0
- package/dist/core/flight-recorder.d.ts.map +1 -0
- package/dist/core/state-machine.d.ts +55 -0
- package/dist/core/state-machine.d.ts.map +1 -0
- package/dist/index.cjs +1803 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5763 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/batch-writer.d.ts +57 -0
- package/dist/storage/batch-writer.d.ts.map +1 -0
- package/dist/storage/db.d.ts +39 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/incident-manager.d.ts +107 -0
- package/dist/storage/incident-manager.d.ts.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/retention.d.ts +41 -0
- package/dist/storage/retention.d.ts.map +1 -0
- package/dist/storage/session.d.ts +32 -0
- package/dist/storage/session.d.ts.map +1 -0
- package/dist/types/artifact.d.ts +72 -0
- package/dist/types/artifact.d.ts.map +1 -0
- package/dist/types/chunk.d.ts +32 -0
- package/dist/types/chunk.d.ts.map +1 -0
- package/dist/types/health.d.ts +16 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/incident.d.ts +35 -0
- package/dist/types/incident.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/options.d.ts +84 -0
- package/dist/types/options.d.ts.map +1 -0
- package/dist/types/timeline.d.ts +74 -0
- package/dist/types/timeline.d.ts.map +1 -0
- package/dist/types/transport.d.ts +6 -0
- package/dist/types/transport.d.ts.map +1 -0
- package/dist/utils/compression.d.ts +28 -0
- package/dist/utils/compression.d.ts.map +1 -0
- package/dist/utils/gist-uploader.d.ts +12 -0
- package/dist/utils/gist-uploader.d.ts.map +1 -0
- package/dist/utils/markdown.d.ts +12 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/validation/validate.d.ts +27 -0
- package/dist/validation/validate.d.ts.map +1 -0
- package/dist/viewer/assets/backtrack-favico-Ds-TMW85.png +0 -0
- package/dist/viewer/assets/index-D6J82Kcf.js +191 -0
- package/dist/viewer/assets/index-cVrM07fr.css +1 -0
- package/dist/viewer/index.html +19 -0
- package/dist/widget/annotator.d.ts +85 -0
- package/dist/widget/annotator.d.ts.map +1 -0
- package/dist/widget/styles.d.ts +2 -0
- package/dist/widget/styles.d.ts.map +1 -0
- package/dist/widget/widget.d.ts +78 -0
- package/dist/widget/widget.d.ts.map +1 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Copyright (c) 2026 Matheus Moorete. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to inspect, run, and use the Software free of charge for evaluation and application integration, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
Redistribution, modification, sublicensing, or creating derivative works of the Software, in whole or in part, without the express written permission of the copyright holder is strictly prohibited.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Backtrack (@backtrack-js/browser)
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@backtrack-js/browser)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
**Backtrack** is a privacy-first, client-side session recorder and time-travel engine for web applications. It captures pre-bug context (DOM mutations, console logs, network requests, navigation, and unhandled errors) in a rolling local ring-buffer, allowing developers to backtrack user sessions and reproduce bugs with exact fidelity.
|
|
7
|
+
|
|
8
|
+
- 🔒 **Privacy-First:** Strict data masking (passwords, credit cards, inputs, authorization headers). No data leaves the browser unless explicitly exported.
|
|
9
|
+
- ⚡ **Lightweight & Framework-Agnostic:** Pure TypeScript core. Zero framework dependencies (no React required in your client bundle).
|
|
10
|
+
- 🔄 **Rolling Ring Buffer:** Continuously stores the last $N$ minutes of user activity in IndexedDB with automatic retention and storage limits.
|
|
11
|
+
- 🎬 **Full Session Replay:** Powered by [rrweb](https://github.com/rrweb-io/rrweb) with synchronized timeline events.
|
|
12
|
+
- 🛠 **Built-in Incident Viewer:** Includes an interactive web inspector and CLI to replay incidents offline.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# npm
|
|
20
|
+
npm install @backtrack-js/browser
|
|
21
|
+
|
|
22
|
+
# yarn
|
|
23
|
+
yarn add @backtrack-js/browser
|
|
24
|
+
|
|
25
|
+
# pnpm
|
|
26
|
+
pnpm add @backtrack-js/browser
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Backtrack } from '@backtrack-js/browser';
|
|
35
|
+
|
|
36
|
+
// 1. Initialize Backtrack
|
|
37
|
+
const recorder = new Backtrack({
|
|
38
|
+
bufferMinutes: 5, // Keep last 5 minutes of activity in rolling buffer
|
|
39
|
+
afterErrorSeconds: 15, // Keep recording for 15 seconds after an error occurs
|
|
40
|
+
maxStorageMb: 50, // Retention target for rolling buffer (protected incidents are never auto-pruned)
|
|
41
|
+
captureHttpStatus: [500, 502, 503, 504], // Auto-trigger on server errors
|
|
42
|
+
network: {
|
|
43
|
+
capturePayloads: false, // Bodies are disabled by default for privacy
|
|
44
|
+
maxPayloadSize: 64 * 1024
|
|
45
|
+
},
|
|
46
|
+
privacy: {
|
|
47
|
+
maskAllInputs: true, // Mask all form inputs by default
|
|
48
|
+
blockMedia: true // Replace images/media with placeholders
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 2. Start recording
|
|
53
|
+
await recorder.start();
|
|
54
|
+
|
|
55
|
+
// 3. (Optional) Manually trigger an incident on user feedback or caught error
|
|
56
|
+
const incidentId = await recorder.capture('User reported payment bug', 30);
|
|
57
|
+
console.log('Incident captured:', incidentId);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Privacy & Data Masking
|
|
63
|
+
|
|
64
|
+
Backtrack is designed with strict security defaults:
|
|
65
|
+
|
|
66
|
+
- **Inputs & Forms:** All `<input>`, `<textarea>`, and `<select>` values are masked by default (`maskAllInputs: true`).
|
|
67
|
+
- **Network Headers:** Authorization, Cookies, API keys, and Bearer tokens are automatically redacted.
|
|
68
|
+
- **Sensitive Selectors:** Use standard selectors or CSS classes to protect proprietary or personal data:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const recorder = new Backtrack({
|
|
72
|
+
privacy: {
|
|
73
|
+
maskAllInputs: true,
|
|
74
|
+
blockMedia: true,
|
|
75
|
+
blockSelector: '.backtrack-block, [data-private]', // Completely hides elements in replay
|
|
76
|
+
maskTextSelector: '.backtrack-mask, .customer-pII', // Scrambles text content
|
|
77
|
+
sanitizeUrl: (url) => {
|
|
78
|
+
// Remove sensitive query parameters
|
|
79
|
+
url.searchParams.delete('token');
|
|
80
|
+
return url.toString();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Network & Payload Capture
|
|
89
|
+
|
|
90
|
+
Network requests and responses can be configured using `network` options:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const recorder = new Backtrack({
|
|
94
|
+
network: {
|
|
95
|
+
capturePayloads: false,
|
|
96
|
+
maxPayloadSize: 64 * 1024
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- **Bodies are not captured by default (`capturePayloads: false`):** Request and response bodies are disabled by default to prevent unintentional data leakage. HTTP method, URL, status, timing, and sanitized headers continue to be captured.
|
|
102
|
+
- **Enabling bodies increases the risk of PII:** Enabling `capturePayloads: true` should be done mindfully according to your compliance and privacy requirements.
|
|
103
|
+
- **Sanitization remains active:** Even when payload capture is explicitly enabled, Backtrack continues to sanitize and redact known sensitive keys (passwords, tokens, credentials, etc.) and patterns (Bearer tokens, emails, CPFs, cards).
|
|
104
|
+
- **`maxPayloadSize` is a limit per payload, not per incident:** It bounds each individual request or response body (default is `64 * 1024` bytes). Payloads exceeding this limit are truncated safely with a truncation marker.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Capturing Incidents
|
|
109
|
+
|
|
110
|
+
### Automatic Triggers
|
|
111
|
+
Backtrack automatically triggers and freezes an incident when:
|
|
112
|
+
1. An unhandled window exception occurs (`window.onerror`).
|
|
113
|
+
2. An unhandled promise rejection occurs (`unhandledrejection`).
|
|
114
|
+
3. An HTTP request returns a matching error status (`captureHttpStatus`).
|
|
115
|
+
|
|
116
|
+
### Manual Capture
|
|
117
|
+
You can trigger incident recording manually anywhere in your code (e.g. from an error boundary or user feedback modal):
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Capture the last 60 seconds of context
|
|
121
|
+
const incidentId = await recorder.capture('Checkout error', 60);
|
|
122
|
+
|
|
123
|
+
// Export the complete incident artifact as a JSON object
|
|
124
|
+
const artifact = await recorder.exportIncident(incidentId);
|
|
125
|
+
|
|
126
|
+
// Download or send artifact to your support/logging service
|
|
127
|
+
const jsonBlob = new Blob([JSON.stringify(artifact, null, 2)], { type: 'application/json' });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Exception Capture
|
|
131
|
+
In React Error Boundaries or try/catch blocks:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
recorder.captureException(error, {
|
|
135
|
+
source: 'react',
|
|
136
|
+
componentStack: errorInfo.componentStack
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Remote Transport & beforeSend
|
|
143
|
+
|
|
144
|
+
Backtrack allows applications to automatically dispatch finalized incidents to any remote endpoint or telemetry backend using `transport` and `beforeSend`:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const recorder = new Backtrack({
|
|
148
|
+
transport: {
|
|
149
|
+
async send(artifact) {
|
|
150
|
+
const response = await fetch('/api/backtrack/incidents', {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
headers: {
|
|
153
|
+
'content-type': 'application/json'
|
|
154
|
+
},
|
|
155
|
+
body: JSON.stringify(artifact)
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(`Upload failed: ${response.status}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
beforeSend(artifact) {
|
|
165
|
+
if (artifact.environment.url.includes('/healthcheck')) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return artifact;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Dispatch Flow & Semantics
|
|
175
|
+
|
|
176
|
+
- **Sent Only When Finalized:** Incidents are dispatched only after they have been completely finalized and safely written to IndexedDB.
|
|
177
|
+
```text
|
|
178
|
+
incidente finalizado
|
|
179
|
+
↓
|
|
180
|
+
montar artefato
|
|
181
|
+
↓
|
|
182
|
+
beforeSend
|
|
183
|
+
↓
|
|
184
|
+
null? cancela envio
|
|
185
|
+
↓
|
|
186
|
+
transport.send
|
|
187
|
+
```
|
|
188
|
+
- **Role of `beforeSend`:**
|
|
189
|
+
- Affects **only** the automated transmission via `transport.send`.
|
|
190
|
+
- Does **not** mutate the artifact stored in local IndexedDB.
|
|
191
|
+
- Does **not** affect `getArtifact()` or explicit file downloads via `exportIncident()`.
|
|
192
|
+
- Returning `null` cancels the upload.
|
|
193
|
+
- If `beforeSend` throws an error, the upload is aborted cleanly without affecting the application or the recorded incident.
|
|
194
|
+
- **Fault Isolation:** Network or transport errors will never break the host application, reject user captures, delete local incidents, prevent finalization, or trigger unhandled promise rejections.
|
|
195
|
+
- **Concurrency & Delivery Guarantees:**
|
|
196
|
+
- Each finalized incident triggers at most one upload attempt.
|
|
197
|
+
- Multiple incidents can upload concurrently.
|
|
198
|
+
- `stop()` does not wait for pending network uploads.
|
|
199
|
+
- There is currently no delivery guarantee (persistent retry queue, offline backoff, etc.); if transport fails, the incident remains saved in local IndexedDB and can be accessed or resent by the application.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Release and trace correlation
|
|
204
|
+
|
|
205
|
+
Backtrack artifacts (`formatVersion: 2`) provide an observability `context` to correlate client-side replays with backend traces, application releases, and environments.
|
|
206
|
+
|
|
207
|
+
### Static Context
|
|
208
|
+
|
|
209
|
+
Configure static metadata when initializing Backtrack:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const recorder = new Backtrack({
|
|
213
|
+
context: {
|
|
214
|
+
release: 'checkout-web@2.14.0',
|
|
215
|
+
environment: 'production',
|
|
216
|
+
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
217
|
+
spanId: '00f067aa0ba902b7'
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Dynamic Context (`getContext`)
|
|
223
|
+
|
|
224
|
+
When trace IDs or active environments change dynamically during user navigation (e.g. from OpenTelemetry, Datadog, Sentry, or custom APM SDKs), use `getContext` to evaluate the latest context at incident export time:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const recorder = new Backtrack({
|
|
228
|
+
context: {
|
|
229
|
+
release: 'checkout-web@2.14.0',
|
|
230
|
+
environment: 'production'
|
|
231
|
+
},
|
|
232
|
+
getContext: async () => {
|
|
233
|
+
const activeSpan = tracer.getActiveSpan();
|
|
234
|
+
const spanContext = activeSpan?.spanContext();
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
release: 'checkout-web@2.14.0',
|
|
238
|
+
environment: 'production',
|
|
239
|
+
traceId: spanContext?.traceId,
|
|
240
|
+
spanId: spanContext?.spanId
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Correlation Semantics
|
|
247
|
+
|
|
248
|
+
- **Precedence:** `getContext() > context (static) > environment metadata`.
|
|
249
|
+
- **Trace correlation scope:** Backtrack **only correlates** external trace and span identifiers supplied by your application. It does **not** generate backend traces, start trace spans, or perform distributed instrumentation on its own.
|
|
250
|
+
- **Normalization:** `traceId` (32 hex characters) and `spanId` (16 hex characters) are validated and normalized to lowercase. A `spanId` cannot be supplied without an accompanying `traceId`.
|
|
251
|
+
- **Fault Resilience:** If `getContext()` throws an error or rejects, incident export continues safely using available static context without failing the export or losing incident data.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Inspecting Incidents (Viewer)
|
|
256
|
+
|
|
257
|
+
Backtrack includes an offline visualizer with video replay, console logs, network inspection, and breadcrumbs.
|
|
258
|
+
|
|
259
|
+
### Launch the Viewer CLI
|
|
260
|
+
Run the embedded CLI to launch the local visualizer:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npx backtrack
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
This starts a local server at `http://localhost:5173/` and opens your default browser. Drag and drop any exported incident `.json` file to replay it!
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## API Reference
|
|
271
|
+
|
|
272
|
+
### `Backtrack` (`FlightRecorder`)
|
|
273
|
+
|
|
274
|
+
| Method | Return Type | Description |
|
|
275
|
+
| --- | --- | --- |
|
|
276
|
+
| `start()` | `Promise<void>` | Initializes storage and begins recording. |
|
|
277
|
+
| `stop()` | `Promise<void>` | Stops recording, flushes pending batches to storage, and detaches listeners. |
|
|
278
|
+
| `capture(reason?, windowSeconds?)` | `Promise<string>` | Manually captures and finalizes an incident. Returns incident ID. |
|
|
279
|
+
| `captureException(error, context?)` | `Promise<string \| undefined> \| void` | Records a caught exception and triggers an incident. |
|
|
280
|
+
| `listIncidents()` | `Promise<IncidentSummary[]>` | Lists all stored incidents. |
|
|
281
|
+
| `getArtifact(incidentId)` | `Promise<FlightRecorderArtifact>` | Retrieves the full artifact for an incident (v1 or v2). |
|
|
282
|
+
| `exportIncident(incidentId)` | `Promise<FlightRecorderArtifact>` | Exports the artifact and marks it finalized. |
|
|
283
|
+
| `deleteIncident(incidentId)` | `Promise<void>` | Deletes an incident from local storage. |
|
|
284
|
+
| `clear()` | `Promise<void>` | Clears all stored chunks, sessions, and incidents. |
|
|
285
|
+
| `getHealth()` | `RecorderHealth` | Returns current buffer status, storage usage, and state. |
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
[MIT](LICENSE)
|
package/bin/cli.cjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
function printHelp() {
|
|
4
|
+
console.log(`
|
|
5
|
+
Uso: backtrack [comando] [opções]
|
|
6
|
+
|
|
7
|
+
Comandos:
|
|
8
|
+
viewer Inicia o servidor local do Backtrack Viewer (padrão)
|
|
9
|
+
symbolize <incidente> [opções] Resolve stack traces usando source maps
|
|
10
|
+
|
|
11
|
+
Opções globais:
|
|
12
|
+
-h, --help Mostra esta mensagem de ajuda
|
|
13
|
+
|
|
14
|
+
Execute "backtrack symbolize --help" para ver as opções do comando symbolize.
|
|
15
|
+
`.trim());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const command = process.argv[2];
|
|
19
|
+
|
|
20
|
+
if (command === '--help' || command === '-h') {
|
|
21
|
+
printHelp();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (command === 'symbolize') {
|
|
26
|
+
const result = require('./symbolize.cjs').run(process.argv.slice(3));
|
|
27
|
+
if (result instanceof Promise) {
|
|
28
|
+
result.then((code) => {
|
|
29
|
+
if (typeof code === 'number') {
|
|
30
|
+
process.exitCode = code;
|
|
31
|
+
}
|
|
32
|
+
}).catch((err) => {
|
|
33
|
+
console.error(err);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
!command ||
|
|
42
|
+
command === 'viewer' ||
|
|
43
|
+
command === '--port' ||
|
|
44
|
+
command === '-p'
|
|
45
|
+
) {
|
|
46
|
+
require('./viewer.cjs');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.error(`Comando desconhecido: ${command}`);
|
|
51
|
+
process.exitCode = 1;
|
package/bin/cli.js
ADDED