@neat.is/core 0.2.9 → 0.3.0
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/dist/{chunk-IRPH6KL4.js → chunk-B7UUGIXB.js} +3 -1
- package/dist/chunk-B7UUGIXB.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-BVF7MVR5.js → chunk-I5KODOJV.js} +2 -2
- package/dist/{chunk-5KX7EI4F.js → chunk-NVCEZXL7.js} +331 -20
- package/dist/chunk-NVCEZXL7.js.map +1 -0
- package/dist/{chunk-I5IMCXRO.js → chunk-QYUB3FQL.js} +2 -2
- package/dist/cli.cjs +443 -42
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +111 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +323 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -12
- package/dist/index.d.ts +4 -12
- package/dist/index.js +5 -4
- package/dist/neatd.cjs +106 -5
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +112 -6
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-B4XBSI4W.js → otel-grpc-USFL3OI3.js} +3 -2
- package/dist/otel-grpc-USFL3OI3.js.map +1 -0
- package/dist/server.cjs +431 -125
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -3
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-5KX7EI4F.js.map +0 -1
- package/dist/chunk-IRPH6KL4.js.map +0 -1
- /package/dist/{otel-grpc-B4XBSI4W.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-BVF7MVR5.js.map → chunk-I5KODOJV.js.map} +0 -0
- /package/dist/{chunk-I5IMCXRO.js.map → chunk-QYUB3FQL.js.map} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
startPersistLoop,
|
|
12
12
|
touchLastSeen,
|
|
13
13
|
writeAtomically
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-B7UUGIXB.js";
|
|
15
15
|
|
|
16
16
|
// src/daemon.ts
|
|
17
17
|
import { promises as fs } from "fs";
|
|
@@ -159,4 +159,4 @@ export {
|
|
|
159
159
|
routeSpanToProject,
|
|
160
160
|
startDaemon
|
|
161
161
|
};
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
162
|
+
//# sourceMappingURL=chunk-I5KODOJV.js.map
|
|
@@ -5,10 +5,16 @@ import {
|
|
|
5
5
|
Projects,
|
|
6
6
|
TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH,
|
|
7
7
|
TRANSITIVE_DEPENDENCIES_MAX_DEPTH,
|
|
8
|
+
checkCompatibility,
|
|
9
|
+
checkDeprecatedApi,
|
|
10
|
+
compatPairs,
|
|
11
|
+
confidenceForEdge,
|
|
12
|
+
deprecatedApis,
|
|
8
13
|
evaluateAllPolicies,
|
|
9
14
|
eventBus,
|
|
10
15
|
extractFromDirectory,
|
|
11
16
|
getBlastRadius,
|
|
17
|
+
getProject,
|
|
12
18
|
getRootCause,
|
|
13
19
|
getTransitiveDependencies,
|
|
14
20
|
listProjects,
|
|
@@ -16,7 +22,7 @@ import {
|
|
|
16
22
|
pathsForProject,
|
|
17
23
|
readErrorEvents,
|
|
18
24
|
readStaleEvents
|
|
19
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-B7UUGIXB.js";
|
|
20
26
|
|
|
21
27
|
// src/diff.ts
|
|
22
28
|
import { promises as fs } from "fs";
|
|
@@ -97,7 +103,249 @@ function canonicalJson(value) {
|
|
|
97
103
|
// src/api.ts
|
|
98
104
|
import Fastify from "fastify";
|
|
99
105
|
import cors from "@fastify/cors";
|
|
100
|
-
import { PoliciesCheckBodySchema, PolicySeveritySchema } from "@neat.is/types";
|
|
106
|
+
import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } from "@neat.is/types";
|
|
107
|
+
|
|
108
|
+
// src/divergences.ts
|
|
109
|
+
import {
|
|
110
|
+
DivergenceResultSchema,
|
|
111
|
+
EdgeType,
|
|
112
|
+
NodeType,
|
|
113
|
+
parseEdgeId,
|
|
114
|
+
Provenance
|
|
115
|
+
} from "@neat.is/types";
|
|
116
|
+
function bucketKey(source, target, type) {
|
|
117
|
+
return `${type}|${source}|${target}`;
|
|
118
|
+
}
|
|
119
|
+
function bucketEdges(graph) {
|
|
120
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
121
|
+
graph.forEachEdge((id, attrs) => {
|
|
122
|
+
const e = attrs;
|
|
123
|
+
const parsed = parseEdgeId(id);
|
|
124
|
+
const provenance = parsed?.provenance ?? e.provenance;
|
|
125
|
+
const key = bucketKey(e.source, e.target, e.type);
|
|
126
|
+
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
127
|
+
switch (provenance) {
|
|
128
|
+
case Provenance.EXTRACTED:
|
|
129
|
+
cur.extracted = e;
|
|
130
|
+
break;
|
|
131
|
+
case Provenance.OBSERVED:
|
|
132
|
+
cur.observed = e;
|
|
133
|
+
break;
|
|
134
|
+
case Provenance.INFERRED:
|
|
135
|
+
cur.inferred = e;
|
|
136
|
+
break;
|
|
137
|
+
case Provenance.FRONTIER:
|
|
138
|
+
cur.frontier = e;
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
if (e.provenance === Provenance.STALE) cur.stale = e;
|
|
142
|
+
}
|
|
143
|
+
buckets.set(key, cur);
|
|
144
|
+
});
|
|
145
|
+
return buckets;
|
|
146
|
+
}
|
|
147
|
+
function nodeIsFrontier(graph, nodeId) {
|
|
148
|
+
if (!graph.hasNode(nodeId)) return false;
|
|
149
|
+
const attrs = graph.getNodeAttributes(nodeId);
|
|
150
|
+
return attrs.type === NodeType.FrontierNode;
|
|
151
|
+
}
|
|
152
|
+
function hasAnyObservedFromSource(graph, sourceId) {
|
|
153
|
+
if (!graph.hasNode(sourceId)) return false;
|
|
154
|
+
for (const edgeId of graph.outboundEdges(sourceId)) {
|
|
155
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
156
|
+
if (e.provenance === Provenance.OBSERVED) return true;
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
function clampConfidence(n) {
|
|
161
|
+
if (!Number.isFinite(n)) return 0;
|
|
162
|
+
return Math.max(0, Math.min(1, n));
|
|
163
|
+
}
|
|
164
|
+
function reasonForMissingObserved(source, target, type) {
|
|
165
|
+
return `Code declares ${source} \u2192 ${target} (${type}) but no production traffic has been observed for this edge.`;
|
|
166
|
+
}
|
|
167
|
+
function reasonForMissingExtracted(source, target, type) {
|
|
168
|
+
return `Production observed ${source} \u2192 ${target} (${type}) but static analysis did not surface this edge.`;
|
|
169
|
+
}
|
|
170
|
+
var RECOMMENDATION_MISSING_OBSERVED = "Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.";
|
|
171
|
+
var RECOMMENDATION_MISSING_EXTRACTED = "Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.";
|
|
172
|
+
var RECOMMENDATION_HOST_MISMATCH = "Check environment-specific config overrides \u2014 the runtime host differs from what static configuration declares.";
|
|
173
|
+
function detectMissingDivergences(graph, bucket) {
|
|
174
|
+
const out = [];
|
|
175
|
+
if (bucket.extracted && !bucket.observed) {
|
|
176
|
+
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
177
|
+
const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source);
|
|
178
|
+
out.push({
|
|
179
|
+
type: "missing-observed",
|
|
180
|
+
source: bucket.source,
|
|
181
|
+
target: bucket.target,
|
|
182
|
+
edgeType: bucket.type,
|
|
183
|
+
extracted: bucket.extracted,
|
|
184
|
+
confidence: sourceHasTraffic ? 1 : 0.5,
|
|
185
|
+
reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),
|
|
186
|
+
recommendation: RECOMMENDATION_MISSING_OBSERVED
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (bucket.observed && !bucket.extracted) {
|
|
191
|
+
const cascaded = clampConfidence(confidenceForEdge(bucket.observed));
|
|
192
|
+
out.push({
|
|
193
|
+
type: "missing-extracted",
|
|
194
|
+
source: bucket.source,
|
|
195
|
+
target: bucket.target,
|
|
196
|
+
edgeType: bucket.type,
|
|
197
|
+
observed: bucket.observed,
|
|
198
|
+
confidence: cascaded,
|
|
199
|
+
reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),
|
|
200
|
+
recommendation: RECOMMENDATION_MISSING_EXTRACTED
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function declaredHostFor(svc) {
|
|
206
|
+
const raw = svc.dbConnectionTarget?.trim();
|
|
207
|
+
if (!raw) return null;
|
|
208
|
+
const colon = raw.lastIndexOf(":");
|
|
209
|
+
if (colon === -1) return raw;
|
|
210
|
+
const port = raw.slice(colon + 1);
|
|
211
|
+
if (/^\d+$/.test(port)) return raw.slice(0, colon);
|
|
212
|
+
return raw;
|
|
213
|
+
}
|
|
214
|
+
function hasExtractedConfiguredBy(graph, svcId) {
|
|
215
|
+
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
216
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
217
|
+
if (e.type === EdgeType.CONFIGURED_BY && e.provenance === Provenance.EXTRACTED) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
function detectHostMismatch(graph, svcId, svc) {
|
|
224
|
+
const declaredHost = declaredHostFor(svc);
|
|
225
|
+
if (!declaredHost) return [];
|
|
226
|
+
if (!hasExtractedConfiguredBy(graph, svcId)) return [];
|
|
227
|
+
const out = [];
|
|
228
|
+
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
229
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
230
|
+
if (edge.type !== EdgeType.CONNECTS_TO) continue;
|
|
231
|
+
if (edge.provenance !== Provenance.OBSERVED) continue;
|
|
232
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
233
|
+
if (target.type !== NodeType.DatabaseNode) continue;
|
|
234
|
+
const observedHost = target.host?.trim();
|
|
235
|
+
if (!observedHost) continue;
|
|
236
|
+
if (observedHost === declaredHost) continue;
|
|
237
|
+
out.push({
|
|
238
|
+
type: "host-mismatch",
|
|
239
|
+
source: svcId,
|
|
240
|
+
target: edge.target,
|
|
241
|
+
extractedHost: declaredHost,
|
|
242
|
+
observedHost,
|
|
243
|
+
confidence: clampConfidence(confidenceForEdge(edge)),
|
|
244
|
+
reason: `Config declares ${svcId} connects to ${declaredHost}; production connects to ${observedHost}.`,
|
|
245
|
+
recommendation: RECOMMENDATION_HOST_MISMATCH
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
function detectCompatDivergences(graph, svcId, svc) {
|
|
251
|
+
const out = [];
|
|
252
|
+
const deps = svc.dependencies ?? {};
|
|
253
|
+
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
254
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
255
|
+
if (edge.type !== EdgeType.CONNECTS_TO) continue;
|
|
256
|
+
if (edge.provenance !== Provenance.OBSERVED) continue;
|
|
257
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
258
|
+
if (target.type !== NodeType.DatabaseNode) continue;
|
|
259
|
+
for (const pair of compatPairs()) {
|
|
260
|
+
if (pair.engine !== target.engine) continue;
|
|
261
|
+
const declared = deps[pair.driver];
|
|
262
|
+
if (!declared) continue;
|
|
263
|
+
const result = checkCompatibility(
|
|
264
|
+
pair.driver,
|
|
265
|
+
declared,
|
|
266
|
+
target.engine,
|
|
267
|
+
target.engineVersion
|
|
268
|
+
);
|
|
269
|
+
if (!result.compatible && result.reason) {
|
|
270
|
+
out.push({
|
|
271
|
+
type: "version-mismatch",
|
|
272
|
+
source: svcId,
|
|
273
|
+
target: edge.target,
|
|
274
|
+
extractedVersion: declared,
|
|
275
|
+
observedVersion: target.engineVersion,
|
|
276
|
+
compatibility: "incompatible",
|
|
277
|
+
confidence: 1,
|
|
278
|
+
reason: result.reason,
|
|
279
|
+
recommendation: result.minDriverVersion ? `Upgrade ${pair.driver} to >= ${result.minDriverVersion}.` : `Update the ${pair.driver} driver to a version compatible with ${target.engine} ${target.engineVersion}.`
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const rule of deprecatedApis()) {
|
|
284
|
+
const declared = deps[rule.package];
|
|
285
|
+
if (!declared) continue;
|
|
286
|
+
const result = checkDeprecatedApi(rule, declared);
|
|
287
|
+
if (!result.compatible && result.reason) {
|
|
288
|
+
const ruleRef = {
|
|
289
|
+
kind: rule.kind ?? "deprecated-api",
|
|
290
|
+
reason: result.reason,
|
|
291
|
+
package: rule.package
|
|
292
|
+
};
|
|
293
|
+
out.push({
|
|
294
|
+
type: "compat-violation",
|
|
295
|
+
source: svcId,
|
|
296
|
+
target: edge.target,
|
|
297
|
+
rule: ruleRef,
|
|
298
|
+
observed: edge,
|
|
299
|
+
confidence: 1,
|
|
300
|
+
reason: result.reason,
|
|
301
|
+
recommendation: `Replace deprecated ${rule.package}@${declared} with a supported version.`
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
function involvesNode(d, nodeId) {
|
|
309
|
+
return d.source === nodeId || d.target === nodeId;
|
|
310
|
+
}
|
|
311
|
+
function computeDivergences(graph, opts = {}) {
|
|
312
|
+
const all = [];
|
|
313
|
+
const buckets = bucketEdges(graph);
|
|
314
|
+
for (const bucket of buckets.values()) {
|
|
315
|
+
for (const d of detectMissingDivergences(graph, bucket)) all.push(d);
|
|
316
|
+
}
|
|
317
|
+
graph.forEachNode((nodeId, attrs) => {
|
|
318
|
+
const n = attrs;
|
|
319
|
+
if (n.type !== NodeType.ServiceNode) return;
|
|
320
|
+
const svc = n;
|
|
321
|
+
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
322
|
+
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
323
|
+
});
|
|
324
|
+
let filtered = all;
|
|
325
|
+
if (opts.type) {
|
|
326
|
+
const allowed = opts.type;
|
|
327
|
+
filtered = filtered.filter((d) => allowed.has(d.type));
|
|
328
|
+
}
|
|
329
|
+
if (opts.minConfidence !== void 0) {
|
|
330
|
+
const threshold = opts.minConfidence;
|
|
331
|
+
filtered = filtered.filter((d) => d.confidence >= threshold);
|
|
332
|
+
}
|
|
333
|
+
if (opts.node) {
|
|
334
|
+
const target = opts.node;
|
|
335
|
+
filtered = filtered.filter((d) => involvesNode(d, target));
|
|
336
|
+
}
|
|
337
|
+
filtered.sort((a, b) => {
|
|
338
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
339
|
+
if (a.type !== b.type) return a.type.localeCompare(b.type);
|
|
340
|
+
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
341
|
+
return a.target.localeCompare(b.target);
|
|
342
|
+
});
|
|
343
|
+
return DivergenceResultSchema.parse({
|
|
344
|
+
divergences: filtered,
|
|
345
|
+
totalAffected: filtered.length,
|
|
346
|
+
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
347
|
+
});
|
|
348
|
+
}
|
|
101
349
|
|
|
102
350
|
// src/streaming.ts
|
|
103
351
|
var SSE_HEARTBEAT_MS = 3e4;
|
|
@@ -209,9 +457,15 @@ function registerRoutes(scope, ctx) {
|
|
|
209
457
|
scope.get("/health", async (req, reply) => {
|
|
210
458
|
const proj = resolveProject(registry, req, reply);
|
|
211
459
|
if (!proj) return;
|
|
460
|
+
const uptimeMs = Date.now() - startedAt;
|
|
212
461
|
return {
|
|
213
|
-
|
|
462
|
+
ok: true,
|
|
214
463
|
project: proj.name,
|
|
464
|
+
uptimeMs,
|
|
465
|
+
// Legacy fields kept additively. The web shell's StatusBar reads
|
|
466
|
+
// nodeCount / edgeCount; ADR-061's HealthResponseSchema validates
|
|
467
|
+
// the canonical triple and lets the extras pass through.
|
|
468
|
+
uptime: Math.floor(uptimeMs / 1e3),
|
|
215
469
|
nodeCount: proj.graph.order,
|
|
216
470
|
edgeCount: proj.graph.size,
|
|
217
471
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -231,7 +485,7 @@ function registerRoutes(scope, ctx) {
|
|
|
231
485
|
if (!proj.graph.hasNode(id)) {
|
|
232
486
|
return reply.code(404).send({ error: "node not found", id });
|
|
233
487
|
}
|
|
234
|
-
return proj.graph.getNodeAttributes(id);
|
|
488
|
+
return { node: proj.graph.getNodeAttributes(id) };
|
|
235
489
|
}
|
|
236
490
|
);
|
|
237
491
|
scope.get(
|
|
@@ -248,12 +502,12 @@ function registerRoutes(scope, ctx) {
|
|
|
248
502
|
return { inbound, outbound };
|
|
249
503
|
}
|
|
250
504
|
);
|
|
251
|
-
scope.get("/graph/
|
|
505
|
+
scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
|
|
252
506
|
const proj = resolveProject(registry, req, reply);
|
|
253
507
|
if (!proj) return;
|
|
254
|
-
const {
|
|
255
|
-
if (!proj.graph.hasNode(
|
|
256
|
-
return reply.code(404).send({ error: "node not found", id });
|
|
508
|
+
const { nodeId } = req.params;
|
|
509
|
+
if (!proj.graph.hasNode(nodeId)) {
|
|
510
|
+
return reply.code(404).send({ error: "node not found", id: nodeId });
|
|
257
511
|
}
|
|
258
512
|
const depth = req.query.depth ? Number(req.query.depth) : TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH;
|
|
259
513
|
if (!Number.isFinite(depth) || depth < 1 || depth > TRANSITIVE_DEPENDENCIES_MAX_DEPTH) {
|
|
@@ -261,25 +515,67 @@ function registerRoutes(scope, ctx) {
|
|
|
261
515
|
error: `depth must be an integer in [1, ${TRANSITIVE_DEPENDENCIES_MAX_DEPTH}]`
|
|
262
516
|
});
|
|
263
517
|
}
|
|
264
|
-
return getTransitiveDependencies(proj.graph,
|
|
518
|
+
return getTransitiveDependencies(proj.graph, nodeId, depth);
|
|
519
|
+
});
|
|
520
|
+
scope.get("/graph/divergences", async (req, reply) => {
|
|
521
|
+
const proj = resolveProject(registry, req, reply);
|
|
522
|
+
if (!proj) return;
|
|
523
|
+
let typeFilter;
|
|
524
|
+
if (req.query.type) {
|
|
525
|
+
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
526
|
+
const parsed = [];
|
|
527
|
+
for (const c of candidates) {
|
|
528
|
+
const r = DivergenceTypeSchema.safeParse(c);
|
|
529
|
+
if (!r.success) {
|
|
530
|
+
return reply.code(400).send({
|
|
531
|
+
error: `unknown divergence type "${c}"`,
|
|
532
|
+
allowed: DivergenceTypeSchema.options
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
parsed.push(r.data);
|
|
536
|
+
}
|
|
537
|
+
typeFilter = new Set(parsed);
|
|
538
|
+
}
|
|
539
|
+
let minConfidence;
|
|
540
|
+
if (req.query.minConfidence !== void 0) {
|
|
541
|
+
const n = Number(req.query.minConfidence);
|
|
542
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
543
|
+
return reply.code(400).send({
|
|
544
|
+
error: "minConfidence must be a number in [0, 1]"
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
minConfidence = n;
|
|
548
|
+
}
|
|
549
|
+
return computeDivergences(proj.graph, {
|
|
550
|
+
...typeFilter ? { type: typeFilter } : {},
|
|
551
|
+
...minConfidence !== void 0 ? { minConfidence } : {},
|
|
552
|
+
...req.query.node ? { node: req.query.node } : {}
|
|
553
|
+
});
|
|
265
554
|
});
|
|
266
555
|
scope.get("/incidents", async (req, reply) => {
|
|
267
556
|
const proj = resolveProject(registry, req, reply);
|
|
268
557
|
if (!proj) return;
|
|
269
558
|
const epath = errorsPathFor(proj);
|
|
270
|
-
if (!epath) return [];
|
|
271
|
-
|
|
559
|
+
if (!epath) return { count: 0, total: 0, events: [] };
|
|
560
|
+
const events = await readErrorEvents(epath);
|
|
561
|
+
const total = events.length;
|
|
562
|
+
const limit = req.query.limit ? Number(req.query.limit) : 50;
|
|
563
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.min(limit, 200) : 50;
|
|
564
|
+
const sliced = events.slice(0, safeLimit);
|
|
565
|
+
return { count: sliced.length, total, events: sliced };
|
|
272
566
|
});
|
|
273
|
-
scope.get("/
|
|
567
|
+
scope.get("/stale-events", async (req, reply) => {
|
|
274
568
|
const proj = resolveProject(registry, req, reply);
|
|
275
569
|
if (!proj) return;
|
|
276
570
|
const spath = staleEventsPathFor(proj);
|
|
277
|
-
if (!spath) return [];
|
|
571
|
+
if (!spath) return { count: 0, total: 0, events: [] };
|
|
278
572
|
const events = await readStaleEvents(spath);
|
|
279
573
|
const filtered = req.query.edgeType ? events.filter((e) => e.edgeType === req.query.edgeType) : events;
|
|
280
574
|
const ordered = [...filtered].reverse();
|
|
575
|
+
const total = ordered.length;
|
|
281
576
|
const limit = req.query.limit ? Number(req.query.limit) : 50;
|
|
282
|
-
|
|
577
|
+
const sliced = ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50);
|
|
578
|
+
return { count: sliced.length, total, events: sliced };
|
|
283
579
|
});
|
|
284
580
|
scope.get(
|
|
285
581
|
"/incidents/:nodeId",
|
|
@@ -291,14 +587,15 @@ function registerRoutes(scope, ctx) {
|
|
|
291
587
|
return reply.code(404).send({ error: "node not found", id: nodeId });
|
|
292
588
|
}
|
|
293
589
|
const epath = errorsPathFor(proj);
|
|
294
|
-
if (!epath) return [];
|
|
590
|
+
if (!epath) return { count: 0, total: 0, events: [] };
|
|
295
591
|
const events = await readErrorEvents(epath);
|
|
296
|
-
|
|
592
|
+
const filtered = events.filter(
|
|
297
593
|
(e) => e.affectedNode === nodeId || e.service === nodeId.replace(/^service:/, "")
|
|
298
594
|
);
|
|
595
|
+
return { count: filtered.length, total: filtered.length, events: filtered };
|
|
299
596
|
}
|
|
300
597
|
);
|
|
301
|
-
scope.get("/
|
|
598
|
+
scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
|
|
302
599
|
const proj = resolveProject(registry, req, reply);
|
|
303
600
|
if (!proj) return;
|
|
304
601
|
const { nodeId } = req.params;
|
|
@@ -318,7 +615,7 @@ function registerRoutes(scope, ctx) {
|
|
|
318
615
|
if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
|
|
319
616
|
return result;
|
|
320
617
|
});
|
|
321
|
-
scope.get("/
|
|
618
|
+
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
322
619
|
const proj = resolveProject(registry, req, reply);
|
|
323
620
|
if (!proj) return;
|
|
324
621
|
const { nodeId } = req.params;
|
|
@@ -428,7 +725,7 @@ function registerRoutes(scope, ctx) {
|
|
|
428
725
|
if (req.query.policyId) {
|
|
429
726
|
violations = violations.filter((v) => v.policyId === req.query.policyId);
|
|
430
727
|
}
|
|
431
|
-
return violations;
|
|
728
|
+
return { violations };
|
|
432
729
|
});
|
|
433
730
|
scope.post("/policies/check", async (req, reply) => {
|
|
434
731
|
const proj = resolveProject(registry, req, reply);
|
|
@@ -507,6 +804,20 @@ async function buildApi(opts) {
|
|
|
507
804
|
});
|
|
508
805
|
}
|
|
509
806
|
});
|
|
807
|
+
app.get("/projects/:project", async (req, reply) => {
|
|
808
|
+
try {
|
|
809
|
+
const entry = await getProject(req.params.project);
|
|
810
|
+
if (!entry) {
|
|
811
|
+
return reply.code(404).send({ error: "project not found", project: req.params.project });
|
|
812
|
+
}
|
|
813
|
+
return { project: entry };
|
|
814
|
+
} catch (err) {
|
|
815
|
+
return reply.code(500).send({
|
|
816
|
+
error: "failed to read project registry",
|
|
817
|
+
details: err.message
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
});
|
|
510
821
|
registerRoutes(app, routeCtx);
|
|
511
822
|
await app.register(
|
|
512
823
|
async (scope) => {
|
|
@@ -522,4 +833,4 @@ export {
|
|
|
522
833
|
computeGraphDiff,
|
|
523
834
|
buildApi
|
|
524
835
|
};
|
|
525
|
-
//# sourceMappingURL=chunk-
|
|
836
|
+
//# sourceMappingURL=chunk-NVCEZXL7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/diff.ts","../src/api.ts","../src/divergences.ts","../src/streaming.ts"],"sourcesContent":["import { promises as fs } from 'node:fs'\nimport type { GraphEdge, GraphNode } from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\n\n// Diff a snapshot on disk (or fetched over HTTP) against the live in-memory\n// graph. The \"base\" is the snapshot you supplied; the \"current\" is whatever\n// the server has loaded right now. Symmetric in shape, but consumers usually\n// want to read it as \"what changed since base.\"\n//\n// The shape mirrors the issue's spec (#77):\n// { added: { nodes, edges }, removed: { nodes, edges },\n// changed: { nodes: [{id, before, after}], edges: [{id, before, after}] } }\n//\n// Both timestamps are echoed back so the caller doesn't have to track them\n// separately.\n\ninterface PersistedNodeEntry {\n key?: string\n attributes?: Record<string, unknown>\n}\n\ninterface PersistedEdgeEntry {\n key?: string\n source?: string\n target?: string\n attributes?: Record<string, unknown>\n}\n\nexport interface PersistedSnapshot {\n schemaVersion?: number\n exportedAt?: string\n graph?: {\n nodes?: PersistedNodeEntry[]\n edges?: PersistedEdgeEntry[]\n }\n}\n\nexport interface GraphDiff {\n base: { exportedAt?: string }\n current: { exportedAt: string }\n added: { nodes: GraphNode[]; edges: GraphEdge[] }\n removed: { nodes: GraphNode[]; edges: GraphEdge[] }\n changed: {\n nodes: { id: string; before: GraphNode; after: GraphNode }[]\n edges: { id: string; before: GraphEdge; after: GraphEdge }[]\n }\n}\n\nexport async function loadSnapshotForDiff(target: string): Promise<PersistedSnapshot> {\n if (/^https?:\\/\\//i.test(target)) {\n const res = await fetch(target)\n if (!res.ok) {\n throw new Error(`fetch ${target} failed: ${res.status} ${res.statusText}`)\n }\n return (await res.json()) as PersistedSnapshot\n }\n const raw = await fs.readFile(target, 'utf8')\n return JSON.parse(raw) as PersistedSnapshot\n}\n\nfunction indexEntries<T>(\n entries: { key?: string; attributes?: Record<string, unknown> }[] | undefined,\n): Map<string, T> {\n const m = new Map<string, T>()\n if (!entries) return m\n for (const entry of entries) {\n const id = (entry.attributes?.id as string | undefined) ?? entry.key\n if (!id) continue\n m.set(id, entry.attributes as T)\n }\n return m\n}\n\nexport function computeGraphDiff(\n liveGraph: NeatGraph,\n baseSnapshot: PersistedSnapshot,\n currentExportedAt: string = new Date().toISOString(),\n): GraphDiff {\n const baseNodes = indexEntries<GraphNode>(baseSnapshot.graph?.nodes)\n const baseEdges = indexEntries<GraphEdge>(baseSnapshot.graph?.edges)\n\n const liveNodes = new Map<string, GraphNode>()\n liveGraph.forEachNode((id, attrs) => liveNodes.set(id, attrs as GraphNode))\n const liveEdges = new Map<string, GraphEdge>()\n liveGraph.forEachEdge((id, attrs) => liveEdges.set(id, attrs as GraphEdge))\n\n const result: GraphDiff = {\n base: { exportedAt: baseSnapshot.exportedAt },\n current: { exportedAt: currentExportedAt },\n added: { nodes: [], edges: [] },\n removed: { nodes: [], edges: [] },\n changed: { nodes: [], edges: [] },\n }\n\n for (const [id, after] of liveNodes) {\n const before = baseNodes.get(id)\n if (!before) {\n result.added.nodes.push(after)\n } else if (!shallowEqual(before, after)) {\n result.changed.nodes.push({ id, before, after })\n }\n }\n for (const [id, before] of baseNodes) {\n if (!liveNodes.has(id)) result.removed.nodes.push(before)\n }\n for (const [id, after] of liveEdges) {\n const before = baseEdges.get(id)\n if (!before) {\n result.added.edges.push(after)\n } else if (!shallowEqual(before, after)) {\n result.changed.edges.push({ id, before, after })\n }\n }\n for (const [id, before] of baseEdges) {\n if (!liveEdges.has(id)) result.removed.edges.push(before)\n }\n\n return result\n}\n\n// Stable JSON comparison. Snapshot order isn't guaranteed, so canonicalising\n// keys before stringify keeps the comparison robust against re-ordered fields.\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n return canonicalJson(a) === canonicalJson(b)\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, (_key, v) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n return Object.keys(v as Record<string, unknown>)\n .sort()\n .reduce<Record<string, unknown>>((acc, k) => {\n acc[k] = (v as Record<string, unknown>)[k]\n return acc\n }, {})\n }\n return v\n })\n}\n","import Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n} from 'fastify'\nimport cors from '@fastify/cors'\nimport type {\n ErrorEvent,\n GraphEdge,\n GraphNode,\n Policy,\n PolicyViolation,\n} from '@neat.is/types'\nimport { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } from '@neat.is/types'\nimport type { DivergenceType } from '@neat.is/types'\nimport { computeDivergences } from './divergences.js'\nimport {\n evaluateAllPolicies,\n loadPolicyFile,\n PolicyViolationsLog,\n} from './policy.js'\nimport type { NeatGraph } from './graph.js'\nimport { DEFAULT_PROJECT } from './graph.js'\nimport { extractFromDirectory } from './extract.js'\nimport { readErrorEvents, readStaleEvents } from './ingest.js'\nimport {\n getBlastRadius,\n getRootCause,\n getTransitiveDependencies,\n TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH,\n TRANSITIVE_DEPENDENCIES_MAX_DEPTH,\n} from './traverse.js'\nimport { computeGraphDiff, loadSnapshotForDiff } from './diff.js'\nimport type { SearchIndex } from './search.js'\nimport type { Projects, ProjectContext } from './projects.js'\nimport { Projects as ProjectsClass, pathsForProject } from './projects.js'\nimport { getProject as getRegistryProject, listProjects as listRegistryProjects } from './registry.js'\nimport { handleSse } from './streaming.js'\n\nexport interface BuildApiOptions {\n // Multi-project shape. Optional — when absent we synthesise a single-\n // project registry from the legacy fields below so existing callers\n // (mainly tests) keep working unchanged.\n projects?: Projects\n startedAt?: number\n\n // Legacy single-project shape. Mapped to project=`default` if `projects`\n // isn't provided.\n graph?: NeatGraph\n scanPath?: string\n errorsPath?: string\n staleEventsPath?: string\n searchIndex?: SearchIndex\n}\n\ninterface SerializedGraph {\n nodes: GraphNode[]\n edges: GraphEdge[]\n}\n\nfunction serializeGraph(graph: NeatGraph): SerializedGraph {\n const nodes: GraphNode[] = []\n graph.forEachNode((_id, attrs) => {\n nodes.push(attrs)\n })\n const edges: GraphEdge[] = []\n graph.forEachEdge((_id, attrs) => {\n edges.push(attrs)\n })\n return { nodes, edges }\n}\n\nfunction projectFromReq(req: FastifyRequest): string {\n // `:project` is optional in the URL — the request hits either\n // /projects/:project/X or /X (which means default). Coerce the missing\n // param to DEFAULT_PROJECT here so handlers don't repeat the fallback.\n const params = req.params as { project?: string }\n return params.project ?? DEFAULT_PROJECT\n}\n\nfunction resolveProject(\n registry: Projects,\n req: FastifyRequest,\n reply: FastifyReply,\n): ProjectContext | null {\n const name = projectFromReq(req)\n const ctx = registry.get(name)\n if (!ctx) {\n void reply.code(404).send({ error: 'project not found', project: name })\n return null\n }\n return ctx\n}\n\nfunction buildLegacyRegistry(opts: BuildApiOptions): Projects {\n if (opts.projects) return opts.projects\n if (!opts.graph) {\n throw new Error('buildApi: either `projects` or `graph` must be provided')\n }\n const registry = new ProjectsClass()\n // pathsForProject only matters here for the snapshot/embeddings paths\n // routes never read; the ingest paths come from explicit options below.\n const paths = pathsForProject(DEFAULT_PROJECT, '')\n registry.set(DEFAULT_PROJECT, {\n graph: opts.graph,\n scanPath: opts.scanPath,\n paths: {\n snapshotPath: paths.snapshotPath,\n errorsPath: opts.errorsPath ?? paths.errorsPath,\n staleEventsPath: opts.staleEventsPath ?? paths.staleEventsPath,\n embeddingsCachePath: paths.embeddingsCachePath,\n policyViolationsPath: paths.policyViolationsPath,\n },\n searchIndex: opts.searchIndex,\n })\n return registry\n}\n\ninterface RouteContext {\n registry: Projects\n startedAt: number\n // Legacy callers passed `errorsPath`/`staleEventsPath` explicitly and\n // expected absent values to disable the read. Track that intent so the\n // /incidents handlers don't accidentally read a phantom file.\n errorsPathFor: (ctx: ProjectContext) => string | undefined\n staleEventsPathFor: (ctx: ProjectContext) => string | undefined\n // policy.json lives at the project root (per ADR-042 §File location), not\n // under neat-out/. Routes that read it map a project context to the path.\n policyFilePathFor: (ctx: ProjectContext) => string | undefined\n}\n\n// Registers every project-scoped route on `scope`. Called twice from\n// buildApi: once on the root app (so /graph etc. land at default), once\n// inside a `register(_, { prefix: '/projects/:project' })` plugin so the\n// same handlers run when the URL names a project explicitly.\nfunction registerRoutes(scope: FastifyInstance, ctx: RouteContext): void {\n const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx\n\n // SSE event stream (ADR-051 #1). Dual-mounted: hits /events for default\n // project and /projects/:project/events when scoped. The handler keeps\n // the connection open and writes one frame per bus envelope whose\n // `project` matches.\n scope.get<{ Params: { project?: string } }>('/events', (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n handleSse(req, reply, { project: proj.name })\n })\n\n scope.get<{ Params: { project?: string } }>('/health', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const uptimeMs = Date.now() - startedAt\n return {\n ok: true,\n project: proj.name,\n uptimeMs,\n // Legacy fields kept additively. The web shell's StatusBar reads\n // nodeCount / edgeCount; ADR-061's HealthResponseSchema validates\n // the canonical triple and lets the extras pass through.\n uptime: Math.floor(uptimeMs / 1000),\n nodeCount: proj.graph.order,\n edgeCount: proj.graph.size,\n lastUpdated: new Date().toISOString(),\n }\n })\n\n scope.get<{ Params: { project?: string } }>('/graph', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n return serializeGraph(proj.graph)\n })\n\n scope.get<{ Params: { project?: string; id: string } }>(\n '/graph/node/:id',\n async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const { id } = req.params\n if (!proj.graph.hasNode(id)) {\n return reply.code(404).send({ error: 'node not found', id })\n }\n return { node: proj.graph.getNodeAttributes(id) as GraphNode }\n },\n )\n\n scope.get<{ Params: { project?: string; id: string } }>(\n '/graph/edges/:id',\n async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const { id } = req.params\n if (!proj.graph.hasNode(id)) {\n return reply.code(404).send({ error: 'node not found', id })\n }\n const inbound = proj.graph\n .inboundEdges(id)\n .map((e) => proj.graph.getEdgeAttributes(e) as GraphEdge)\n const outbound = proj.graph\n .outboundEdges(id)\n .map((e) => proj.graph.getEdgeAttributes(e) as GraphEdge)\n return { inbound, outbound }\n },\n )\n\n // Transitive dependencies (issue #144). BFS outbound to depth N, returning\n // a flat list with distance + edgeType + provenance per dependency.\n // Default depth 3, max 10. The MCP get_dependencies tool calls this.\n scope.get<{\n Params: { project?: string; nodeId: string }\n Querystring: { depth?: string }\n }>('/graph/dependencies/:nodeId', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const { nodeId } = req.params\n if (!proj.graph.hasNode(nodeId)) {\n return reply.code(404).send({ error: 'node not found', id: nodeId })\n }\n const depth = req.query.depth ? Number(req.query.depth) : TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH\n if (!Number.isFinite(depth) || depth < 1 || depth > TRANSITIVE_DEPENDENCIES_MAX_DEPTH) {\n return reply.code(400).send({\n error: `depth must be an integer in [1, ${TRANSITIVE_DEPENDENCIES_MAX_DEPTH}]`,\n })\n }\n return getTransitiveDependencies(proj.graph, nodeId, depth)\n })\n\n // Divergence query — the thesis surface (ADR-060). Read-only, derived,\n // dual-mounted via registerRoutes. Query params filter the result set;\n // body shape is DivergenceResult.\n scope.get<{\n Params: { project?: string }\n Querystring: { type?: string; minConfidence?: string; node?: string }\n }>('/graph/divergences', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n let typeFilter: Set<DivergenceType> | undefined\n if (req.query.type) {\n const candidates = req.query.type\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n const parsed: DivergenceType[] = []\n for (const c of candidates) {\n const r = DivergenceTypeSchema.safeParse(c)\n if (!r.success) {\n return reply.code(400).send({\n error: `unknown divergence type \"${c}\"`,\n allowed: DivergenceTypeSchema.options,\n })\n }\n parsed.push(r.data)\n }\n typeFilter = new Set(parsed)\n }\n let minConfidence: number | undefined\n if (req.query.minConfidence !== undefined) {\n const n = Number(req.query.minConfidence)\n if (!Number.isFinite(n) || n < 0 || n > 1) {\n return reply.code(400).send({\n error: 'minConfidence must be a number in [0, 1]',\n })\n }\n minConfidence = n\n }\n return computeDivergences(proj.graph, {\n ...(typeFilter ? { type: typeFilter } : {}),\n ...(minConfidence !== undefined ? { minConfidence } : {}),\n ...(req.query.node ? { node: req.query.node } : {}),\n })\n })\n\n // ADR-061 envelope rule: list endpoints return { count, total, events }.\n // `total` is the size of the underlying collection; `count` is the size of\n // the slice we're handing back. The web shell counts on both.\n scope.get<{\n Params: { project?: string }\n Querystring: { limit?: string }\n }>('/incidents', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const epath = errorsPathFor(proj)\n if (!epath) return { count: 0, total: 0, events: [] }\n const events = await readErrorEvents(epath)\n const total = events.length\n const limit = req.query.limit ? Number(req.query.limit) : 50\n const safeLimit =\n Number.isFinite(limit) && limit > 0 ? Math.min(limit, 200) : 50\n const sliced = events.slice(0, safeLimit)\n return { count: sliced.length, total, events: sliced }\n })\n\n scope.get<{\n Params: { project?: string }\n Querystring: { limit?: string; edgeType?: string }\n }>('/stale-events', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const spath = staleEventsPathFor(proj)\n if (!spath) return { count: 0, total: 0, events: [] }\n const events = await readStaleEvents(spath)\n const filtered = req.query.edgeType\n ? events.filter((e) => e.edgeType === req.query.edgeType)\n : events\n const ordered = [...filtered].reverse()\n const total = ordered.length\n const limit = req.query.limit ? Number(req.query.limit) : 50\n const sliced = ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50)\n return { count: sliced.length, total, events: sliced }\n })\n\n scope.get<{ Params: { project?: string; nodeId: string } }>(\n '/incidents/:nodeId',\n async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const { nodeId } = req.params\n if (!proj.graph.hasNode(nodeId)) {\n return reply.code(404).send({ error: 'node not found', id: nodeId })\n }\n const epath = errorsPathFor(proj)\n if (!epath) return { count: 0, total: 0, events: [] }\n const events = await readErrorEvents(epath)\n const filtered = events.filter(\n (e) =>\n e.affectedNode === nodeId || e.service === nodeId.replace(/^service:/, ''),\n )\n return { count: filtered.length, total: filtered.length, events: filtered }\n },\n )\n\n scope.get<{\n Params: { project?: string; nodeId: string }\n Querystring: { errorId?: string }\n }>('/graph/root-cause/:nodeId', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const { nodeId } = req.params\n if (!proj.graph.hasNode(nodeId)) {\n return reply.code(404).send({ error: 'node not found', id: nodeId })\n }\n let errorEvent: ErrorEvent | undefined\n const epath = errorsPathFor(proj)\n if (req.query.errorId && epath) {\n const events = await readErrorEvents(epath)\n errorEvent = events.find((e) => e.id === req.query.errorId)\n if (!errorEvent) {\n return reply\n .code(404)\n .send({ error: 'error event not found', id: req.query.errorId })\n }\n }\n const result = getRootCause(proj.graph, nodeId, errorEvent)\n if (!result) return reply.code(404).send({ error: 'no root cause found', id: nodeId })\n return result\n })\n\n scope.get<{\n Params: { project?: string; nodeId: string }\n Querystring: { depth?: string }\n }>('/graph/blast-radius/:nodeId', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const { nodeId } = req.params\n if (!proj.graph.hasNode(nodeId)) {\n return reply.code(404).send({ error: 'node not found', id: nodeId })\n }\n const depth = req.query.depth ? Number(req.query.depth) : undefined\n if (depth !== undefined && (!Number.isFinite(depth) || depth < 0)) {\n return reply.code(400).send({ error: 'depth must be a non-negative number' })\n }\n return getBlastRadius(proj.graph, nodeId, depth)\n })\n\n scope.get<{\n Params: { project?: string }\n Querystring: { q?: string; limit?: string }\n }>('/search', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const raw = (req.query.q ?? '').trim()\n if (!raw) return reply.code(400).send({ error: 'query parameter `q` is required' })\n const limit = req.query.limit ? Number(req.query.limit) : undefined\n const safeLimit =\n limit !== undefined && Number.isFinite(limit) && limit > 0 ? limit : undefined\n if (proj.searchIndex) {\n const result = await proj.searchIndex.search(raw, safeLimit)\n return {\n query: result.query,\n provider: result.provider,\n matches: result.matches.map((m) => ({ ...m.node, score: m.score })),\n }\n }\n const q = raw.toLowerCase()\n const matches: (GraphNode & { score: number })[] = []\n proj.graph.forEachNode((id, attrs) => {\n const name = (attrs as { name?: string }).name ?? ''\n if (id.toLowerCase().includes(q) || name.toLowerCase().includes(q)) {\n matches.push({ ...(attrs as GraphNode), score: 1 })\n }\n })\n return {\n query: q,\n provider: 'substring' as const,\n matches: matches.slice(0, safeLimit),\n }\n })\n\n scope.get<{ Params: { project?: string }; Querystring: { against?: string } }>(\n '/graph/diff',\n async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const against = req.query.against\n if (!against) {\n return reply.code(400).send({ error: 'query parameter `against` is required' })\n }\n try {\n const snapshot = await loadSnapshotForDiff(against)\n return computeGraphDiff(proj.graph, snapshot)\n } catch (err) {\n return reply\n .code(400)\n .send({ error: 'failed to load snapshot', against, detail: (err as Error).message })\n }\n },\n )\n\n scope.post<{ Params: { project?: string } }>('/graph/scan', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n if (!proj.scanPath) {\n return reply\n .code(409)\n .send({ error: 'scan path not configured for this project', project: proj.name })\n }\n const result = await extractFromDirectory(proj.graph, proj.scanPath)\n return {\n project: proj.name,\n scanned: proj.scanPath,\n nodesAdded: result.nodesAdded,\n edgesAdded: result.edgesAdded,\n nodeCount: proj.graph.order,\n edgeCount: proj.graph.size,\n }\n })\n\n // Policy surface (ADR-045 / contract #18). /policies returns the parsed\n // policy.json; /policies/violations is the persistent log; /policies/check\n // is dry-run evaluation. All dual-mounted via registerRoutes.\n scope.get<{ Params: { project?: string } }>('/policies', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const policyPath = ctx.policyFilePathFor(proj)\n if (!policyPath) {\n // No policy file configured for this project — return the empty file\n // shape so consumers don't have to special-case \"no policies yet.\"\n return { version: 1, policies: [] }\n }\n try {\n const policies = await loadPolicyFile(policyPath)\n return { version: 1, policies }\n } catch (err) {\n return reply.code(400).send({\n error: 'policy.json failed to parse',\n details: (err as Error).message,\n })\n }\n })\n\n scope.get<{\n Params: { project?: string }\n Querystring: { severity?: string; policyId?: string }\n }>('/policies/violations', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const log = new PolicyViolationsLog(proj.paths.policyViolationsPath)\n let violations = await log.readAll()\n if (req.query.severity) {\n const sev = PolicySeveritySchema.safeParse(req.query.severity)\n if (!sev.success) {\n return reply.code(400).send({\n error: 'invalid severity',\n details: sev.error.format(),\n })\n }\n violations = violations.filter((v) => v.severity === sev.data)\n }\n if (req.query.policyId) {\n violations = violations.filter((v) => v.policyId === req.query.policyId)\n }\n return { violations }\n })\n\n scope.post<{\n Params: { project?: string }\n Body: { hypotheticalAction?: unknown }\n }>('/policies/check', async (req, reply) => {\n const proj = resolveProject(registry, req, reply)\n if (!proj) return\n const parsed = PoliciesCheckBodySchema.safeParse(req.body ?? {})\n if (!parsed.success) {\n return reply.code(400).send({\n error: 'invalid /policies/check body',\n details: parsed.error.format(),\n })\n }\n\n const policyPath = ctx.policyFilePathFor(proj)\n let policies: Policy[] = []\n if (policyPath) {\n try {\n policies = await loadPolicyFile(policyPath)\n } catch (err) {\n return reply.code(400).send({\n error: 'policy.json failed to parse',\n details: (err as Error).message,\n })\n }\n }\n\n // No hypothetical → return current violations against the live graph.\n // With a hypothetical → simulate the action against a deep-copy graph\n // (avoids mutation authority concerns), evaluate, return the delta.\n const evalCtx = { now: () => Date.now() }\n if (!parsed.data.hypotheticalAction) {\n const violations = evaluateAllPolicies(proj.graph, policies, evalCtx)\n const blocking = violations.filter((v) => v.onViolation === 'block')\n return { allowed: blocking.length === 0, violations }\n }\n\n // For now the dry-run simulation re-uses evaluateAllPolicies on the\n // current graph. Full hypothetical simulation (e.g. \"what if I added\n // this OBSERVED edge?\") is the v0.2.4-δ scope; #117 ships the surface,\n // #118 fills in the action shapes' simulation logic.\n const violations = evaluateAllPolicies(proj.graph, policies, evalCtx)\n const blocking = violations.filter((v) => v.onViolation === 'block')\n return {\n allowed: blocking.length === 0,\n hypotheticalAction: parsed.data.hypotheticalAction,\n violations,\n } as { allowed: boolean; hypotheticalAction: unknown; violations: PolicyViolation[] }\n })\n}\n\nexport async function buildApi(opts: BuildApiOptions): Promise<FastifyInstance> {\n const app = Fastify({ logger: false })\n await app.register(cors, { origin: true })\n\n const startedAt = opts.startedAt ?? Date.now()\n const registry = buildLegacyRegistry(opts)\n\n const legacyErrorsExplicit = !opts.projects && opts.errorsPath !== undefined\n const legacyStaleExplicit = !opts.projects && opts.staleEventsPath !== undefined\n\n const errorsPathFor = (proj: ProjectContext): string | undefined => {\n if (proj.name === DEFAULT_PROJECT && !opts.projects) {\n return legacyErrorsExplicit ? opts.errorsPath : undefined\n }\n return proj.paths.errorsPath\n }\n const staleEventsPathFor = (proj: ProjectContext): string | undefined => {\n if (proj.name === DEFAULT_PROJECT && !opts.projects) {\n return legacyStaleExplicit ? opts.staleEventsPath : undefined\n }\n return proj.paths.staleEventsPath\n }\n\n // policy.json lives at the project's scanPath root per ADR-042. Without a\n // scanPath we have nowhere to read it from — those projects show as\n // \"no policies configured\" via the empty-file response.\n const policyFilePathFor = (proj: ProjectContext): string | undefined => {\n if (!proj.scanPath) return undefined\n return `${proj.scanPath}/policy.json`\n }\n\n const routeCtx: RouteContext = {\n registry,\n startedAt,\n errorsPathFor,\n staleEventsPathFor,\n policyFilePathFor,\n }\n\n // Multi-project switcher (ADR-051 #4). Direct passthrough of the\n // machine-level registry from registry.ts (ADR-048) — distinct from the\n // dual-mount routing in ADR-026, which exposes per-project endpoints.\n // Returns Array<{ name, path, status, registeredAt, lastSeenAt?, languages }>.\n app.get('/projects', async (_req, reply) => {\n try {\n return await listRegistryProjects()\n } catch (err) {\n return reply.code(500).send({\n error: 'failed to read project registry',\n details: (err as Error).message,\n })\n }\n })\n\n // Singular project lookup (ADR-061 #7). Distinct route from the\n // `/projects/:project/...` dual-mount prefix because Fastify matches on\n // the full path; the trailing-segment-less request lands here.\n app.get<{ Params: { project: string } }>('/projects/:project', async (req, reply) => {\n try {\n const entry = await getRegistryProject(req.params.project)\n if (!entry) {\n return reply\n .code(404)\n .send({ error: 'project not found', project: req.params.project })\n }\n return { project: entry }\n } catch (err) {\n return reply.code(500).send({\n error: 'failed to read project registry',\n details: (err as Error).message,\n })\n }\n })\n\n // Default mount: /health, /graph, /incidents, etc. all hit project=default.\n registerRoutes(app, routeCtx)\n\n // Project-scoped mount: same handlers, URL params include `:project`.\n await app.register(\n async (scope) => {\n registerRoutes(scope, routeCtx)\n },\n { prefix: '/projects/:project' },\n )\n\n return app\n}\n","// computeDivergences — the thesis surface, derived (ADR-060).\n//\n// Walks the live graph and surfaces the five locked divergence shapes:\n// missing-observed, missing-extracted, version-mismatch, host-mismatch,\n// and compat-violation. Pure: no I/O, no mutation, no async. The function\n// operates on a NeatGraph reference and returns a fresh DivergenceResult\n// each call — there is no persistence (binding rule 2).\n//\n// Mutation authority (ADR-030 / contract #3) is locked to ingest.ts and\n// extract/*; this module reads only. The contract test\n// `packages/core/test/audits/contracts.test.ts` enforces it.\n\nimport type {\n CompatRuleRef,\n Divergence,\n DivergenceResult,\n DivergenceType,\n GraphEdge,\n GraphNode,\n ServiceNode,\n} from '@neat.is/types'\nimport {\n DivergenceResultSchema,\n EdgeType,\n NodeType,\n parseEdgeId,\n Provenance,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport {\n checkCompatibility,\n checkDeprecatedApi,\n compatPairs,\n deprecatedApis,\n} from './compat.js'\nimport { confidenceForEdge } from './traverse.js'\n\nexport interface DivergenceQueryOpts {\n // Filter the result to a subset of divergence types. Undefined keeps all\n // five. Empty set returns nothing.\n type?: ReadonlySet<DivergenceType>\n // Drop divergences below this confidence threshold. Undefined keeps all.\n minConfidence?: number\n // Scope to divergences that involve this node (as source or target).\n node?: string\n}\n\n// (source, target, type) → which provenance variants are present. Each\n// bucket is the unit the missing-observed / missing-extracted detectors\n// operate over.\ninterface EdgeBucket {\n source: string\n target: string\n type: GraphEdge['type']\n extracted?: GraphEdge\n observed?: GraphEdge\n inferred?: GraphEdge\n stale?: GraphEdge\n frontier?: GraphEdge\n}\n\nfunction bucketKey(source: string, target: string, type: string): string {\n return `${type}|${source}|${target}`\n}\n\nfunction bucketEdges(graph: NeatGraph): Map<string, EdgeBucket> {\n const buckets = new Map<string, EdgeBucket>()\n graph.forEachEdge((id, attrs) => {\n const e = attrs as GraphEdge\n const parsed = parseEdgeId(id)\n // parseEdgeId can fall through to EXTRACTED for unknown shapes — fall\n // back to the edge's own provenance when the id doesn't parse cleanly.\n const provenance = parsed?.provenance ?? e.provenance\n const key = bucketKey(e.source, e.target, e.type)\n const cur =\n buckets.get(key) ?? { source: e.source, target: e.target, type: e.type }\n switch (provenance) {\n case Provenance.EXTRACTED:\n cur.extracted = e\n break\n case Provenance.OBSERVED:\n cur.observed = e\n break\n case Provenance.INFERRED:\n cur.inferred = e\n break\n case Provenance.FRONTIER:\n cur.frontier = e\n break\n default:\n // STALE rides on what used to be an OBSERVED edge — the id format\n // stays OBSERVED per identity.ts, so this branch is mostly defensive.\n if (e.provenance === Provenance.STALE) cur.stale = e\n }\n buckets.set(key, cur)\n })\n return buckets\n}\n\nfunction nodeIsFrontier(graph: NeatGraph, nodeId: string): boolean {\n if (!graph.hasNode(nodeId)) return false\n const attrs = graph.getNodeAttributes(nodeId) as GraphNode\n return attrs.type === NodeType.FrontierNode\n}\n\nfunction hasAnyObservedFromSource(graph: NeatGraph, sourceId: string): boolean {\n if (!graph.hasNode(sourceId)) return false\n for (const edgeId of graph.outboundEdges(sourceId)) {\n const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (e.provenance === Provenance.OBSERVED) return true\n }\n return false\n}\n\nfunction clampConfidence(n: number): number {\n if (!Number.isFinite(n)) return 0\n return Math.max(0, Math.min(1, n))\n}\n\nfunction reasonForMissingObserved(source: string, target: string, type: string): string {\n return `Code declares ${source} → ${target} (${type}) but no production traffic has been observed for this edge.`\n}\n\nfunction reasonForMissingExtracted(source: string, target: string, type: string): string {\n return `Production observed ${source} → ${target} (${type}) but static analysis did not surface this edge.`\n}\n\nconst RECOMMENDATION_MISSING_OBSERVED =\n 'Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.'\nconst RECOMMENDATION_MISSING_EXTRACTED =\n 'Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.'\nconst RECOMMENDATION_HOST_MISMATCH =\n 'Check environment-specific config overrides — the runtime host differs from what static configuration declares.'\n\nfunction detectMissingDivergences(\n graph: NeatGraph,\n bucket: EdgeBucket,\n): Divergence[] {\n const out: Divergence[] = []\n\n if (bucket.extracted && !bucket.observed) {\n // Skip when the would-be target is a FrontierNode — those represent\n // unresolved span peers, not real entities we expect OBSERVED traffic\n // to. The coexistence contract is between EXTRACTED and OBSERVED on\n // real nodes; FRONTIER is unknown territory.\n if (!nodeIsFrontier(graph, bucket.target)) {\n const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source)\n out.push({\n type: 'missing-observed',\n source: bucket.source,\n target: bucket.target,\n edgeType: bucket.type,\n extracted: bucket.extracted,\n confidence: sourceHasTraffic ? 1.0 : 0.5,\n reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),\n recommendation: RECOMMENDATION_MISSING_OBSERVED,\n })\n }\n }\n\n if (bucket.observed && !bucket.extracted) {\n const cascaded = clampConfidence(confidenceForEdge(bucket.observed))\n out.push({\n type: 'missing-extracted',\n source: bucket.source,\n target: bucket.target,\n edgeType: bucket.type,\n observed: bucket.observed,\n confidence: cascaded,\n reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),\n recommendation: RECOMMENDATION_MISSING_EXTRACTED,\n })\n }\n\n return out\n}\n\n// Returns the declared host of the service's static DB target, when\n// recoverable. ServiceNode.dbConnectionTarget is the static-extraction\n// surface for \"this service connects to X\" — `X` is host[:port] or a\n// docker-compose-style service name. Empty / undefined means we have no\n// EXTRACTED host to compare against and host-mismatch can't fire.\nfunction declaredHostFor(svc: ServiceNode): string | null {\n const raw = svc.dbConnectionTarget?.trim()\n if (!raw) return null\n // Strip a trailing port if present so it lines up with DatabaseNode.host\n // (ADR-028 §6 — DatabaseNode id excludes port).\n const colon = raw.lastIndexOf(':')\n if (colon === -1) return raw\n const port = raw.slice(colon + 1)\n if (/^\\d+$/.test(port)) return raw.slice(0, colon)\n return raw\n}\n\nfunction hasExtractedConfiguredBy(graph: NeatGraph, svcId: string): boolean {\n for (const edgeId of graph.outboundEdges(svcId)) {\n const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (e.type === EdgeType.CONFIGURED_BY && e.provenance === Provenance.EXTRACTED) {\n return true\n }\n }\n return false\n}\n\nfunction detectHostMismatch(\n graph: NeatGraph,\n svcId: string,\n svc: ServiceNode,\n): Divergence[] {\n const declaredHost = declaredHostFor(svc)\n if (!declaredHost) return []\n if (!hasExtractedConfiguredBy(graph, svcId)) return []\n\n const out: Divergence[] = []\n for (const edgeId of graph.outboundEdges(svcId)) {\n const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (edge.type !== EdgeType.CONNECTS_TO) continue\n if (edge.provenance !== Provenance.OBSERVED) continue\n const target = graph.getNodeAttributes(edge.target) as GraphNode\n if (target.type !== NodeType.DatabaseNode) continue\n const observedHost = target.host?.trim()\n if (!observedHost) continue\n if (observedHost === declaredHost) continue\n\n out.push({\n type: 'host-mismatch',\n source: svcId,\n target: edge.target,\n extractedHost: declaredHost,\n observedHost,\n confidence: clampConfidence(confidenceForEdge(edge)),\n reason: `Config declares ${svcId} connects to ${declaredHost}; production connects to ${observedHost}.`,\n recommendation: RECOMMENDATION_HOST_MISMATCH,\n })\n }\n return out\n}\n\nfunction detectCompatDivergences(\n graph: NeatGraph,\n svcId: string,\n svc: ServiceNode,\n): Divergence[] {\n const out: Divergence[] = []\n const deps = svc.dependencies ?? {}\n\n for (const edgeId of graph.outboundEdges(svcId)) {\n const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (edge.type !== EdgeType.CONNECTS_TO) continue\n if (edge.provenance !== Provenance.OBSERVED) continue\n const target = graph.getNodeAttributes(edge.target) as GraphNode\n if (target.type !== NodeType.DatabaseNode) continue\n\n // Driver-engine compat. Definitive — when a rule fires it's a\n // version-mismatch with confidence 1.0.\n for (const pair of compatPairs()) {\n if (pair.engine !== target.engine) continue\n const declared = deps[pair.driver]\n if (!declared) continue\n const result = checkCompatibility(\n pair.driver,\n declared,\n target.engine,\n target.engineVersion,\n )\n if (!result.compatible && result.reason) {\n out.push({\n type: 'version-mismatch',\n source: svcId,\n target: edge.target,\n extractedVersion: declared,\n observedVersion: target.engineVersion,\n compatibility: 'incompatible',\n confidence: 1.0,\n reason: result.reason,\n recommendation: result.minDriverVersion\n ? `Upgrade ${pair.driver} to >= ${result.minDriverVersion}.`\n : `Update the ${pair.driver} driver to a version compatible with ${target.engine} ${target.engineVersion}.`,\n })\n }\n }\n\n // Deprecated-api compat. Broader than version-mismatch — surfaces as\n // compat-violation. Driver-engine rules above already covered the\n // \"version is too low\" shape; deprecated covers \"version is too high\n // / no longer supported.\"\n for (const rule of deprecatedApis()) {\n const declared = deps[rule.package]\n if (!declared) continue\n const result = checkDeprecatedApi(rule, declared)\n if (!result.compatible && result.reason) {\n const ruleRef: CompatRuleRef = {\n kind: rule.kind ?? 'deprecated-api',\n reason: result.reason,\n package: rule.package,\n }\n out.push({\n type: 'compat-violation',\n source: svcId,\n target: edge.target,\n rule: ruleRef,\n observed: edge,\n confidence: 1.0,\n reason: result.reason,\n recommendation: `Replace deprecated ${rule.package}@${declared} with a supported version.`,\n })\n }\n }\n }\n return out\n}\n\nfunction involvesNode(d: Divergence, nodeId: string): boolean {\n return d.source === nodeId || d.target === nodeId\n}\n\nexport function computeDivergences(\n graph: NeatGraph,\n opts: DivergenceQueryOpts = {},\n): DivergenceResult {\n const all: Divergence[] = []\n\n // Pass 1 — bucket every edge and emit missing-observed / missing-extracted.\n const buckets = bucketEdges(graph)\n for (const bucket of buckets.values()) {\n for (const d of detectMissingDivergences(graph, bucket)) all.push(d)\n }\n\n // Pass 2 — per-service host + compat rules.\n graph.forEachNode((nodeId, attrs) => {\n const n = attrs as GraphNode\n if (n.type !== NodeType.ServiceNode) return\n const svc = n as ServiceNode\n for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d)\n for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d)\n })\n\n // Filter + sort. Higher confidence first; within the same confidence,\n // stable on (type, source, target) so callers see deterministic output.\n let filtered = all\n if (opts.type) {\n const allowed = opts.type\n filtered = filtered.filter((d) => allowed.has(d.type))\n }\n if (opts.minConfidence !== undefined) {\n const threshold = opts.minConfidence\n filtered = filtered.filter((d) => d.confidence >= threshold)\n }\n if (opts.node) {\n const target = opts.node\n filtered = filtered.filter((d) => involvesNode(d, target))\n }\n\n filtered.sort((a, b) => {\n if (b.confidence !== a.confidence) return b.confidence - a.confidence\n if (a.type !== b.type) return a.type.localeCompare(b.type)\n if (a.source !== b.source) return a.source.localeCompare(b.source)\n return a.target.localeCompare(b.target)\n })\n\n return DivergenceResultSchema.parse({\n divergences: filtered,\n totalAffected: filtered.length,\n computedAt: new Date().toISOString(),\n })\n}\n","// SSE handler for the frontend-facing event stream (ADR-051 #1).\n// Subscribes to the bus in events.ts, filters by project, writes\n// `event: <type>\\ndata: <json>\\n\\n` frames to the client.\n//\n// Backpressure: per-connection queue cap of 1000 outstanding writes; once\n// hit, the connection is dropped with `event: error data: { reason:\n// 'backpressure' }` per ADR-051 #8. Heartbeat: comment line every 30s\n// keeps proxies from idle-timing out (ADR-051 #3).\n\nimport type { FastifyReply, FastifyRequest } from 'fastify'\nimport {\n EVENT_BUS_CHANNEL,\n eventBus,\n type NeatEventEnvelope,\n} from './events.js'\n\nexport const SSE_HEARTBEAT_MS = 30_000\nexport const SSE_BACKPRESSURE_CAP = 1000\n\nexport interface HandleSseOptions {\n project: string\n heartbeatMs?: number\n backpressureCap?: number\n}\n\nexport function handleSse(\n req: FastifyRequest,\n reply: FastifyReply,\n opts: HandleSseOptions,\n): void {\n const heartbeatMs = opts.heartbeatMs ?? SSE_HEARTBEAT_MS\n const backpressureCap = opts.backpressureCap ?? SSE_BACKPRESSURE_CAP\n\n reply.raw.setHeader('Content-Type', 'text/event-stream')\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform')\n reply.raw.setHeader('Connection', 'keep-alive')\n reply.raw.setHeader('X-Accel-Buffering', 'no')\n reply.raw.flushHeaders?.()\n\n let pending = 0\n let dropped = false\n\n const closeConnection = (): void => {\n if (dropped) return\n dropped = true\n eventBus.off(EVENT_BUS_CHANNEL, listener)\n clearInterval(heartbeat)\n if (!reply.raw.writableEnded) reply.raw.end()\n }\n\n const writeFrame = (frame: string): void => {\n if (dropped) return\n if (pending >= backpressureCap) {\n // Past the cap — emit one final error frame and drop. Don't try to\n // gracefully drain; a slow consumer that's already 1000 frames behind\n // is not going to catch up.\n const errFrame = `event: error\\ndata: ${JSON.stringify({ reason: 'backpressure' })}\\n\\n`\n reply.raw.write(errFrame)\n closeConnection()\n return\n }\n pending++\n reply.raw.write(frame, () => {\n pending = Math.max(0, pending - 1)\n })\n }\n\n const listener = (envelope: NeatEventEnvelope): void => {\n if (envelope.project !== opts.project) return\n writeFrame(`event: ${envelope.type}\\ndata: ${JSON.stringify(envelope.payload)}\\n\\n`)\n }\n\n eventBus.on(EVENT_BUS_CHANNEL, listener)\n\n const heartbeat = setInterval(() => {\n if (dropped) return\n reply.raw.write(':heartbeat\\n\\n')\n }, heartbeatMs)\n if (typeof heartbeat.unref === 'function') heartbeat.unref()\n\n req.raw.on('close', closeConnection)\n reply.raw.on('close', closeConnection)\n reply.raw.on('error', closeConnection)\n}\n"],"mappings":"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","names":["violations","blocking"]}
|
|
@@ -115,7 +115,7 @@ function loadProtobufDecoder() {
|
|
|
115
115
|
async function decodeProtobufBody(buf) {
|
|
116
116
|
const Type = loadProtobufDecoder();
|
|
117
117
|
const decoded = Type.decode(buf).toJSON();
|
|
118
|
-
const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await import("./otel-grpc-
|
|
118
|
+
const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await import("./otel-grpc-USFL3OI3.js");
|
|
119
119
|
return reshapeGrpcRequest2(decoded);
|
|
120
120
|
}
|
|
121
121
|
async function buildOtelReceiver(opts) {
|
|
@@ -322,4 +322,4 @@ export {
|
|
|
322
322
|
buildOtelReceiver,
|
|
323
323
|
logSpanHandler
|
|
324
324
|
};
|
|
325
|
-
//# sourceMappingURL=chunk-
|
|
325
|
+
//# sourceMappingURL=chunk-QYUB3FQL.js.map
|