brakit 0.7.4 → 0.7.6
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 +78 -50
- package/dist/api.d.ts +116 -3
- package/dist/api.js +615 -353
- package/dist/bin/brakit.js +38 -31
- package/dist/runtime/index.js +1150 -510
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Every request, query, and security issue — before you ship.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
<h1 align="center"><img src="docs/images/icon.png" height="24" alt="" /> Brakit</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<b>See what your app is actually doing.</b> <br />
|
|
5
|
+
Every request, query, and security issue — before you ship. <br />
|
|
6
|
+
<b>Open source · Local only · Zero config · 2 dependencies</b>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h3 align="center">
|
|
10
|
+
<a href="docs/design/architecture.md">Architecture</a> •
|
|
11
|
+
<a href="https://brakit.ai">Website</a> •
|
|
12
|
+
<a href="CONTRIBUTING.md">Contributing</a>
|
|
13
|
+
</h3>
|
|
14
|
+
|
|
15
|
+
<h4 align="center">
|
|
16
|
+
<a href="LICENSE">
|
|
17
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" />
|
|
18
|
+
</a>
|
|
19
|
+
<a href="https://nodejs.org">
|
|
20
|
+
<img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg" alt="Node >= 18" />
|
|
21
|
+
</a>
|
|
22
|
+
<a href="https://typescriptlang.org">
|
|
23
|
+
<img src="https://img.shields.io/badge/built%20with-TypeScript-3178c6.svg" alt="TypeScript" />
|
|
24
|
+
</a>
|
|
25
|
+
<a href="CONTRIBUTING.md">
|
|
26
|
+
<img src="https://img.shields.io/badge/PRs-Welcome-brightgreen" alt="PRs welcome!" />
|
|
27
|
+
</a>
|
|
28
|
+
</h4>
|
|
12
29
|
|
|
13
30
|
---
|
|
14
31
|
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img width="700" src="docs/images/dashboard.png" alt="Brakit Dashboard" />
|
|
34
|
+
</p>
|
|
35
|
+
|
|
15
36
|
## Quick Start
|
|
16
37
|
|
|
17
38
|
```bash
|
|
@@ -28,18 +49,19 @@ Dashboard at `http://localhost:<port>/__brakit`. Insights in the terminal.
|
|
|
28
49
|
|
|
29
50
|
> **Requirements:** Node.js >= 18 and a project with `package.json`.
|
|
30
51
|
|
|
31
|
-
[Documentation](https://brakit.ai/docs) · [Website](https://brakit.ai)
|
|
32
|
-
|
|
33
52
|
---
|
|
34
53
|
|
|
35
54
|
## What You Get
|
|
36
55
|
|
|
37
|
-
- **
|
|
56
|
+
- **Live dashboard** at `/__brakit` — performance overview, request history, scatter charts, real-time via SSE
|
|
57
|
+
- **8 security rules** scanned against live traffic — leaked secrets, PII in responses, missing auth flags
|
|
58
|
+
- **Time breakdown** — every endpoint shows where time goes: DB, Fetch, or App code
|
|
59
|
+
- **N+1 query detection** — same query pattern repeated 5+ times in a single request
|
|
60
|
+
- **Regression detection** — p95 latency or query count increased vs. previous session
|
|
38
61
|
- **Action-level visibility** — see "Sign Up" and "Load Dashboard", not 47 raw HTTP requests
|
|
39
62
|
- **Duplicate detection** — same API called twice? Flagged with redundancy percentage
|
|
40
|
-
- **N+1 query detection** — same query pattern repeated 5+ times in a single request? That's an N+1
|
|
41
63
|
- **Full server tracing** — fetch calls, DB queries, console logs, errors — zero code changes
|
|
42
|
-
- **
|
|
64
|
+
- **Response overfetch** — large JSON responses with many fields your client doesn't use
|
|
43
65
|
- **Performance tracking** — health grades and p95 trends across dev sessions
|
|
44
66
|
|
|
45
67
|
---
|
|
@@ -56,16 +78,16 @@ Brakit watches every action your app takes — not raw HTTP noise, but what actu
|
|
|
56
78
|
|
|
57
79
|
8 high-confidence rules that scan your live traffic and flag real issues — not theoretical ones:
|
|
58
80
|
|
|
59
|
-
| | Rule
|
|
60
|
-
| ------------ |
|
|
61
|
-
| **Critical** | Exposed Secret
|
|
62
|
-
| **Critical** | Token in URL
|
|
63
|
-
| **Critical** | Stack Trace Leak
|
|
64
|
-
| **Critical** | Error Info Leak
|
|
65
|
-
| Warning | PII in Response
|
|
66
|
-
| Warning | Insecure Cookie
|
|
67
|
-
| Warning | Sensitive Logs
|
|
68
|
-
| Warning | CORS + Credentials | `credentials: true` with wildcard origin
|
|
81
|
+
| | Rule | What it catches |
|
|
82
|
+
| ------------ | ------------------ | -------------------------------------------------------------------------------- |
|
|
83
|
+
| **Critical** | Exposed Secret | Response contains `password`, `api_key`, `client_secret` fields with real values |
|
|
84
|
+
| **Critical** | Token in URL | Auth tokens in query parameters instead of headers |
|
|
85
|
+
| **Critical** | Stack Trace Leak | Internal stack traces sent to the client |
|
|
86
|
+
| **Critical** | Error Info Leak | DB connection strings, SQL queries, or secret values in error responses |
|
|
87
|
+
| Warning | PII in Response | API echoes back emails, returns full user records with internal IDs |
|
|
88
|
+
| Warning | Insecure Cookie | Missing `HttpOnly` or `SameSite` flags |
|
|
89
|
+
| Warning | Sensitive Logs | Passwords, secrets, or token values in console output |
|
|
90
|
+
| Warning | CORS + Credentials | `credentials: true` with wildcard origin |
|
|
69
91
|
|
|
70
92
|
---
|
|
71
93
|
|
|
@@ -93,27 +115,27 @@ Instrumentation hooks capture fetch calls, DB queries, console output, and error
|
|
|
93
115
|
|
|
94
116
|
Brakit never runs in production. 7 independent layers ensure it:
|
|
95
117
|
|
|
96
|
-
| #
|
|
97
|
-
|
|
98
|
-
| 1
|
|
99
|
-
| 2
|
|
100
|
-
| 3
|
|
101
|
-
| 4
|
|
102
|
-
| 5
|
|
103
|
-
| 6
|
|
104
|
-
| 7
|
|
118
|
+
| # | Layer | How it blocks |
|
|
119
|
+
| --- | ---------------------------- | ----------------------------------------- |
|
|
120
|
+
| 1 | `shouldActivate()` | Checks `NODE_ENV` + 15 cloud/CI env vars |
|
|
121
|
+
| 2 | `instrumentation.ts` guard | Its own `NODE_ENV !== 'production'` check |
|
|
122
|
+
| 3 | devDependency | Pruned in production builds |
|
|
123
|
+
| 4 | `try/catch` on import | Missing module = silent no-op |
|
|
124
|
+
| 5 | Localhost-only dashboard | Non-local IPs get 404 on `/__brakit` |
|
|
125
|
+
| 6 | `safeWrap` + circuit breaker | 10 errors = brakit self-disables |
|
|
126
|
+
| 7 | `BRAKIT_DISABLE=true` | Manual kill switch |
|
|
105
127
|
|
|
106
128
|
### Supported Frameworks
|
|
107
129
|
|
|
108
|
-
| Framework
|
|
109
|
-
|
|
|
110
|
-
| Next.js
|
|
111
|
-
| Remix
|
|
112
|
-
| Nuxt
|
|
113
|
-
| Vite
|
|
114
|
-
| Astro
|
|
115
|
-
| Express
|
|
116
|
-
| Fastify
|
|
130
|
+
| Framework | Status |
|
|
131
|
+
| --------- | -------------------------- |
|
|
132
|
+
| Next.js | Full support (auto-detect) |
|
|
133
|
+
| Remix | Auto-detect |
|
|
134
|
+
| Nuxt | Auto-detect |
|
|
135
|
+
| Vite | Auto-detect |
|
|
136
|
+
| Astro | Auto-detect |
|
|
137
|
+
| Express | Auto-detect |
|
|
138
|
+
| Fastify | Auto-detect |
|
|
117
139
|
|
|
118
140
|
### Supported Databases
|
|
119
141
|
|
|
@@ -157,34 +179,40 @@ Only 2 production dependencies: `citty` (CLI) and `picocolors` (terminal colors)
|
|
|
157
179
|
|
|
158
180
|
```
|
|
159
181
|
src/
|
|
160
|
-
runtime/ In-process entry point,
|
|
161
|
-
analysis/
|
|
182
|
+
runtime/ In-process entry point, interceptor, capture, safety
|
|
183
|
+
analysis/ Insights engine and security scanner
|
|
184
|
+
insights/ InsightRule implementations (one file per rule)
|
|
162
185
|
rules/ SecurityRule implementations (one file per rule)
|
|
163
186
|
cli/ CLI commands (install, uninstall)
|
|
187
|
+
constants/ Shared thresholds, route paths, limits
|
|
164
188
|
dashboard/
|
|
165
189
|
api/ REST handlers — requests, flows, telemetry, metrics
|
|
166
190
|
client/ Browser JS generated as template strings
|
|
167
191
|
views/ Tab renderers (overview, flows, graph, etc.)
|
|
168
192
|
styles/ CSS modules
|
|
169
193
|
detect/ Framework auto-detection
|
|
170
|
-
instrument/
|
|
194
|
+
instrument/ Database adapters and instrumentation hooks
|
|
171
195
|
adapters/ BrakitAdapter implementations (one file per library)
|
|
172
196
|
hooks/ Core hooks (fetch, console, errors, context)
|
|
197
|
+
output/ Terminal insight listener
|
|
173
198
|
store/ In-memory telemetry stores + persistent metrics
|
|
174
199
|
types/ TypeScript definitions by domain
|
|
200
|
+
utils/ Shared utilities (collections, format, math, endpoint)
|
|
175
201
|
```
|
|
176
202
|
|
|
177
203
|
---
|
|
178
204
|
|
|
179
205
|
## Contributing
|
|
180
206
|
|
|
181
|
-
Brakit is early and moving fast. The most common contributions — adding a
|
|
182
|
-
database adapter
|
|
183
|
-
interface. See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
207
|
+
Brakit is early and moving fast. The most common contributions — adding a
|
|
208
|
+
database adapter, a security rule, or an insight rule — each require exactly
|
|
209
|
+
one file and one interface. See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
210
|
+
step-by-step guides.
|
|
184
211
|
|
|
185
212
|
Some areas where help would be great:
|
|
186
213
|
|
|
187
214
|
- **Database adapters** — Drizzle, Mongoose, SQLite, MongoDB
|
|
215
|
+
- **Insight rules** — New performance patterns, custom thresholds
|
|
188
216
|
- **Security rules** — More patterns, configurable severity
|
|
189
217
|
- **Dashboard** — Request diff, timeline view, HAR export
|
|
190
218
|
|
package/dist/api.d.ts
CHANGED
|
@@ -101,6 +101,54 @@ type TelemetryEvent = {
|
|
|
101
101
|
data: Omit<TracedQuery, "id">;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
+
interface SessionMetric {
|
|
105
|
+
sessionId: string;
|
|
106
|
+
startedAt: number;
|
|
107
|
+
avgDurationMs: number;
|
|
108
|
+
p95DurationMs: number;
|
|
109
|
+
requestCount: number;
|
|
110
|
+
errorCount: number;
|
|
111
|
+
avgQueryCount: number;
|
|
112
|
+
avgQueryTimeMs: number;
|
|
113
|
+
avgFetchTimeMs: number;
|
|
114
|
+
}
|
|
115
|
+
interface EndpointMetrics {
|
|
116
|
+
endpoint: string;
|
|
117
|
+
sessions: SessionMetric[];
|
|
118
|
+
dataPoints?: LiveRequestPoint[];
|
|
119
|
+
}
|
|
120
|
+
interface MetricsData {
|
|
121
|
+
version: 1;
|
|
122
|
+
endpoints: EndpointMetrics[];
|
|
123
|
+
}
|
|
124
|
+
interface LiveRequestPoint {
|
|
125
|
+
timestamp: number;
|
|
126
|
+
durationMs: number;
|
|
127
|
+
statusCode: number;
|
|
128
|
+
queryCount: number;
|
|
129
|
+
queryTimeMs: number;
|
|
130
|
+
fetchTimeMs: number;
|
|
131
|
+
}
|
|
132
|
+
interface LiveEndpointSummary {
|
|
133
|
+
p95Ms: number;
|
|
134
|
+
errorRate: number;
|
|
135
|
+
avgQueryCount: number;
|
|
136
|
+
totalRequests: number;
|
|
137
|
+
avgQueryTimeMs: number;
|
|
138
|
+
avgFetchTimeMs: number;
|
|
139
|
+
avgAppTimeMs: number;
|
|
140
|
+
}
|
|
141
|
+
interface LiveEndpointData {
|
|
142
|
+
endpoint: string;
|
|
143
|
+
requests: LiveRequestPoint[];
|
|
144
|
+
summary: LiveEndpointSummary;
|
|
145
|
+
}
|
|
146
|
+
interface RequestMetrics {
|
|
147
|
+
queryCount: number;
|
|
148
|
+
queryTimeMs: number;
|
|
149
|
+
fetchTimeMs: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
104
152
|
type SecuritySeverity = "critical" | "warning" | "info";
|
|
105
153
|
interface SecurityFinding {
|
|
106
154
|
severity: SecuritySeverity;
|
|
@@ -140,7 +188,7 @@ declare class SecurityScanner {
|
|
|
140
188
|
declare function createDefaultScanner(): SecurityScanner;
|
|
141
189
|
|
|
142
190
|
type InsightSeverity = "critical" | "warning" | "info";
|
|
143
|
-
type InsightType = "n1" | "cross-endpoint" | "redundant-query" | "error" | "error-hotspot" | "duplicate" | "slow" | "query-heavy" | "select-star" | "high-rows" | "large-response" | "response-overfetch" | "security";
|
|
191
|
+
type InsightType = "n1" | "cross-endpoint" | "redundant-query" | "error" | "error-hotspot" | "duplicate" | "slow" | "query-heavy" | "select-star" | "high-rows" | "large-response" | "response-overfetch" | "security" | "regression";
|
|
144
192
|
interface Insight {
|
|
145
193
|
severity: InsightSeverity;
|
|
146
194
|
type: InsightType;
|
|
@@ -155,8 +203,44 @@ interface InsightContext {
|
|
|
155
203
|
queries: readonly TracedQuery[];
|
|
156
204
|
errors: readonly TracedError[];
|
|
157
205
|
flows: readonly RequestFlow[];
|
|
206
|
+
fetches: readonly TracedFetch[];
|
|
207
|
+
previousMetrics?: readonly EndpointMetrics[];
|
|
158
208
|
securityFindings?: readonly SecurityFinding[];
|
|
159
209
|
}
|
|
210
|
+
interface EndpointGroup {
|
|
211
|
+
total: number;
|
|
212
|
+
errors: number;
|
|
213
|
+
totalDuration: number;
|
|
214
|
+
queryCount: number;
|
|
215
|
+
totalSize: number;
|
|
216
|
+
totalQueryTimeMs: number;
|
|
217
|
+
totalFetchTimeMs: number;
|
|
218
|
+
queryShapeDurations: Map<string, {
|
|
219
|
+
totalMs: number;
|
|
220
|
+
count: number;
|
|
221
|
+
label: string;
|
|
222
|
+
}>;
|
|
223
|
+
}
|
|
224
|
+
interface PreparedInsightContext extends InsightContext {
|
|
225
|
+
nonStatic: readonly TracedRequest[];
|
|
226
|
+
queriesByReq: ReadonlyMap<string, TracedQuery[]>;
|
|
227
|
+
fetchesByReq: ReadonlyMap<string, TracedFetch[]>;
|
|
228
|
+
reqById: ReadonlyMap<string, TracedRequest>;
|
|
229
|
+
endpointGroups: ReadonlyMap<string, EndpointGroup>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
interface InsightRule {
|
|
233
|
+
id: InsightType;
|
|
234
|
+
check(ctx: PreparedInsightContext): Insight[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
declare class InsightRunner {
|
|
238
|
+
private rules;
|
|
239
|
+
register(rule: InsightRule): void;
|
|
240
|
+
run(ctx: InsightContext): Insight[];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
declare function createDefaultInsightRunner(): InsightRunner;
|
|
160
244
|
declare function computeInsights(ctx: InsightContext): Insight[];
|
|
161
245
|
|
|
162
246
|
declare function detectProject(rootDir: string): Promise<DetectedProject>;
|
|
@@ -170,8 +254,37 @@ declare class AdapterRegistry {
|
|
|
170
254
|
getActive(): readonly BrakitAdapter[];
|
|
171
255
|
}
|
|
172
256
|
|
|
257
|
+
interface MetricsPersistence {
|
|
258
|
+
load(): MetricsData;
|
|
259
|
+
save(data: MetricsData): void;
|
|
260
|
+
saveSync(data: MetricsData): void;
|
|
261
|
+
remove(): void;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
declare class MetricsStore {
|
|
265
|
+
private persistence;
|
|
266
|
+
private data;
|
|
267
|
+
private endpointIndex;
|
|
268
|
+
private sessionId;
|
|
269
|
+
private sessionStart;
|
|
270
|
+
private flushTimer;
|
|
271
|
+
private accumulators;
|
|
272
|
+
private pendingPoints;
|
|
273
|
+
constructor(persistence: MetricsPersistence);
|
|
274
|
+
start(): void;
|
|
275
|
+
stop(): void;
|
|
276
|
+
recordRequest(req: TracedRequest, metrics: RequestMetrics): void;
|
|
277
|
+
getAll(): readonly EndpointMetrics[];
|
|
278
|
+
getEndpoint(endpoint: string): EndpointMetrics | undefined;
|
|
279
|
+
getLiveEndpoints(): LiveEndpointData[];
|
|
280
|
+
reset(): void;
|
|
281
|
+
flush(sync?: boolean): void;
|
|
282
|
+
private getOrCreateEndpoint;
|
|
283
|
+
}
|
|
284
|
+
|
|
173
285
|
type AnalysisListener = (insights: Insight[], findings: SecurityFinding[]) => void;
|
|
174
286
|
declare class AnalysisEngine {
|
|
287
|
+
private metricsStore;
|
|
175
288
|
private debounceMs;
|
|
176
289
|
private scanner;
|
|
177
290
|
private cachedInsights;
|
|
@@ -182,7 +295,7 @@ declare class AnalysisEngine {
|
|
|
182
295
|
private boundQueryListener;
|
|
183
296
|
private boundErrorListener;
|
|
184
297
|
private boundLogListener;
|
|
185
|
-
constructor(debounceMs?: number);
|
|
298
|
+
constructor(metricsStore: MetricsStore, debounceMs?: number);
|
|
186
299
|
start(): void;
|
|
187
300
|
stop(): void;
|
|
188
301
|
onUpdate(fn: AnalysisListener): void;
|
|
@@ -195,4 +308,4 @@ declare class AnalysisEngine {
|
|
|
195
308
|
|
|
196
309
|
declare const VERSION: string;
|
|
197
310
|
|
|
198
|
-
export { AdapterRegistry, AnalysisEngine, type BrakitAdapter, type BrakitConfig, type DetectedProject, type FlatHeaders, type Framework, type HttpMethod, type Insight, type InsightContext, type NormalizedOp, type RequestCategory, type RequestListener, type SecurityContext, type SecurityFinding, type SecurityRule, SecurityScanner, type SecuritySeverity, type TracedRequest, VERSION, computeInsights, createDefaultScanner, detectProject };
|
|
311
|
+
export { AdapterRegistry, AnalysisEngine, type BrakitAdapter, type BrakitConfig, type DetectedProject, type FlatHeaders, type Framework, type HttpMethod, type Insight, type InsightContext, type InsightRule, InsightRunner, type NormalizedOp, type RequestCategory, type RequestListener, type SecurityContext, type SecurityFinding, type SecurityRule, SecurityScanner, type SecuritySeverity, type TracedRequest, VERSION, computeInsights, createDefaultInsightRunner, createDefaultScanner, detectProject };
|