@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.
- package/README.md +143 -0
- package/dist/agent.d.ts +127 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +1031 -0
- package/dist/agent.js.map +1 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +2 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/route-scanner.d.ts +35 -0
- package/dist/discovery/route-scanner.d.ts.map +1 -0
- package/dist/discovery/route-scanner.js +385 -0
- package/dist/discovery/route-scanner.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/index.d.ts +2 -0
- package/dist/instrumentation/index.d.ts.map +1 -0
- package/dist/instrumentation/index.js +2 -0
- package/dist/instrumentation/index.js.map +1 -0
- package/dist/instrumentation/tracer.d.ts +40 -0
- package/dist/instrumentation/tracer.d.ts.map +1 -0
- package/dist/instrumentation/tracer.js +190 -0
- package/dist/instrumentation/tracer.js.map +1 -0
- package/dist/introspection/container-introspector.d.ts +81 -0
- package/dist/introspection/container-introspector.d.ts.map +1 -0
- package/dist/introspection/container-introspector.js +251 -0
- package/dist/introspection/container-introspector.js.map +1 -0
- package/dist/logging/log-capture.d.ts +58 -0
- package/dist/logging/log-capture.d.ts.map +1 -0
- package/dist/logging/log-capture.js +184 -0
- package/dist/logging/log-capture.js.map +1 -0
- package/dist/recording/index.d.ts +2 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +2 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/request-recorder.d.ts +43 -0
- package/dist/recording/request-recorder.d.ts.map +1 -0
- package/dist/recording/request-recorder.js +373 -0
- package/dist/recording/request-recorder.js.map +1 -0
- package/dist/security/fix-resolver.d.ts +40 -0
- package/dist/security/fix-resolver.d.ts.map +1 -0
- package/dist/security/fix-resolver.js +283 -0
- package/dist/security/fix-resolver.js.map +1 -0
- package/dist/security/fix-runner.d.ts +60 -0
- package/dist/security/fix-runner.d.ts.map +1 -0
- package/dist/security/fix-runner.js +188 -0
- package/dist/security/fix-runner.js.map +1 -0
- package/dist/security/index.d.ts +140 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +460 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/lockfile-graph.d.ts +69 -0
- package/dist/security/lockfile-graph.d.ts.map +1 -0
- package/dist/security/lockfile-graph.js +245 -0
- package/dist/security/lockfile-graph.js.map +1 -0
- package/dist/security/npm-audit.d.ts +67 -0
- package/dist/security/npm-audit.d.ts.map +1 -0
- package/dist/security/npm-audit.js +320 -0
- package/dist/security/npm-audit.js.map +1 -0
- package/dist/security/osv-cache.d.ts +51 -0
- package/dist/security/osv-cache.d.ts.map +1 -0
- package/dist/security/osv-cache.js +99 -0
- package/dist/security/osv-cache.js.map +1 -0
- package/dist/security/osv-client.d.ts +47 -0
- package/dist/security/osv-client.d.ts.map +1 -0
- package/dist/security/osv-client.js +247 -0
- package/dist/security/osv-client.js.map +1 -0
- package/dist/security/posture-analyzer.d.ts +44 -0
- package/dist/security/posture-analyzer.d.ts.map +1 -0
- package/dist/security/posture-analyzer.js +397 -0
- package/dist/security/posture-analyzer.js.map +1 -0
- package/dist/security/reachability.d.ts +59 -0
- package/dist/security/reachability.d.ts.map +1 -0
- package/dist/security/reachability.js +302 -0
- package/dist/security/reachability.js.map +1 -0
- package/dist/security/score.d.ts +36 -0
- package/dist/security/score.d.ts.map +1 -0
- package/dist/security/score.js +94 -0
- package/dist/security/score.js.map +1 -0
- package/dist/types/index.d.ts +587 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +14 -0
- package/dist/types/index.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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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"}
|