@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,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"}