brakit 0.8.1 → 0.8.2

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,9 +1,9 @@
1
1
  <h1 align="center"><img src="docs/images/icon.png" height="24" alt="" />&nbsp;&nbsp;Brakit</h1>
2
2
 
3
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 first · Zero config · 2 dependencies</b>
4
+ <b>AI writes your code. Brakit watches what it does.</b> <br />
5
+ Every request, query, and security issue, caught before you ship. <br />
6
+ <b>Open source · Local first · Zero config · AI-native via MCP</b>
7
7
  </p>
8
8
 
9
9
  <h3 align="center">
@@ -74,7 +74,7 @@ Dashboard at `http://localhost:<port>/__brakit`. Insights in the terminal.
74
74
  - **Full server tracing** — fetch calls, DB queries, console logs, errors — zero code changes
75
75
  - **Response overfetch** — large JSON responses with many fields your client doesn't use
76
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
77
+ - **AI-native via MCP** — Claude Code and Cursor can query findings, inspect endpoints, and verify fixes directly
78
78
 
79
79
  ---
80
80
 
@@ -188,7 +188,7 @@ npm run typecheck # Type-check without emitting
188
188
  npm test # Run tests with vitest
189
189
  ```
190
190
 
191
- Only 2 production dependencies: `citty` (CLI) and `picocolors` (terminal colors). Everything else is Node.js built-ins.
191
+ Minimal production dependencies. Everything else is Node.js built-ins.
192
192
 
193
193
  ### Architecture
194
194
 
package/dist/api.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { IncomingHttpHeaders } from 'node:http';
2
+
1
3
  type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | (string & {});
2
4
  type FlatHeaders = Record<string, string>;
3
5
  interface TracedRequest {
@@ -117,10 +119,6 @@ interface EndpointMetrics {
117
119
  sessions: SessionMetric[];
118
120
  dataPoints?: LiveRequestPoint[];
119
121
  }
120
- interface MetricsData {
121
- version: 1;
122
- endpoints: EndpointMetrics[];
123
- }
124
122
  interface LiveRequestPoint {
125
123
  timestamp: number;
126
124
  durationMs: number;
@@ -239,15 +237,22 @@ declare class FindingStore {
239
237
  private findings;
240
238
  private flushTimer;
241
239
  private dirty;
242
- private writing;
240
+ private readonly writer;
243
241
  private readonly findingsPath;
244
- private readonly tmpPath;
245
- private readonly metricsDir;
246
242
  constructor(rootDir: string);
247
243
  start(): void;
248
244
  stop(): void;
249
245
  upsert(finding: SecurityFinding, source: FindingSource): StatefulFinding;
250
246
  transition(findingId: string, state: FindingState): boolean;
247
+ /**
248
+ * Reconcile passive findings against the current analysis results.
249
+ *
250
+ * Passive findings are detected by continuous scanning (not user-triggered).
251
+ * When a previously-seen finding is absent from the current results, it means
252
+ * the issue has been fixed — transition it to "resolved" automatically.
253
+ * Active findings (from MCP verify-fix) are not auto-resolved because they
254
+ * require explicit verification.
255
+ */
251
256
  reconcilePassive(currentFindings: readonly SecurityFinding[]): void;
252
257
  getAll(): readonly StatefulFinding[];
253
258
  getByState(state: FindingState): readonly StatefulFinding[];
@@ -256,8 +261,7 @@ declare class FindingStore {
256
261
  private load;
257
262
  private flush;
258
263
  private flushSync;
259
- private writeAsync;
260
- private ensureDir;
264
+ private serialize;
261
265
  }
262
266
 
263
267
  interface BrakitAdapter {
@@ -312,44 +316,105 @@ declare class AdapterRegistry {
312
316
  getActive(): readonly BrakitAdapter[];
313
317
  }
314
318
 
315
- interface MetricsPersistence {
316
- load(): MetricsData;
317
- save(data: MetricsData): void;
318
- saveSync(data: MetricsData): void;
319
- remove(): void;
319
+ interface AnalysisUpdate {
320
+ insights: Insight[];
321
+ findings: SecurityFinding[];
322
+ statefulFindings: readonly StatefulFinding[];
323
+ statefulInsights: readonly StatefulInsight[];
324
+ }
325
+ interface ChannelMap {
326
+ "telemetry:fetch": Omit<TracedFetch, "id">;
327
+ "telemetry:query": Omit<TracedQuery, "id">;
328
+ "telemetry:log": Omit<TracedLog, "id">;
329
+ "telemetry:error": Omit<TracedError, "id">;
330
+ "request:completed": TracedRequest;
331
+ "analysis:updated": AnalysisUpdate;
332
+ "store:cleared": void;
333
+ }
334
+ type Listener<T> = (data: T) => void;
335
+ declare class EventBus {
336
+ private listeners;
337
+ emit<K extends keyof ChannelMap>(channel: K, data: ChannelMap[K]): void;
338
+ on<K extends keyof ChannelMap>(channel: K, fn: Listener<ChannelMap[K]>): () => void;
339
+ off<K extends keyof ChannelMap>(channel: K, fn: Listener<ChannelMap[K]>): void;
320
340
  }
321
341
 
322
- declare class MetricsStore {
323
- private persistence;
324
- private data;
325
- private endpointIndex;
326
- private sessionId;
327
- private sessionStart;
328
- private flushTimer;
329
- private accumulators;
330
- private pendingPoints;
331
- constructor(persistence: MetricsPersistence);
332
- start(): void;
333
- stop(): void;
342
+ interface CaptureInput {
343
+ requestId: string;
344
+ method: string;
345
+ url: string;
346
+ requestHeaders: IncomingHttpHeaders;
347
+ requestBody: Buffer | null;
348
+ statusCode: number;
349
+ responseHeaders: IncomingHttpHeaders;
350
+ responseBody: Buffer | null;
351
+ responseContentType: string;
352
+ startTime: number;
353
+ endTime?: number;
354
+ config: Pick<BrakitConfig, "maxBodyCapture">;
355
+ }
356
+
357
+ interface TelemetryStoreInterface<T extends TelemetryEntry> {
358
+ add(data: Omit<T, "id">): T;
359
+ getAll(): readonly T[];
360
+ getByRequest(requestId: string): T[];
361
+ clear(): void;
362
+ }
363
+ interface RequestStoreInterface {
364
+ capture(input: CaptureInput): TracedRequest;
365
+ getAll(): readonly TracedRequest[];
366
+ clear(): void;
367
+ }
368
+ interface MetricsStoreInterface {
334
369
  recordRequest(req: TracedRequest, metrics: RequestMetrics): void;
335
370
  getAll(): readonly EndpointMetrics[];
336
371
  getEndpoint(endpoint: string): EndpointMetrics | undefined;
337
372
  getLiveEndpoints(): LiveEndpointData[];
338
373
  reset(): void;
339
- flush(sync?: boolean): void;
340
- private getOrCreateEndpoint;
374
+ start(): void;
375
+ stop(): void;
376
+ }
377
+ interface FindingStoreInterface {
378
+ upsert(finding: SecurityFinding, source: FindingSource): StatefulFinding;
379
+ transition(findingId: string, state: FindingState): boolean;
380
+ reconcilePassive(findings: readonly SecurityFinding[]): void;
381
+ getAll(): readonly StatefulFinding[];
382
+ getByState(state: FindingState): readonly StatefulFinding[];
383
+ get(findingId: string): StatefulFinding | undefined;
384
+ clear(): void;
385
+ start(): void;
386
+ stop(): void;
387
+ }
388
+ interface AnalysisEngineInterface {
389
+ start(): void;
390
+ stop(): void;
391
+ recompute(): void;
392
+ getInsights(): readonly Insight[];
393
+ getFindings(): readonly SecurityFinding[];
394
+ getStatefulInsights(): readonly StatefulInsight[];
395
+ getStatefulFindings(): readonly StatefulFinding[];
341
396
  }
342
397
 
343
- interface AnalysisUpdate {
344
- insights: Insight[];
345
- findings: SecurityFinding[];
346
- statefulFindings: readonly StatefulFinding[];
347
- statefulInsights: readonly StatefulInsight[];
398
+ interface ServiceMap {
399
+ "event-bus": EventBus;
400
+ "request-store": RequestStoreInterface;
401
+ "query-store": TelemetryStoreInterface<TracedQuery>;
402
+ "fetch-store": TelemetryStoreInterface<TracedFetch>;
403
+ "log-store": TelemetryStoreInterface<TracedLog>;
404
+ "error-store": TelemetryStoreInterface<TracedError>;
405
+ "metrics-store": MetricsStoreInterface;
406
+ "finding-store": FindingStoreInterface;
407
+ "analysis-engine": AnalysisEngineInterface;
408
+ }
409
+ declare class ServiceRegistry {
410
+ private services;
411
+ register<K extends keyof ServiceMap>(name: K, service: ServiceMap[K]): void;
412
+ get<K extends keyof ServiceMap>(name: K): ServiceMap[K];
413
+ has<K extends keyof ServiceMap>(name: K): boolean;
348
414
  }
349
- type AnalysisListener = (update: AnalysisUpdate) => void;
415
+
350
416
  declare class AnalysisEngine {
351
- private metricsStore;
352
- private findingStore?;
417
+ private registry;
353
418
  private debounceMs;
354
419
  private scanner;
355
420
  private insightTracker;
@@ -357,16 +422,10 @@ declare class AnalysisEngine {
357
422
  private cachedFindings;
358
423
  private cachedStatefulInsights;
359
424
  private debounceTimer;
360
- private listeners;
361
- private boundRequestListener;
362
- private boundQueryListener;
363
- private boundErrorListener;
364
- private boundLogListener;
365
- constructor(metricsStore: MetricsStore, findingStore?: FindingStore | undefined, debounceMs?: number);
425
+ private subs;
426
+ constructor(registry: ServiceRegistry, debounceMs?: number);
366
427
  start(): void;
367
428
  stop(): void;
368
- onUpdate(fn: AnalysisListener): void;
369
- offUpdate(fn: AnalysisListener): void;
370
429
  getInsights(): readonly Insight[];
371
430
  getFindings(): readonly SecurityFinding[];
372
431
  getStatefulFindings(): readonly StatefulFinding[];