@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,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry tracer configuration for ExpressoTS Studio
|
|
3
|
+
*/
|
|
4
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
5
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
6
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
7
|
+
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from '@opentelemetry/semantic-conventions';
|
|
8
|
+
import { trace, context, SpanKind, SpanStatusCode } from '@opentelemetry/api';
|
|
9
|
+
/** Custom span processor that emits spans to Studio */
|
|
10
|
+
export class StudioSpanProcessor {
|
|
11
|
+
spans = new Map();
|
|
12
|
+
onTraceComplete;
|
|
13
|
+
traceTimeouts = new Map();
|
|
14
|
+
traceCompletionDelay = 1000; // Wait 1 second for all spans
|
|
15
|
+
constructor(onTraceComplete) {
|
|
16
|
+
this.onTraceComplete = onTraceComplete;
|
|
17
|
+
}
|
|
18
|
+
forceFlush() {
|
|
19
|
+
return Promise.resolve();
|
|
20
|
+
}
|
|
21
|
+
onStart(_span) {
|
|
22
|
+
// Span started - can emit event if needed
|
|
23
|
+
}
|
|
24
|
+
onEnd(span) {
|
|
25
|
+
const traceId = span.spanContext().traceId;
|
|
26
|
+
const spanInfo = this.convertSpan(span);
|
|
27
|
+
if (!this.spans.has(traceId)) {
|
|
28
|
+
this.spans.set(traceId, []);
|
|
29
|
+
}
|
|
30
|
+
this.spans.get(traceId).push(spanInfo);
|
|
31
|
+
// Reset timeout for this trace
|
|
32
|
+
const existingTimeout = this.traceTimeouts.get(traceId);
|
|
33
|
+
if (existingTimeout) {
|
|
34
|
+
clearTimeout(existingTimeout);
|
|
35
|
+
}
|
|
36
|
+
// Set new timeout to complete the trace
|
|
37
|
+
const timeout = setTimeout(() => {
|
|
38
|
+
this.completeTrace(traceId);
|
|
39
|
+
}, this.traceCompletionDelay);
|
|
40
|
+
this.traceTimeouts.set(traceId, timeout);
|
|
41
|
+
}
|
|
42
|
+
completeTrace(traceId) {
|
|
43
|
+
const spans = this.spans.get(traceId);
|
|
44
|
+
if (!spans || spans.length === 0)
|
|
45
|
+
return;
|
|
46
|
+
// Find root span (one without parent or with parent not in this trace)
|
|
47
|
+
const spanIds = new Set(spans.map((s) => s.spanId));
|
|
48
|
+
const rootSpan = spans.find((s) => !s.parentSpanId || !spanIds.has(s.parentSpanId)) || spans[0];
|
|
49
|
+
const traceInfo = {
|
|
50
|
+
traceId,
|
|
51
|
+
rootSpan,
|
|
52
|
+
spans: spans.sort((a, b) => a.startTime - b.startTime),
|
|
53
|
+
startTime: Math.min(...spans.map((s) => s.startTime)),
|
|
54
|
+
endTime: Math.max(...spans.map((s) => s.endTime)),
|
|
55
|
+
duration: 0,
|
|
56
|
+
};
|
|
57
|
+
traceInfo.duration = traceInfo.endTime - traceInfo.startTime;
|
|
58
|
+
this.onTraceComplete?.(traceInfo);
|
|
59
|
+
// Cleanup
|
|
60
|
+
this.spans.delete(traceId);
|
|
61
|
+
this.traceTimeouts.delete(traceId);
|
|
62
|
+
}
|
|
63
|
+
convertSpan(span) {
|
|
64
|
+
const attributes = {};
|
|
65
|
+
for (const [key, value] of Object.entries(span.attributes)) {
|
|
66
|
+
if (typeof value === 'string' ||
|
|
67
|
+
typeof value === 'number' ||
|
|
68
|
+
typeof value === 'boolean') {
|
|
69
|
+
attributes[key] = value;
|
|
70
|
+
}
|
|
71
|
+
else if (value !== undefined) {
|
|
72
|
+
attributes[key] = String(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
traceId: span.spanContext().traceId,
|
|
77
|
+
spanId: span.spanContext().spanId,
|
|
78
|
+
parentSpanId: span.parentSpanContext?.spanId,
|
|
79
|
+
name: span.name,
|
|
80
|
+
kind: this.getSpanKindName(span.kind),
|
|
81
|
+
startTime: span.startTime[0] * 1000 + span.startTime[1] / 1e6,
|
|
82
|
+
endTime: span.endTime[0] * 1000 + span.endTime[1] / 1e6,
|
|
83
|
+
duration: (span.endTime[0] - span.startTime[0]) * 1000 +
|
|
84
|
+
(span.endTime[1] - span.startTime[1]) / 1e6,
|
|
85
|
+
status: this.getStatusName(span.status.code),
|
|
86
|
+
attributes,
|
|
87
|
+
events: span.events.map((e) => ({
|
|
88
|
+
name: e.name,
|
|
89
|
+
timestamp: e.time[0] * 1000 + e.time[1] / 1e6,
|
|
90
|
+
attributes: e.attributes,
|
|
91
|
+
})),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
getSpanKindName(kind) {
|
|
95
|
+
switch (kind) {
|
|
96
|
+
case SpanKind.SERVER:
|
|
97
|
+
return 'SERVER';
|
|
98
|
+
case SpanKind.CLIENT:
|
|
99
|
+
return 'CLIENT';
|
|
100
|
+
case SpanKind.PRODUCER:
|
|
101
|
+
return 'PRODUCER';
|
|
102
|
+
case SpanKind.CONSUMER:
|
|
103
|
+
return 'CONSUMER';
|
|
104
|
+
default:
|
|
105
|
+
return 'INTERNAL';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
getStatusName(code) {
|
|
109
|
+
switch (code) {
|
|
110
|
+
case SpanStatusCode.OK:
|
|
111
|
+
return 'OK';
|
|
112
|
+
case SpanStatusCode.ERROR:
|
|
113
|
+
return 'ERROR';
|
|
114
|
+
default:
|
|
115
|
+
return 'UNSET';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
shutdown() {
|
|
119
|
+
for (const timeout of this.traceTimeouts.values()) {
|
|
120
|
+
clearTimeout(timeout);
|
|
121
|
+
}
|
|
122
|
+
this.traceTimeouts.clear();
|
|
123
|
+
this.spans.clear();
|
|
124
|
+
return Promise.resolve();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** OpenTelemetry SDK wrapper */
|
|
128
|
+
export class StudioTracer {
|
|
129
|
+
sdk = null;
|
|
130
|
+
spanProcessor = null;
|
|
131
|
+
serviceName;
|
|
132
|
+
serviceVersion;
|
|
133
|
+
constructor(serviceName = 'expressots-app', serviceVersion = '1.0.0') {
|
|
134
|
+
this.serviceName = serviceName;
|
|
135
|
+
this.serviceVersion = serviceVersion;
|
|
136
|
+
}
|
|
137
|
+
/** Initialize the OpenTelemetry SDK */
|
|
138
|
+
async start(onTraceComplete) {
|
|
139
|
+
this.spanProcessor = new StudioSpanProcessor(onTraceComplete);
|
|
140
|
+
this.sdk = new NodeSDK({
|
|
141
|
+
resource: resourceFromAttributes({
|
|
142
|
+
[ATTR_SERVICE_NAME]: this.serviceName,
|
|
143
|
+
[ATTR_SERVICE_VERSION]: this.serviceVersion,
|
|
144
|
+
}),
|
|
145
|
+
spanProcessors: [this.spanProcessor],
|
|
146
|
+
instrumentations: [
|
|
147
|
+
getNodeAutoInstrumentations({
|
|
148
|
+
'@opentelemetry/instrumentation-fs': { enabled: false },
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
await this.sdk.start();
|
|
153
|
+
}
|
|
154
|
+
/** Stop the OpenTelemetry SDK */
|
|
155
|
+
async stop() {
|
|
156
|
+
if (this.sdk) {
|
|
157
|
+
await this.sdk.shutdown();
|
|
158
|
+
this.sdk = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Get the current tracer */
|
|
162
|
+
getTracer(name = 'studio-agent') {
|
|
163
|
+
return trace.getTracer(name);
|
|
164
|
+
}
|
|
165
|
+
/** Create a custom span */
|
|
166
|
+
createSpan(name, fn, attributes) {
|
|
167
|
+
const tracer = this.getTracer();
|
|
168
|
+
return tracer.startActiveSpan(name, async (span) => {
|
|
169
|
+
if (attributes) {
|
|
170
|
+
span.setAttributes(attributes);
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
await fn();
|
|
174
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
span.setStatus({
|
|
178
|
+
code: SpanStatusCode.ERROR,
|
|
179
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
180
|
+
});
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
span.end();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export { trace, context, SpanKind, SpanStatusCode };
|
|
190
|
+
//# sourceMappingURL=tracer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracer.js","sourceRoot":"","sources":["../../src/instrumentation/tracer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Introspector
|
|
3
|
+
*
|
|
4
|
+
* Builds a DI snapshot (bindings + summary + dependency graph) from an
|
|
5
|
+
* ExpressoTS `AppContainer` and tracks which bindings are resolved during
|
|
6
|
+
* each request via Inversify's middleware hook + AsyncLocalStorage.
|
|
7
|
+
*
|
|
8
|
+
* Designed to fail gracefully: if the container is missing, if it isn't
|
|
9
|
+
* the expected ExpressoTS/Inversify shape, or if Reflect metadata is
|
|
10
|
+
* unavailable, every method returns an empty result instead of throwing.
|
|
11
|
+
*/
|
|
12
|
+
export interface BindingNode {
|
|
13
|
+
id: string;
|
|
14
|
+
serviceIdentifier: string;
|
|
15
|
+
className: string;
|
|
16
|
+
scope: string;
|
|
17
|
+
type: string;
|
|
18
|
+
activated: boolean;
|
|
19
|
+
cached: boolean;
|
|
20
|
+
moduleId?: number | string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface BindingEdge {
|
|
23
|
+
source: string;
|
|
24
|
+
target: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ContainerSnapshot {
|
|
27
|
+
bindings: BindingNode[];
|
|
28
|
+
edges: BindingEdge[];
|
|
29
|
+
summary: {
|
|
30
|
+
total: number;
|
|
31
|
+
byScope: Record<string, number>;
|
|
32
|
+
byType: Record<string, number>;
|
|
33
|
+
cached: number;
|
|
34
|
+
activated: number;
|
|
35
|
+
};
|
|
36
|
+
options?: Record<string, unknown>;
|
|
37
|
+
timestamp: string;
|
|
38
|
+
containerId: number;
|
|
39
|
+
}
|
|
40
|
+
interface RequestContext {
|
|
41
|
+
traceId?: string;
|
|
42
|
+
resolved: Set<string>;
|
|
43
|
+
}
|
|
44
|
+
export declare class ContainerIntrospector {
|
|
45
|
+
private appContainer;
|
|
46
|
+
private als;
|
|
47
|
+
private snapshot;
|
|
48
|
+
constructor(appContainer: unknown);
|
|
49
|
+
/** Whether we have a usable AppContainer / Inversify container reference. */
|
|
50
|
+
isAvailable(): boolean;
|
|
51
|
+
/** Builds the snapshot once and caches it. */
|
|
52
|
+
capture(): ContainerSnapshot;
|
|
53
|
+
/**
|
|
54
|
+
* Installs an Inversify middleware that records every `container.get(...)`
|
|
55
|
+
* resolution into the active request's resolved-set. Safe to call once.
|
|
56
|
+
*/
|
|
57
|
+
installResolutionTracker(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Runs `fn` inside a request scope so resolutions get attributed to a
|
|
60
|
+
* traceId. Returns the set of resolved service identifiers when the
|
|
61
|
+
* callback completes.
|
|
62
|
+
*/
|
|
63
|
+
runWithRequest<T>(traceId: string | undefined, fn: () => T): {
|
|
64
|
+
result: T;
|
|
65
|
+
resolved: Set<string>;
|
|
66
|
+
};
|
|
67
|
+
/** Get the current request context (or undefined when not in a request). */
|
|
68
|
+
getCurrentRequest(): RequestContext | undefined;
|
|
69
|
+
private getInversifyContainer;
|
|
70
|
+
private scanBindings;
|
|
71
|
+
/**
|
|
72
|
+
* Reads inversify's constructor-injection metadata and returns the list of
|
|
73
|
+
* service identifiers each constructor parameter depends on.
|
|
74
|
+
*/
|
|
75
|
+
private getConstructorDependencies;
|
|
76
|
+
private computeSummary;
|
|
77
|
+
private formatServiceIdentifier;
|
|
78
|
+
private emptySnapshot;
|
|
79
|
+
}
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=container-introspector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-introspector.d.ts","sourceRoot":"","sources":["../../src/introspection/container-introspector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAcD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,GAAG,CAAoC;IAC/C,OAAO,CAAC,QAAQ,CAAkC;gBAEtC,YAAY,EAAE,OAAO;IAKjC,6EAA6E;IAC7E,WAAW,IAAI,OAAO;IAMtB,8CAA8C;IAC9C,OAAO,IAAI,iBAAiB;IAgC5B;;;OAGG;IACH,wBAAwB,IAAI,IAAI;IA2BhC;;;;OAIG;IACH,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG;QAC3D,MAAM,EAAE,CAAC,CAAC;QACV,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;KACvB;IAMD,4EAA4E;IAC5E,iBAAiB,IAAI,cAAc,GAAG,SAAS;IAQ/C,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,YAAY;IA0DpB;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAoClC,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,aAAa;CAUtB"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Introspector
|
|
3
|
+
*
|
|
4
|
+
* Builds a DI snapshot (bindings + summary + dependency graph) from an
|
|
5
|
+
* ExpressoTS `AppContainer` and tracks which bindings are resolved during
|
|
6
|
+
* each request via Inversify's middleware hook + AsyncLocalStorage.
|
|
7
|
+
*
|
|
8
|
+
* Designed to fail gracefully: if the container is missing, if it isn't
|
|
9
|
+
* the expected ExpressoTS/Inversify shape, or if Reflect metadata is
|
|
10
|
+
* unavailable, every method returns an empty result instead of throwing.
|
|
11
|
+
*/
|
|
12
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
13
|
+
/** Reflect metadata keys used by Inversify (vendored copy in @expressots/core). */
|
|
14
|
+
const INVERSIFY_TAGGED = 'inversify:tagged';
|
|
15
|
+
const INVERSIFY_TAGGED_PROPS = 'inversify:tagged_props';
|
|
16
|
+
/**
|
|
17
|
+
* Typed view of `Reflect.getMetadata` (provided by `reflect-metadata` which
|
|
18
|
+
* `inversify` already loads — but our local `tsconfig` doesn't pull in those
|
|
19
|
+
* type declarations, so we cast through `any` for compile time).
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const ReflectAny = Reflect;
|
|
23
|
+
export class ContainerIntrospector {
|
|
24
|
+
appContainer;
|
|
25
|
+
als;
|
|
26
|
+
snapshot = null;
|
|
27
|
+
constructor(appContainer) {
|
|
28
|
+
this.appContainer = appContainer;
|
|
29
|
+
this.als = new AsyncLocalStorage();
|
|
30
|
+
}
|
|
31
|
+
/** Whether we have a usable AppContainer / Inversify container reference. */
|
|
32
|
+
isAvailable() {
|
|
33
|
+
if (!this.appContainer)
|
|
34
|
+
return false;
|
|
35
|
+
const inversify = this.getInversifyContainer();
|
|
36
|
+
return Boolean(inversify);
|
|
37
|
+
}
|
|
38
|
+
/** Builds the snapshot once and caches it. */
|
|
39
|
+
capture() {
|
|
40
|
+
if (this.snapshot)
|
|
41
|
+
return this.snapshot;
|
|
42
|
+
const inversify = this.getInversifyContainer();
|
|
43
|
+
if (!inversify) {
|
|
44
|
+
this.snapshot = this.emptySnapshot();
|
|
45
|
+
return this.snapshot;
|
|
46
|
+
}
|
|
47
|
+
let introspection = null;
|
|
48
|
+
try {
|
|
49
|
+
introspection = typeof this.appContainer.introspect === 'function'
|
|
50
|
+
? this.appContainer.introspect()
|
|
51
|
+
: null;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
introspection = null;
|
|
55
|
+
}
|
|
56
|
+
const { nodes, edges } = this.scanBindings(inversify);
|
|
57
|
+
const summary = this.computeSummary(nodes);
|
|
58
|
+
this.snapshot = {
|
|
59
|
+
bindings: nodes,
|
|
60
|
+
edges,
|
|
61
|
+
summary,
|
|
62
|
+
options: introspection?.options ?? {},
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
containerId: introspection?.containerId ?? inversify?.id ?? -1,
|
|
65
|
+
};
|
|
66
|
+
return this.snapshot;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Installs an Inversify middleware that records every `container.get(...)`
|
|
70
|
+
* resolution into the active request's resolved-set. Safe to call once.
|
|
71
|
+
*/
|
|
72
|
+
installResolutionTracker() {
|
|
73
|
+
const inversify = this.getInversifyContainer();
|
|
74
|
+
if (!inversify || typeof inversify.applyMiddleware !== 'function') {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const als = this.als;
|
|
78
|
+
const trackingMiddleware = (planAndResolve) => (args) => {
|
|
79
|
+
const ctx = als.getStore();
|
|
80
|
+
if (ctx) {
|
|
81
|
+
try {
|
|
82
|
+
ctx.resolved.add(this.formatServiceIdentifier(args?.serviceIdentifier));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Tracking is best-effort; never break resolution.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return planAndResolve(args);
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
inversify.applyMiddleware(trackingMiddleware);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Older Inversify versions or wrapped containers may not allow this.
|
|
95
|
+
// Failing to track per-request resolutions is non-fatal.
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Runs `fn` inside a request scope so resolutions get attributed to a
|
|
100
|
+
* traceId. Returns the set of resolved service identifiers when the
|
|
101
|
+
* callback completes.
|
|
102
|
+
*/
|
|
103
|
+
runWithRequest(traceId, fn) {
|
|
104
|
+
const ctx = { traceId, resolved: new Set() };
|
|
105
|
+
const result = this.als.run(ctx, fn);
|
|
106
|
+
return { result, resolved: ctx.resolved };
|
|
107
|
+
}
|
|
108
|
+
/** Get the current request context (or undefined when not in a request). */
|
|
109
|
+
getCurrentRequest() {
|
|
110
|
+
return this.als.getStore();
|
|
111
|
+
}
|
|
112
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
113
|
+
// Internals
|
|
114
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
115
|
+
getInversifyContainer() {
|
|
116
|
+
if (!this.appContainer)
|
|
117
|
+
return null;
|
|
118
|
+
// ExpressoTS AppContainer exposes `Container` as the inversify instance.
|
|
119
|
+
const inv = this.appContainer.Container ??
|
|
120
|
+
this.appContainer.container ??
|
|
121
|
+
this.appContainer;
|
|
122
|
+
if (!inv)
|
|
123
|
+
return null;
|
|
124
|
+
if (!inv._bindingDictionary)
|
|
125
|
+
return null;
|
|
126
|
+
return inv;
|
|
127
|
+
}
|
|
128
|
+
scanBindings(inversify) {
|
|
129
|
+
const nodes = [];
|
|
130
|
+
const edges = [];
|
|
131
|
+
const dict = inversify._bindingDictionary;
|
|
132
|
+
const map = dict?._map;
|
|
133
|
+
if (!map)
|
|
134
|
+
return { nodes, edges };
|
|
135
|
+
// First pass: build all nodes so we know which service identifiers exist.
|
|
136
|
+
const sidToNodeId = new Map();
|
|
137
|
+
for (const [sid, bindings] of map) {
|
|
138
|
+
for (const binding of bindings) {
|
|
139
|
+
const sidStr = this.formatServiceIdentifier(sid);
|
|
140
|
+
const impl = binding?.implementationType;
|
|
141
|
+
const className = (impl && impl.name) || sidStr;
|
|
142
|
+
// Unique id per binding (multiple bindings may share an identifier).
|
|
143
|
+
const id = `${sidStr}#${nodes.length}`;
|
|
144
|
+
nodes.push({
|
|
145
|
+
id,
|
|
146
|
+
serviceIdentifier: sidStr,
|
|
147
|
+
className,
|
|
148
|
+
scope: String(binding?.scope ?? 'Singleton'),
|
|
149
|
+
type: String(binding?.type ?? 'Instance'),
|
|
150
|
+
activated: Boolean(binding?.activated),
|
|
151
|
+
cached: binding?.cache !== null && binding?.cache !== undefined,
|
|
152
|
+
moduleId: binding?.moduleId ?? null,
|
|
153
|
+
});
|
|
154
|
+
if (!sidToNodeId.has(sidStr)) {
|
|
155
|
+
sidToNodeId.set(sidStr, id);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Second pass: emit edges using inversify's tagged-paramtypes metadata.
|
|
160
|
+
for (const [sid, bindings] of map) {
|
|
161
|
+
for (const binding of bindings) {
|
|
162
|
+
const impl = binding?.implementationType;
|
|
163
|
+
if (!impl)
|
|
164
|
+
continue;
|
|
165
|
+
const sidStr = this.formatServiceIdentifier(sid);
|
|
166
|
+
const sourceId = sidToNodeId.get(sidStr);
|
|
167
|
+
if (!sourceId)
|
|
168
|
+
continue;
|
|
169
|
+
const deps = this.getConstructorDependencies(impl);
|
|
170
|
+
for (const depSid of deps) {
|
|
171
|
+
const targetId = sidToNodeId.get(depSid);
|
|
172
|
+
if (targetId) {
|
|
173
|
+
edges.push({ source: sourceId, target: targetId });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { nodes, edges };
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Reads inversify's constructor-injection metadata and returns the list of
|
|
182
|
+
* service identifiers each constructor parameter depends on.
|
|
183
|
+
*/
|
|
184
|
+
getConstructorDependencies(impl) {
|
|
185
|
+
const out = [];
|
|
186
|
+
if (!impl)
|
|
187
|
+
return out;
|
|
188
|
+
// Inversify stores `@inject(SID)` constructor params under "inversify:tagged"
|
|
189
|
+
// keyed by the prototype + index. The simpler `inversify:paramtypes` key on
|
|
190
|
+
// the prototype gives the TS design types — useful as a fallback.
|
|
191
|
+
const tagged = ReflectAny.getMetadata?.(INVERSIFY_TAGGED, impl);
|
|
192
|
+
if (tagged && typeof tagged.forEach === 'function') {
|
|
193
|
+
tagged.forEach((metadataList) => {
|
|
194
|
+
for (const m of metadataList || []) {
|
|
195
|
+
if (m?.key === 'inject' && m?.value !== undefined) {
|
|
196
|
+
out.push(this.formatServiceIdentifier(m.value));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
// Property-injected dependencies via @inject() on properties.
|
|
202
|
+
const taggedProps = ReflectAny.getMetadata?.(INVERSIFY_TAGGED_PROPS, impl);
|
|
203
|
+
if (taggedProps && typeof taggedProps === 'object') {
|
|
204
|
+
for (const propKey of Object.keys(taggedProps)) {
|
|
205
|
+
for (const m of taggedProps[propKey] || []) {
|
|
206
|
+
if (m?.key === 'inject' && m?.value !== undefined) {
|
|
207
|
+
out.push(this.formatServiceIdentifier(m.value));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
computeSummary(nodes) {
|
|
215
|
+
const byScope = {};
|
|
216
|
+
const byType = {};
|
|
217
|
+
let cached = 0;
|
|
218
|
+
let activated = 0;
|
|
219
|
+
for (const n of nodes) {
|
|
220
|
+
byScope[n.scope] = (byScope[n.scope] ?? 0) + 1;
|
|
221
|
+
byType[n.type] = (byType[n.type] ?? 0) + 1;
|
|
222
|
+
if (n.cached)
|
|
223
|
+
cached++;
|
|
224
|
+
if (n.activated)
|
|
225
|
+
activated++;
|
|
226
|
+
}
|
|
227
|
+
return { total: nodes.length, byScope, byType, cached, activated };
|
|
228
|
+
}
|
|
229
|
+
formatServiceIdentifier(sid) {
|
|
230
|
+
if (sid == null)
|
|
231
|
+
return 'unknown';
|
|
232
|
+
if (typeof sid === 'string')
|
|
233
|
+
return sid;
|
|
234
|
+
if (typeof sid === 'symbol')
|
|
235
|
+
return sid.toString();
|
|
236
|
+
if (typeof sid === 'function')
|
|
237
|
+
return sid.name || sid.toString();
|
|
238
|
+
return String(sid);
|
|
239
|
+
}
|
|
240
|
+
emptySnapshot() {
|
|
241
|
+
return {
|
|
242
|
+
bindings: [],
|
|
243
|
+
edges: [],
|
|
244
|
+
summary: { total: 0, byScope: {}, byType: {}, cached: 0, activated: 0 },
|
|
245
|
+
options: {},
|
|
246
|
+
timestamp: new Date().toISOString(),
|
|
247
|
+
containerId: -1,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=container-introspector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-introspector.js","sourceRoot":"","sources":["../../src/introspection/container-introspector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
16
|
+
export interface LogEntry {
|
|
17
|
+
level: LogLevel;
|
|
18
|
+
message: string;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
traceId?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class LogCapture {
|
|
23
|
+
private buffer;
|
|
24
|
+
private readonly 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
|
+
private readonly current;
|
|
33
|
+
private listeners;
|
|
34
|
+
private installed;
|
|
35
|
+
constructor(maxBuffer?: number);
|
|
36
|
+
/**
|
|
37
|
+
* Install a permanent hook on each console method. Uses an accessor
|
|
38
|
+
* property so subsequent `console.log = …` assignments (which the
|
|
39
|
+
* framework performs during its startup buffering lifecycle) are
|
|
40
|
+
* absorbed by our setter rather than wiping out the wrapper.
|
|
41
|
+
*/
|
|
42
|
+
install(): void;
|
|
43
|
+
/** Restore the original console methods (used in tests / hot-reload). */
|
|
44
|
+
uninstall(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Run `fn` inside a request scope so any `console.*` calls during the
|
|
47
|
+
* request body are tagged with the given traceId.
|
|
48
|
+
*/
|
|
49
|
+
runWith<T>(traceId: string, fn: () => T): T;
|
|
50
|
+
/** Subscribe to live log events. Returns an unsubscribe function. */
|
|
51
|
+
onLog(listener: (entry: LogEntry) => void): () => void;
|
|
52
|
+
/** Snapshot of the in-memory ring buffer (oldest first). */
|
|
53
|
+
getBuffer(): LogEntry[];
|
|
54
|
+
/** Clear the in-memory buffer. */
|
|
55
|
+
clear(): void;
|
|
56
|
+
private handleLog;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=log-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-capture.d.ts","sourceRoot":"","sources":["../../src/logging/log-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAcD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqD;IAC7E,OAAO,CAAC,SAAS,CAAwC;IACzD,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,SAAO;IAI5B;;;;;OAKG;IACH,OAAO,IAAI,IAAI;IAqDf,yEAAyE;IACzE,SAAS,IAAI,IAAI;IAiBjB;;;OAGG;IACH,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAI3C,qEAAqE;IACrE,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI;IAKtD,4DAA4D;IAC5D,SAAS,IAAI,QAAQ,EAAE;IAIvB,kCAAkC;IAClC,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,SAAS;CAuBlB"}
|