@expressots/studio-agent 4.0.0-preview.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.
Files changed (86) hide show
  1. package/README.md +143 -0
  2. package/dist/agent.d.ts +127 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +1031 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/discovery/index.d.ts +2 -0
  7. package/dist/discovery/index.d.ts.map +1 -0
  8. package/dist/discovery/index.js +2 -0
  9. package/dist/discovery/index.js.map +1 -0
  10. package/dist/discovery/route-scanner.d.ts +35 -0
  11. package/dist/discovery/route-scanner.d.ts.map +1 -0
  12. package/dist/discovery/route-scanner.js +385 -0
  13. package/dist/discovery/route-scanner.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +15 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/instrumentation/index.d.ts +2 -0
  19. package/dist/instrumentation/index.d.ts.map +1 -0
  20. package/dist/instrumentation/index.js +2 -0
  21. package/dist/instrumentation/index.js.map +1 -0
  22. package/dist/instrumentation/tracer.d.ts +40 -0
  23. package/dist/instrumentation/tracer.d.ts.map +1 -0
  24. package/dist/instrumentation/tracer.js +190 -0
  25. package/dist/instrumentation/tracer.js.map +1 -0
  26. package/dist/introspection/container-introspector.d.ts +81 -0
  27. package/dist/introspection/container-introspector.d.ts.map +1 -0
  28. package/dist/introspection/container-introspector.js +251 -0
  29. package/dist/introspection/container-introspector.js.map +1 -0
  30. package/dist/logging/log-capture.d.ts +58 -0
  31. package/dist/logging/log-capture.d.ts.map +1 -0
  32. package/dist/logging/log-capture.js +184 -0
  33. package/dist/logging/log-capture.js.map +1 -0
  34. package/dist/recording/index.d.ts +2 -0
  35. package/dist/recording/index.d.ts.map +1 -0
  36. package/dist/recording/index.js +2 -0
  37. package/dist/recording/index.js.map +1 -0
  38. package/dist/recording/request-recorder.d.ts +43 -0
  39. package/dist/recording/request-recorder.d.ts.map +1 -0
  40. package/dist/recording/request-recorder.js +373 -0
  41. package/dist/recording/request-recorder.js.map +1 -0
  42. package/dist/security/fix-resolver.d.ts +40 -0
  43. package/dist/security/fix-resolver.d.ts.map +1 -0
  44. package/dist/security/fix-resolver.js +283 -0
  45. package/dist/security/fix-resolver.js.map +1 -0
  46. package/dist/security/fix-runner.d.ts +60 -0
  47. package/dist/security/fix-runner.d.ts.map +1 -0
  48. package/dist/security/fix-runner.js +188 -0
  49. package/dist/security/fix-runner.js.map +1 -0
  50. package/dist/security/index.d.ts +140 -0
  51. package/dist/security/index.d.ts.map +1 -0
  52. package/dist/security/index.js +460 -0
  53. package/dist/security/index.js.map +1 -0
  54. package/dist/security/lockfile-graph.d.ts +69 -0
  55. package/dist/security/lockfile-graph.d.ts.map +1 -0
  56. package/dist/security/lockfile-graph.js +245 -0
  57. package/dist/security/lockfile-graph.js.map +1 -0
  58. package/dist/security/npm-audit.d.ts +67 -0
  59. package/dist/security/npm-audit.d.ts.map +1 -0
  60. package/dist/security/npm-audit.js +320 -0
  61. package/dist/security/npm-audit.js.map +1 -0
  62. package/dist/security/osv-cache.d.ts +51 -0
  63. package/dist/security/osv-cache.d.ts.map +1 -0
  64. package/dist/security/osv-cache.js +99 -0
  65. package/dist/security/osv-cache.js.map +1 -0
  66. package/dist/security/osv-client.d.ts +47 -0
  67. package/dist/security/osv-client.d.ts.map +1 -0
  68. package/dist/security/osv-client.js +247 -0
  69. package/dist/security/osv-client.js.map +1 -0
  70. package/dist/security/posture-analyzer.d.ts +44 -0
  71. package/dist/security/posture-analyzer.d.ts.map +1 -0
  72. package/dist/security/posture-analyzer.js +397 -0
  73. package/dist/security/posture-analyzer.js.map +1 -0
  74. package/dist/security/reachability.d.ts +59 -0
  75. package/dist/security/reachability.d.ts.map +1 -0
  76. package/dist/security/reachability.js +302 -0
  77. package/dist/security/reachability.js.map +1 -0
  78. package/dist/security/score.d.ts +36 -0
  79. package/dist/security/score.d.ts.map +1 -0
  80. package/dist/security/score.js +94 -0
  81. package/dist/security/score.js.map +1 -0
  82. package/dist/types/index.d.ts +587 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +14 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/package.json +75 -0
