brakit 0.7.6 → 0.8.1
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 +22 -4
- package/dist/api.d.ts +99 -30
- package/dist/api.js +348 -49
- package/dist/bin/brakit.js +1128 -29
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +743 -0
- package/dist/runtime/index.js +680 -173
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<b>See what your app is actually doing.</b> <br />
|
|
5
5
|
Every request, query, and security issue — before you ship. <br />
|
|
6
|
-
<b>Open source · Local
|
|
6
|
+
<b>Open source · Local first · Zero config · 2 dependencies</b>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<h3 align="center">
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
<a href="https://typescriptlang.org">
|
|
23
23
|
<img src="https://img.shields.io/badge/built%20with-TypeScript-3178c6.svg" alt="TypeScript" />
|
|
24
24
|
</a>
|
|
25
|
+
<a href="https://www.npmjs.com/package/brakit">
|
|
26
|
+
<img src="https://img.shields.io/npm/v/brakit" alt="npm version" />
|
|
27
|
+
</a>
|
|
25
28
|
<a href="CONTRIBUTING.md">
|
|
26
29
|
<img src="https://img.shields.io/badge/PRs-Welcome-brightgreen" alt="PRs welcome!" />
|
|
27
30
|
</a>
|
|
@@ -30,7 +33,15 @@
|
|
|
30
33
|
---
|
|
31
34
|
|
|
32
35
|
<p align="center">
|
|
33
|
-
<img width="700" src="docs/images/dashboard.png" alt="Brakit Dashboard" />
|
|
36
|
+
<img width="700" src="docs/images/dashboard.png" alt="Brakit Dashboard — issues surfaced automatically" />
|
|
37
|
+
<br />
|
|
38
|
+
<sub>Brakit catches N+1 queries, PII leaks, and slow endpoints as you develop</sub>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img width="700" src="docs/images/vscode.png" alt="Claude reading Brakit findings in VS Code" />
|
|
43
|
+
<br />
|
|
44
|
+
<sub>Claude reads findings via MCP and fixes your code</sub>
|
|
34
45
|
</p>
|
|
35
46
|
|
|
36
47
|
## Quick Start
|
|
@@ -63,6 +74,7 @@ Dashboard at `http://localhost:<port>/__brakit`. Insights in the terminal.
|
|
|
63
74
|
- **Full server tracing** — fetch calls, DB queries, console logs, errors — zero code changes
|
|
64
75
|
- **Response overfetch** — large JSON responses with many fields your client doesn't use
|
|
65
76
|
- **Performance tracking** — health grades and p95 trends across dev sessions
|
|
77
|
+
- **AI-native via MCP** — Claude, Cursor, and other AI tools can query findings, inspect endpoints, and verify fixes directly
|
|
66
78
|
|
|
67
79
|
---
|
|
68
80
|
|
|
@@ -93,7 +105,9 @@ Brakit watches every action your app takes — not raw HTTP noise, but what actu
|
|
|
93
105
|
|
|
94
106
|
## Who Is This For
|
|
95
107
|
|
|
96
|
-
|
|
108
|
+
- **AI-assisted developers** — Using Cursor, Copilot, or Claude Code to generate API code you don't fully review? Brakit catches the issues they introduce.
|
|
109
|
+
- **Console.log debuggers** — Wish you could just see every action your API executes without adding log statements everywhere? That's literally what brakit does.
|
|
110
|
+
- **Anyone shipping Node.js APIs** — If you want to catch security and performance issues before they reach production, brakit surfaces them as you develop.
|
|
97
111
|
|
|
98
112
|
---
|
|
99
113
|
|
|
@@ -105,6 +119,7 @@ import 'brakit' → hooks into http.Server → captures everything
|
|
|
105
119
|
+-- Dashboard UI (/__brakit)
|
|
106
120
|
+-- Live SSE stream (real-time updates)
|
|
107
121
|
+-- Terminal output (insights as you develop)
|
|
122
|
+
+-- MCP server (AI assistant integration)
|
|
108
123
|
```
|
|
109
124
|
|
|
110
125
|
`import 'brakit'` runs inside your process. It patches `http.Server.prototype.emit` to intercept all requests — capturing request/response pairs, grouping them into actions, and streaming everything to the dashboard at `/__brakit` on your existing port. No proxy, no second process, no different port.
|
|
@@ -156,7 +171,7 @@ Brakit never runs in production. 7 independent layers ensure it:
|
|
|
156
171
|
npx brakit uninstall
|
|
157
172
|
```
|
|
158
173
|
|
|
159
|
-
Removes the instrumentation file and devDependency. Your app is unchanged.
|
|
174
|
+
Removes the instrumentation file, MCP configuration, `.brakit` data directory, `.gitignore` entry, and devDependency. Your app is unchanged.
|
|
160
175
|
|
|
161
176
|
---
|
|
162
177
|
|
|
@@ -194,6 +209,8 @@ src/
|
|
|
194
209
|
instrument/ Database adapters and instrumentation hooks
|
|
195
210
|
adapters/ BrakitAdapter implementations (one file per library)
|
|
196
211
|
hooks/ Core hooks (fetch, console, errors, context)
|
|
212
|
+
mcp/ MCP server for AI tool integration (Claude, Cursor)
|
|
213
|
+
tools/ Tool implementations (one file per tool)
|
|
197
214
|
output/ Terminal insight listener
|
|
198
215
|
store/ In-memory telemetry stores + persistent metrics
|
|
199
216
|
types/ TypeScript definitions by domain
|
|
@@ -214,6 +231,7 @@ Some areas where help would be great:
|
|
|
214
231
|
- **Database adapters** — Drizzle, Mongoose, SQLite, MongoDB
|
|
215
232
|
- **Insight rules** — New performance patterns, custom thresholds
|
|
216
233
|
- **Security rules** — More patterns, configurable severity
|
|
234
|
+
- **MCP tools** — New AI-facing tools for the MCP server
|
|
217
235
|
- **Dashboard** — Request diff, timeline view, HAR export
|
|
218
236
|
|
|
219
237
|
Please open an issue first for larger changes so we can discuss the approach.
|
package/dist/api.d.ts
CHANGED
|
@@ -149,7 +149,9 @@ interface RequestMetrics {
|
|
|
149
149
|
fetchTimeMs: number;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
/** Shared severity levels used by both security findings and insights. */
|
|
153
|
+
type Severity = "critical" | "warning" | "info";
|
|
154
|
+
type SecuritySeverity = Severity;
|
|
153
155
|
interface SecurityFinding {
|
|
154
156
|
severity: SecuritySeverity;
|
|
155
157
|
rule: string;
|
|
@@ -160,34 +162,25 @@ interface SecurityFinding {
|
|
|
160
162
|
count: number;
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
check(ctx: SecurityContext): SecurityFinding[];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
declare class SecurityScanner {
|
|
183
|
-
private rules;
|
|
184
|
-
register(rule: SecurityRule): void;
|
|
185
|
-
scan(ctx: SecurityContext): SecurityFinding[];
|
|
186
|
-
getRules(): readonly SecurityRule[];
|
|
165
|
+
type FindingState = "open" | "fixing" | "resolved";
|
|
166
|
+
type FindingSource = "passive";
|
|
167
|
+
interface StatefulFinding {
|
|
168
|
+
/** Stable ID derived from rule + endpoint + description hash */
|
|
169
|
+
findingId: string;
|
|
170
|
+
state: FindingState;
|
|
171
|
+
source: FindingSource;
|
|
172
|
+
finding: SecurityFinding;
|
|
173
|
+
firstSeenAt: number;
|
|
174
|
+
lastSeenAt: number;
|
|
175
|
+
resolvedAt: number | null;
|
|
176
|
+
occurrences: number;
|
|
177
|
+
}
|
|
178
|
+
interface FindingsData {
|
|
179
|
+
version: 1;
|
|
180
|
+
findings: StatefulFinding[];
|
|
187
181
|
}
|
|
188
|
-
declare function createDefaultScanner(): SecurityScanner;
|
|
189
182
|
|
|
190
|
-
type InsightSeverity =
|
|
183
|
+
type InsightSeverity = Severity;
|
|
191
184
|
type InsightType = "n1" | "cross-endpoint" | "redundant-query" | "error" | "error-hotspot" | "duplicate" | "slow" | "query-heavy" | "select-star" | "high-rows" | "large-response" | "response-overfetch" | "security" | "regression";
|
|
192
185
|
interface Insight {
|
|
193
186
|
severity: InsightSeverity;
|
|
@@ -229,6 +222,71 @@ interface PreparedInsightContext extends InsightContext {
|
|
|
229
222
|
endpointGroups: ReadonlyMap<string, EndpointGroup>;
|
|
230
223
|
}
|
|
231
224
|
|
|
225
|
+
type InsightState = "open" | "resolved";
|
|
226
|
+
interface StatefulInsight {
|
|
227
|
+
key: string;
|
|
228
|
+
state: InsightState;
|
|
229
|
+
insight: Insight;
|
|
230
|
+
firstSeenAt: number;
|
|
231
|
+
lastSeenAt: number;
|
|
232
|
+
resolvedAt: number | null;
|
|
233
|
+
/** Consecutive recompute cycles where the insight was not detected. */
|
|
234
|
+
consecutiveAbsences: number;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
declare class FindingStore {
|
|
238
|
+
private rootDir;
|
|
239
|
+
private findings;
|
|
240
|
+
private flushTimer;
|
|
241
|
+
private dirty;
|
|
242
|
+
private writing;
|
|
243
|
+
private readonly findingsPath;
|
|
244
|
+
private readonly tmpPath;
|
|
245
|
+
private readonly metricsDir;
|
|
246
|
+
constructor(rootDir: string);
|
|
247
|
+
start(): void;
|
|
248
|
+
stop(): void;
|
|
249
|
+
upsert(finding: SecurityFinding, source: FindingSource): StatefulFinding;
|
|
250
|
+
transition(findingId: string, state: FindingState): boolean;
|
|
251
|
+
reconcilePassive(currentFindings: readonly SecurityFinding[]): void;
|
|
252
|
+
getAll(): readonly StatefulFinding[];
|
|
253
|
+
getByState(state: FindingState): readonly StatefulFinding[];
|
|
254
|
+
get(findingId: string): StatefulFinding | undefined;
|
|
255
|
+
clear(): void;
|
|
256
|
+
private load;
|
|
257
|
+
private flush;
|
|
258
|
+
private flushSync;
|
|
259
|
+
private writeAsync;
|
|
260
|
+
private ensureDir;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
interface BrakitAdapter {
|
|
264
|
+
name: string;
|
|
265
|
+
detect(): boolean;
|
|
266
|
+
patch(emit: (event: TelemetryEvent) => void): void;
|
|
267
|
+
unpatch?(): void;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface SecurityContext {
|
|
271
|
+
requests: readonly TracedRequest[];
|
|
272
|
+
logs: readonly TracedLog[];
|
|
273
|
+
}
|
|
274
|
+
interface SecurityRule {
|
|
275
|
+
id: string;
|
|
276
|
+
severity: SecuritySeverity;
|
|
277
|
+
name: string;
|
|
278
|
+
hint: string;
|
|
279
|
+
check(ctx: SecurityContext): SecurityFinding[];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
declare class SecurityScanner {
|
|
283
|
+
private rules;
|
|
284
|
+
register(rule: SecurityRule): void;
|
|
285
|
+
scan(ctx: SecurityContext): SecurityFinding[];
|
|
286
|
+
getRules(): readonly SecurityRule[];
|
|
287
|
+
}
|
|
288
|
+
declare function createDefaultScanner(): SecurityScanner;
|
|
289
|
+
|
|
232
290
|
interface InsightRule {
|
|
233
291
|
id: InsightType;
|
|
234
292
|
check(ctx: PreparedInsightContext): Insight[];
|
|
@@ -282,30 +340,41 @@ declare class MetricsStore {
|
|
|
282
340
|
private getOrCreateEndpoint;
|
|
283
341
|
}
|
|
284
342
|
|
|
285
|
-
|
|
343
|
+
interface AnalysisUpdate {
|
|
344
|
+
insights: Insight[];
|
|
345
|
+
findings: SecurityFinding[];
|
|
346
|
+
statefulFindings: readonly StatefulFinding[];
|
|
347
|
+
statefulInsights: readonly StatefulInsight[];
|
|
348
|
+
}
|
|
349
|
+
type AnalysisListener = (update: AnalysisUpdate) => void;
|
|
286
350
|
declare class AnalysisEngine {
|
|
287
351
|
private metricsStore;
|
|
352
|
+
private findingStore?;
|
|
288
353
|
private debounceMs;
|
|
289
354
|
private scanner;
|
|
355
|
+
private insightTracker;
|
|
290
356
|
private cachedInsights;
|
|
291
357
|
private cachedFindings;
|
|
358
|
+
private cachedStatefulInsights;
|
|
292
359
|
private debounceTimer;
|
|
293
360
|
private listeners;
|
|
294
361
|
private boundRequestListener;
|
|
295
362
|
private boundQueryListener;
|
|
296
363
|
private boundErrorListener;
|
|
297
364
|
private boundLogListener;
|
|
298
|
-
constructor(metricsStore: MetricsStore, debounceMs?: number);
|
|
365
|
+
constructor(metricsStore: MetricsStore, findingStore?: FindingStore | undefined, debounceMs?: number);
|
|
299
366
|
start(): void;
|
|
300
367
|
stop(): void;
|
|
301
368
|
onUpdate(fn: AnalysisListener): void;
|
|
302
369
|
offUpdate(fn: AnalysisListener): void;
|
|
303
370
|
getInsights(): readonly Insight[];
|
|
304
371
|
getFindings(): readonly SecurityFinding[];
|
|
372
|
+
getStatefulFindings(): readonly StatefulFinding[];
|
|
373
|
+
getStatefulInsights(): readonly StatefulInsight[];
|
|
305
374
|
private scheduleRecompute;
|
|
306
375
|
recompute(): void;
|
|
307
376
|
}
|
|
308
377
|
|
|
309
378
|
declare const VERSION: string;
|
|
310
379
|
|
|
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 };
|
|
380
|
+
export { AdapterRegistry, AnalysisEngine, type BrakitAdapter, type BrakitConfig, type DetectedProject, type FindingSource, type FindingState, FindingStore, type FindingsData, 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 StatefulFinding, type TracedRequest, VERSION, computeInsights, createDefaultInsightRunner, createDefaultScanner, detectProject };
|