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 CHANGED
@@ -1,17 +1,38 @@
1
- # Brakit
2
-
3
- **See what your app is actually doing.**
4
-
5
- Every request, query, and security issue — before you ship.
6
-
7
- Open source · Local only · Zero config · 2 dependencies
8
-
9
- [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
- [![Node >= 18](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org)
11
- [![TypeScript](https://img.shields.io/badge/built%20with-TypeScript-3178c6.svg)](https://typescriptlang.org)
1
+ <h1 align="center"><img src="docs/images/icon.png" height="24" alt="" />&nbsp;&nbsp;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> &bull;
11
+ <a href="https://brakit.ai">Website</a> &bull;
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
- - **8 security rules** scanned against live traffic leaked secrets, PII in responses, missing auth, N+1 queries flagged automatically
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
- - **Live dashboard** at `/__brakit` 9 tabs updating in real-time
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 | What it catches |
60
- | ------------ | ---------------- | ------------------------------------------------------------------------------- |
61
- | **Critical** | Exposed Secret | Response contains `password`, `api_key`, `client_secret` fields with real values |
62
- | **Critical** | Token in URL | Auth tokens in query parameters instead of headers |
63
- | **Critical** | Stack Trace Leak | Internal stack traces sent to the client |
64
- | **Critical** | Error Info Leak | DB connection strings, SQL queries, or secret values in error responses |
65
- | Warning | PII in Response | API echoes back emails, returns full user records with internal IDs |
66
- | Warning | Insecure Cookie | Missing `HttpOnly` or `SameSite` flags |
67
- | Warning | Sensitive Logs | Passwords, secrets, or token values in console output |
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
- | # | Layer | How it blocks |
97
- |---|---|---|
98
- | 1 | `shouldActivate()` | Checks `NODE_ENV` + 15 cloud/CI env vars |
99
- | 2 | `instrumentation.ts` guard | Its own `NODE_ENV !== 'production'` check |
100
- | 3 | devDependency | Pruned in production builds |
101
- | 4 | `try/catch` on import | Missing module = silent no-op |
102
- | 5 | Localhost-only dashboard | Non-local IPs get 404 on `/__brakit` |
103
- | 6 | `safeWrap` + circuit breaker | 10 errors = brakit self-disables |
104
- | 7 | `BRAKIT_DISABLE=true` | Manual kill switch |
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 | Status |
109
- | ----------- | -------------------------- |
110
- | Next.js | Full support (auto-detect) |
111
- | Remix | Auto-detect |
112
- | Nuxt | Auto-detect |
113
- | Vite | Auto-detect |
114
- | Astro | Auto-detect |
115
- | Express | Auto-detect |
116
- | Fastify | Auto-detect |
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, server hooks, capture, safety
161
- analysis/ Security scanning, N+1 detection, insights engine
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/ Runtime hooks and database adapters
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 new
182
- database adapter or a new security rule — each require exactly one file and one
183
- interface. See [CONTRIBUTING.md](CONTRIBUTING.md) for step-by-step guides.
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 };