@@ -0,0 +1,184 @@
1
+ /**
2
+ * LogCapture — intercepts `console.*` calls so Studio can stream the app's
3
+ * stdout/stderr alongside requests, correlated by traceId when available.
4
+ *
5
+ * Design notes:
6
+ * - We patch the global `console` once at agent startup and forward to the
7
+ * originals untouched, so the user still sees their logs in their own
8
+ * terminal exactly as before.
9
+ * - A per-process AsyncLocalStorage carries the active traceId, populated
10
+ * by the Studio middleware. Logs emitted inside that scope are tagged.
11
+ * - A re-entry guard prevents infinite recursion if a listener — or its
12
+ * transitive dependencies — happens to log.
13
+ * - The buffer is bounded (default 1000 lines) and drops oldest first.
14
+ */
15
+ import { AsyncLocalStorage } from 'node:async_hooks';
16
+ import { inspect } from 'node:util';
17
+ /** Carries the active trace context for the current async chain. */
18
+ const requestContext = new AsyncLocalStorage();
19
+ /** Re-entry guard so `cb(entry) → console.log → handleLog → cb(entry)` can't loop. */
20
+ const REENTRY = Symbol.for('expressots.studio.log.reentry');
21
+ const LEVELS = ['log', 'info', 'warn', 'error', 'debug'];
22
+ export class LogCapture {
23
+ buffer = [];
24
+ maxBuffer;
25
+ /**
26
+ * The "currently-installed" implementation for each level. Reading
27
+ * `console[level]` always returns a stable wrapper that delegates to
28
+ * the value stored here, so callers that assign to `console[level]`
29
+ * (e.g. ExpressoTS's startup buffering, then its restore) end up
30
+ * updating the cell instead of replacing our wrapper.
31
+ */
32
+ current = new Map();
33
+ listeners = new Set();
34
+ installed = false;
35
+ constructor(maxBuffer = 1000) {
36
+ this.maxBuffer = maxBuffer;
37
+ }
38
+ /**
39
+ * Install a permanent hook on each console method. Uses an accessor
40
+ * property so subsequent `console.log = …` assignments (which the
41
+ * framework performs during its startup buffering lifecycle) are
42
+ * absorbed by our setter rather than wiping out the wrapper.
43
+ */
44
+ install() {
45
+ if (this.installed)
46
+ return;
47
+ this.installed = true;
48
+ for (const level of LEVELS) {
49
+ const cons = console;
50
+ // Seed with whatever is on `console` right now so the very first
51
+ // call after install still forwards to the existing impl.
52
+ const initial = cons[level];
53
+ if (typeof initial === 'function') {
54
+ this.current.set(level, initial.bind(console));
55
+ }
56
+ const wrapper = (...args) => {
57
+ const impl = this.current.get(level);
58
+ if (impl) {
59
+ try {
60
+ impl.apply(console, args);
61
+ }
62
+ catch {
63
+ // never let logging crash the app
64
+ }
65
+ }
66
+ // Re-entry guard against feedback loops if a listener (or its
67
+ // dependencies) happens to log.
68
+ const sentinel = console;
69
+ if (sentinel[REENTRY])
70
+ return;
71
+ sentinel[REENTRY] = true;
72
+ try {
73
+ this.handleLog(level, args);
74
+ }
75
+ catch {
76
+ // swallow
77
+ }
78
+ finally {
79
+ sentinel[REENTRY] = false;
80
+ }
81
+ };
82
+ Object.defineProperty(console, level, {
83
+ configurable: true,
84
+ enumerable: true,
85
+ get: () => wrapper,
86
+ // Anything that assigns to `console[level]` after we install
87
+ // (e.g. framework `stopBuffering` restoring originals) becomes
88
+ // the new delegate target — but the wrapper itself stays put.
89
+ set: (next) => {
90
+ if (typeof next === 'function') {
91
+ this.current.set(level, next.bind(console));
92
+ }
93
+ },
94
+ });
95
+ }
96
+ }
97
+ /** Restore the original console methods (used in tests / hot-reload). */
98
+ uninstall() {
99
+ if (!this.installed)
100
+ return;
101
+ for (const level of LEVELS) {
102
+ const impl = this.current.get(level);
103
+ // Replace our accessor with a plain data property of the latest impl
104
+ // so reassignment semantics match Node's default console going forward.
105
+ Object.defineProperty(console, level, {
106
+ configurable: true,
107
+ writable: true,
108
+ enumerable: true,
109
+ value: impl ?? (() => undefined),
110
+ });
111
+ }
112
+ this.current.clear();
113
+ this.installed = false;
114
+ }
115
+ /**
116
+ * Run `fn` inside a request scope so any `console.*` calls during the
117
+ * request body are tagged with the given traceId.
118
+ */
119
+ runWith(traceId, fn) {
120
+ return requestContext.run({ traceId }, fn);
121
+ }
122
+ /** Subscribe to live log events. Returns an unsubscribe function. */
123
+ onLog(listener) {
124
+ this.listeners.add(listener);
125
+ return () => this.listeners.delete(listener);
126
+ }
127
+ /** Snapshot of the in-memory ring buffer (oldest first). */
128
+ getBuffer() {
129
+ return this.buffer.slice();
130
+ }
131
+ /** Clear the in-memory buffer. */
132
+ clear() {
133
+ this.buffer = [];
134
+ }
135
+ handleLog(level, args) {
136
+ const ctx = requestContext.getStore();
137
+ const entry = {
138
+ level,
139
+ message: formatArgs(args),
140
+ timestamp: Date.now(),
141
+ traceId: ctx?.traceId,
142
+ };
143
+ this.buffer.push(entry);
144
+ if (this.buffer.length > this.maxBuffer) {
145
+ // Drop oldest. Splice once when over the cap (avoids a per-call shift).
146
+ this.buffer.splice(0, this.buffer.length - this.maxBuffer);
147
+ }
148
+ for (const listener of this.listeners) {
149
+ try {
150
+ listener(entry);
151
+ }
152
+ catch {
153
+ // never let a listener bubble
154
+ }
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * Format console arguments to a single readable string, using node's util.inspect
160
+ * so objects/errors render the way developers expect (with stack traces, etc.).
161
+ */
162
+ function formatArgs(args) {
163
+ if (args.length === 0)
164
+ return '';
165
+ const parts = [];
166
+ for (const arg of args) {
167
+ if (typeof arg === 'string') {
168
+ parts.push(arg);
169
+ }
170
+ else if (arg instanceof Error) {
171
+ parts.push(arg.stack || `${arg.name}: ${arg.message}`);
172
+ }
173
+ else {
174
+ parts.push(inspect(arg, {
175
+ depth: 4,
176
+ colors: false,
177
+ breakLength: 120,
178
+ maxStringLength: 1000,
179
+ }));
180
+ }
181
+ }
182
+ return parts.join(' ');
183
+ }
184
+ //# sourceMappingURL=log-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-capture.js","sourceRoot":"","sources":["../../src/logging/log-capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { RequestRecorder } from './request-recorder.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recording/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { RequestRecorder } from './request-recorder.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/recording/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Request/Response recorder for ExpressoTS Studio
3
+ * Stores request/response pairs for replay functionality
4
+ */
5
+ import type { RecordedRequest, RecordedResponse, RecordedExchange, TraceInfo, HttpMethod } from '../types/index.js';
6
+ export declare class RequestRecorder {
7
+ private db;
8
+ private dbPath;
9
+ private maxExchanges;
10
+ private initialized;
11
+ constructor(dbPath?: string, maxExchanges?: number);
12
+ /** Initialize the database */
13
+ initialize(): Promise<void>;
14
+ /** Record a request */
15
+ recordRequest(method: HttpMethod, path: string, url: string, headers: Record<string, string>, query: Record<string, string>, body?: unknown, cookies?: Record<string, string>, traceId?: string): RecordedRequest;
16
+ /** Record a response */
17
+ recordResponse(requestId: string, statusCode: number, statusMessage: string, headers: Record<string, string>, body?: unknown, duration?: number, traceId?: string): RecordedResponse;
18
+ /** Record trace data */
19
+ recordTrace(traceId: string, trace: TraceInfo, requestId?: string): void;
20
+ /** Get a recorded exchange by ID */
21
+ getExchange(requestId: string): RecordedExchange | null;
22
+ /** Get recent exchanges */
23
+ getRecentExchanges(limit?: number, offset?: number): RecordedExchange[];
24
+ /** Search exchanges by path or method */
25
+ searchExchanges(query: string, method?: HttpMethod, limit?: number): RecordedExchange[];
26
+ /** Get statistics */
27
+ getStats(): {
28
+ totalRequests: number;
29
+ totalErrors: number;
30
+ avgDuration: number;
31
+ requestsByPath: Record<string, number>;
32
+ requestsByMethod: Record<string, number>;
33
+ };
34
+ /** Delete an exchange */
35
+ deleteExchange(requestId: string): void;
36
+ /** Clear all recorded data */
37
+ clearAll(): void;
38
+ /** Cleanup old entries if exceeding max */
39
+ private cleanup;
40
+ /** Close the database connection */
41
+ close(): void;
42
+ }
43
+ //# sourceMappingURL=request-recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-recorder.d.ts","sourceRoot":"","sources":["../../src/recording/request-recorder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACX,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAkC;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,GAAE,MAA4B,EAAE,YAAY,GAAE,MAAa;IAK7E,8BAA8B;IACxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAyDjC,uBAAuB;IACvB,aAAa,CACX,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,OAAO,CAAC,EAAE,MAAM,GACf,eAAe;IA2ClB,wBAAwB;IACxB,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,CAAC,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,gBAAgB;IAsCnB,wBAAwB;IACxB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAWxE,oCAAoC;IACpC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAyDvD,2BAA2B;IAC3B,kBAAkB,CAChB,KAAK,GAAE,MAAY,EACnB,MAAM,GAAE,MAAU,GACjB,gBAAgB,EAAE;IA0DrB,yCAAyC;IACzC,eAAe,CACb,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,UAAU,EACnB,KAAK,GAAE,MAAY,GAClB,gBAAgB,EAAE;IAoErB,qBAAqB;IACrB,QAAQ,IAAI;QACV,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACvC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC1C;IA6CD,yBAAyB;IACzB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAOvC,8BAA8B;IAC9B,QAAQ,IAAI,IAAI;IAQhB,2CAA2C;IAC3C,OAAO,CAAC,OAAO;IAiBf,oCAAoC;IACpC,KAAK,IAAI,IAAI;CAOd"}
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Request/Response recorder for ExpressoTS Studio
3
+ * Stores request/response pairs for replay functionality
4
+ */
5
+ import Database from 'better-sqlite3';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { randomUUID } from 'crypto';
9
+ export class RequestRecorder {
10
+ db = null;
11
+ dbPath;
12
+ maxExchanges;
13
+ initialized = false;
14
+ constructor(dbPath = '.studio/studio.db', maxExchanges = 1000) {
15
+ this.dbPath = dbPath;
16
+ this.maxExchanges = maxExchanges;
17
+ }
18
+ /** Initialize the database */
19
+ async initialize() {
20
+ if (this.initialized)
21
+ return;
22
+ // Ensure directory exists
23
+ const dir = path.dirname(this.dbPath);
24
+ if (!fs.existsSync(dir)) {
25
+ fs.mkdirSync(dir, { recursive: true });
26
+ }
27
+ this.db = new Database(this.dbPath);
28
+ this.db.pragma('journal_mode = WAL');
29
+ // Create tables
30
+ this.db.exec(`
31
+ CREATE TABLE IF NOT EXISTS requests (
32
+ id TEXT PRIMARY KEY,
33
+ trace_id TEXT,
34
+ timestamp INTEGER NOT NULL,
35
+ method TEXT NOT NULL,
36
+ path TEXT NOT NULL,
37
+ url TEXT NOT NULL,
38
+ headers TEXT NOT NULL,
39
+ query TEXT NOT NULL,
40
+ body TEXT,
41
+ cookies TEXT
42
+ );
43
+
44
+ CREATE TABLE IF NOT EXISTS responses (
45
+ id TEXT PRIMARY KEY,
46
+ request_id TEXT NOT NULL,
47
+ trace_id TEXT,
48
+ timestamp INTEGER NOT NULL,
49
+ status_code INTEGER NOT NULL,
50
+ status_message TEXT NOT NULL,
51
+ headers TEXT NOT NULL,
52
+ body TEXT,
53
+ duration INTEGER NOT NULL,
54
+ FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE
55
+ );
56
+
57
+ CREATE TABLE IF NOT EXISTS traces (
58
+ trace_id TEXT PRIMARY KEY,
59
+ request_id TEXT,
60
+ data TEXT NOT NULL,
61
+ timestamp INTEGER NOT NULL,
62
+ FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE
63
+ );
64
+
65
+ CREATE INDEX IF NOT EXISTS idx_requests_timestamp ON requests(timestamp);
66
+ CREATE INDEX IF NOT EXISTS idx_requests_trace_id ON requests(trace_id);
67
+ CREATE INDEX IF NOT EXISTS idx_requests_path ON requests(path);
68
+ CREATE INDEX IF NOT EXISTS idx_responses_request_id ON responses(request_id);
69
+ `);
70
+ this.initialized = true;
71
+ }
72
+ /** Record a request */
73
+ recordRequest(method, path, url, headers, query, body, cookies, traceId) {
74
+ if (!this.db)
75
+ throw new Error('RequestRecorder not initialized');
76
+ const id = randomUUID();
77
+ const timestamp = Date.now();
78
+ const request = {
79
+ id,
80
+ traceId: traceId || '',
81
+ timestamp,
82
+ method,
83
+ path,
84
+ url,
85
+ headers,
86
+ query,
87
+ body,
88
+ cookies,
89
+ };
90
+ const stmt = this.db.prepare(`
91
+ INSERT INTO requests (id, trace_id, timestamp, method, path, url, headers, query, body, cookies)
92
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
93
+ `);
94
+ stmt.run(id, traceId || null, timestamp, method, path, url, JSON.stringify(headers), JSON.stringify(query), body ? JSON.stringify(body) : null, cookies ? JSON.stringify(cookies) : null);
95
+ // Cleanup old entries if needed
96
+ this.cleanup();
97
+ return request;
98
+ }
99
+ /** Record a response */
100
+ recordResponse(requestId, statusCode, statusMessage, headers, body, duration, traceId) {
101
+ if (!this.db)
102
+ throw new Error('RequestRecorder not initialized');
103
+ const id = randomUUID();
104
+ const timestamp = Date.now();
105
+ const response = {
106
+ id,
107
+ requestId,
108
+ traceId: traceId || '',
109
+ timestamp,
110
+ statusCode,
111
+ statusMessage,
112
+ headers,
113
+ body,
114
+ duration: duration || 0,
115
+ };
116
+ const stmt = this.db.prepare(`
117
+ INSERT INTO responses (id, request_id, trace_id, timestamp, status_code, status_message, headers, body, duration)
118
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
119
+ `);
120
+ stmt.run(id, requestId, traceId || null, timestamp, statusCode, statusMessage, JSON.stringify(headers), body ? JSON.stringify(body) : null, duration || 0);
121
+ return response;
122
+ }
123
+ /** Record trace data */
124
+ recordTrace(traceId, trace, requestId) {
125
+ if (!this.db)
126
+ throw new Error('RequestRecorder not initialized');
127
+ const stmt = this.db.prepare(`
128
+ INSERT OR REPLACE INTO traces (trace_id, request_id, data, timestamp)
129
+ VALUES (?, ?, ?, ?)
130
+ `);
131
+ stmt.run(traceId, requestId || null, JSON.stringify(trace), Date.now());
132
+ }
133
+ /** Get a recorded exchange by ID */
134
+ getExchange(requestId) {
135
+ if (!this.db)
136
+ throw new Error('RequestRecorder not initialized');
137
+ const requestStmt = this.db.prepare('SELECT * FROM requests WHERE id = ?');
138
+ const requestRow = requestStmt.get(requestId);
139
+ if (!requestRow)
140
+ return null;
141
+ const responseStmt = this.db.prepare('SELECT * FROM responses WHERE request_id = ?');
142
+ const responseRow = responseStmt.get(requestId);
143
+ const traceStmt = this.db.prepare('SELECT * FROM traces WHERE request_id = ? OR trace_id = ?');
144
+ const traceRow = traceStmt.get(requestId, requestRow.trace_id);
145
+ return {
146
+ id: requestRow.id,
147
+ request: {
148
+ id: requestRow.id,
149
+ traceId: requestRow.trace_id || '',
150
+ timestamp: requestRow.timestamp,
151
+ method: requestRow.method,
152
+ path: requestRow.path,
153
+ url: requestRow.url,
154
+ headers: JSON.parse(requestRow.headers),
155
+ query: JSON.parse(requestRow.query),
156
+ body: requestRow.body ? JSON.parse(requestRow.body) : undefined,
157
+ cookies: requestRow.cookies ? JSON.parse(requestRow.cookies) : undefined,
158
+ },
159
+ response: responseRow
160
+ ? {
161
+ id: responseRow.id,
162
+ requestId: responseRow.request_id,
163
+ traceId: responseRow.trace_id || '',
164
+ timestamp: responseRow.timestamp,
165
+ statusCode: responseRow.status_code,
166
+ statusMessage: responseRow.status_message,
167
+ headers: JSON.parse(responseRow.headers),
168
+ body: responseRow.body ? JSON.parse(responseRow.body) : undefined,
169
+ duration: responseRow.duration,
170
+ }
171
+ : {
172
+ id: '',
173
+ requestId: requestRow.id,
174
+ traceId: '',
175
+ timestamp: 0,
176
+ statusCode: 0,
177
+ statusMessage: 'No response recorded',
178
+ headers: {},
179
+ duration: 0,
180
+ },
181
+ trace: traceRow ? JSON.parse(traceRow.data) : undefined,
182
+ };
183
+ }
184
+ /** Get recent exchanges */
185
+ getRecentExchanges(limit = 100, offset = 0) {
186
+ if (!this.db)
187
+ throw new Error('RequestRecorder not initialized');
188
+ const stmt = this.db.prepare(`
189
+ SELECT r.*,
190
+ res.id as res_id, res.timestamp as res_timestamp,
191
+ res.status_code, res.status_message,
192
+ res.headers as res_headers, res.body as res_body, res.duration,
193
+ t.data as trace_data
194
+ FROM requests r
195
+ LEFT JOIN responses res ON r.id = res.request_id
196
+ LEFT JOIN traces t ON r.trace_id = t.trace_id
197
+ ORDER BY r.timestamp DESC
198
+ LIMIT ? OFFSET ?
199
+ `);
200
+ const rows = stmt.all(limit, offset);
201
+ return rows.map((row) => ({
202
+ id: row.id,
203
+ request: {
204
+ id: row.id,
205
+ traceId: row.trace_id || '',
206
+ timestamp: row.timestamp,
207
+ method: row.method,
208
+ path: row.path,
209
+ url: row.url,
210
+ headers: JSON.parse(row.headers),
211
+ query: JSON.parse(row.query),
212
+ body: row.body ? JSON.parse(row.body) : undefined,
213
+ cookies: row.cookies ? JSON.parse(row.cookies) : undefined,
214
+ },
215
+ response: row.res_id
216
+ ? {
217
+ id: row.res_id,
218
+ requestId: row.id,
219
+ traceId: row.trace_id || '',
220
+ timestamp: row.res_timestamp,
221
+ statusCode: row.status_code,
222
+ statusMessage: row.status_message,
223
+ headers: JSON.parse(row.res_headers),
224
+ body: row.res_body ? JSON.parse(row.res_body) : undefined,
225
+ duration: row.duration,
226
+ }
227
+ : {
228
+ id: '',
229
+ requestId: row.id,
230
+ traceId: '',
231
+ timestamp: 0,
232
+ statusCode: 0,
233
+ statusMessage: 'No response recorded',
234
+ headers: {},
235
+ duration: 0,
236
+ },
237
+ trace: row.trace_data ? JSON.parse(row.trace_data) : undefined,
238
+ }));
239
+ }
240
+ /** Search exchanges by path or method */
241
+ searchExchanges(query, method, limit = 100) {
242
+ if (!this.db)
243
+ throw new Error('RequestRecorder not initialized');
244
+ let sql = `
245
+ SELECT r.*,
246
+ res.id as res_id, res.timestamp as res_timestamp,
247
+ res.status_code, res.status_message,
248
+ res.headers as res_headers, res.body as res_body, res.duration,
249
+ t.data as trace_data
250
+ FROM requests r
251
+ LEFT JOIN responses res ON r.id = res.request_id
252
+ LEFT JOIN traces t ON r.trace_id = t.trace_id
253
+ WHERE r.path LIKE ?
254
+ `;
255
+ const params = [`%${query}%`];
256
+ if (method) {
257
+ sql += ' AND r.method = ?';
258
+ params.push(method);
259
+ }
260
+ sql += ' ORDER BY r.timestamp DESC LIMIT ?';
261
+ params.push(limit);
262
+ const stmt = this.db.prepare(sql);
263
+ const rows = stmt.all(...params);
264
+ return rows.map((row) => ({
265
+ id: row.id,
266
+ request: {
267
+ id: row.id,
268
+ traceId: row.trace_id || '',
269
+ timestamp: row.timestamp,
270
+ method: row.method,
271
+ path: row.path,
272
+ url: row.url,
273
+ headers: JSON.parse(row.headers),
274
+ query: JSON.parse(row.query),
275
+ body: row.body ? JSON.parse(row.body) : undefined,
276
+ cookies: row.cookies ? JSON.parse(row.cookies) : undefined,
277
+ },
278
+ response: row.res_id
279
+ ? {
280
+ id: row.res_id,
281
+ requestId: row.id,
282
+ traceId: row.trace_id || '',
283
+ timestamp: row.res_timestamp,
284
+ statusCode: row.status_code,
285
+ statusMessage: row.status_message,
286
+ headers: JSON.parse(row.res_headers),
287
+ body: row.res_body ? JSON.parse(row.res_body) : undefined,
288
+ duration: row.duration,
289
+ }
290
+ : {
291
+ id: '',
292
+ requestId: row.id,
293
+ traceId: '',
294
+ timestamp: 0,
295
+ statusCode: 0,
296
+ statusMessage: 'No response recorded',
297
+ headers: {},
298
+ duration: 0,
299
+ },
300
+ trace: row.trace_data ? JSON.parse(row.trace_data) : undefined,
301
+ }));
302
+ }
303
+ /** Get statistics */
304
+ getStats() {
305
+ if (!this.db)
306
+ throw new Error('RequestRecorder not initialized');
307
+ const totalStmt = this.db.prepare('SELECT COUNT(*) as count FROM requests');
308
+ const totalRow = totalStmt.get();
309
+ const errorStmt = this.db.prepare('SELECT COUNT(*) as count FROM responses WHERE status_code >= 400');
310
+ const errorRow = errorStmt.get();
311
+ const avgStmt = this.db.prepare('SELECT AVG(duration) as avg FROM responses');
312
+ const avgRow = avgStmt.get();
313
+ const pathStmt = this.db.prepare('SELECT path, COUNT(*) as count FROM requests GROUP BY path ORDER BY count DESC LIMIT 20');
314
+ const pathRows = pathStmt.all();
315
+ const methodStmt = this.db.prepare('SELECT method, COUNT(*) as count FROM requests GROUP BY method');
316
+ const methodRows = methodStmt.all();
317
+ const requestsByPath = {};
318
+ for (const row of pathRows) {
319
+ requestsByPath[row.path] = row.count;
320
+ }
321
+ const requestsByMethod = {};
322
+ for (const row of methodRows) {
323
+ requestsByMethod[row.method] = row.count;
324
+ }
325
+ return {
326
+ totalRequests: totalRow.count,
327
+ totalErrors: errorRow.count,
328
+ avgDuration: avgRow.avg || 0,
329
+ requestsByPath,
330
+ requestsByMethod,
331
+ };
332
+ }
333
+ /** Delete an exchange */
334
+ deleteExchange(requestId) {
335
+ if (!this.db)
336
+ throw new Error('RequestRecorder not initialized');
337
+ const stmt = this.db.prepare('DELETE FROM requests WHERE id = ?');
338
+ stmt.run(requestId);
339
+ }
340
+ /** Clear all recorded data */
341
+ clearAll() {
342
+ if (!this.db)
343
+ throw new Error('RequestRecorder not initialized');
344
+ this.db.exec('DELETE FROM traces');
345
+ this.db.exec('DELETE FROM responses');
346
+ this.db.exec('DELETE FROM requests');
347
+ }
348
+ /** Cleanup old entries if exceeding max */
349
+ cleanup() {
350
+ if (!this.db)
351
+ return;
352
+ const countStmt = this.db.prepare('SELECT COUNT(*) as count FROM requests');
353
+ const row = countStmt.get();
354
+ if (row.count > this.maxExchanges) {
355
+ const deleteCount = row.count - this.maxExchanges;
356
+ const deleteStmt = this.db.prepare(`
357
+ DELETE FROM requests WHERE id IN (
358
+ SELECT id FROM requests ORDER BY timestamp ASC LIMIT ?
359
+ )
360
+ `);
361
+ deleteStmt.run(deleteCount);
362
+ }
363
+ }
364
+ /** Close the database connection */
365
+ close() {
366
+ if (this.db) {
367
+ this.db.close();
368
+ this.db = null;
369
+ this.initialized = false;
370
+ }
371
+ }
372
+ }
373
+ //# sourceMappingURL=request-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-recorder.js","sourceRoot":"","sources":["../../src/recording/request-recorder.ts"],"names":[],"mappings":"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"}