@neat.is/core 0.4.26-dev.20260703 → 0.4.27-dev.20260705
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-2LNICOVU.js → chunk-A3322JYS.js} +29 -2
- package/dist/chunk-A3322JYS.js.map +1 -0
- package/dist/{chunk-4OR4RQEO.js → chunk-BIY46Q6U.js} +13 -1
- package/dist/chunk-BIY46Q6U.js.map +1 -0
- package/dist/{chunk-WZYH5DVG.js → chunk-QM6BMPVJ.js} +419 -218
- package/dist/chunk-QM6BMPVJ.js.map +1 -0
- package/dist/{chunk-C5NCCKPZ.js → chunk-UV5WSM7M.js} +2 -2
- package/dist/{chunk-S7TDPQDD.js → chunk-XV4D7A3Z.js} +105 -6
- package/dist/chunk-XV4D7A3Z.js.map +1 -0
- package/dist/cli.cjs +732 -456
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +49 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +587 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -1
- package/dist/index.d.ts +97 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +600 -286
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-LFYZDSK3.js → otel-grpc-ET5Z6KI6.js} +3 -3
- package/dist/server.cjs +410 -182
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -4
- package/package.json +4 -2
- package/dist/chunk-2LNICOVU.js.map +0 -1
- package/dist/chunk-4OR4RQEO.js.map +0 -1
- package/dist/chunk-S7TDPQDD.js.map +0 -1
- package/dist/chunk-WZYH5DVG.js.map +0 -1
- /package/dist/{chunk-C5NCCKPZ.js.map → chunk-UV5WSM7M.js.map} +0 -0
- /package/dist/{otel-grpc-LFYZDSK3.js.map → otel-grpc-ET5Z6KI6.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -57,9 +57,9 @@ function mountBearerAuth(app, opts) {
|
|
|
57
57
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
58
58
|
const publicRead = opts.publicRead === true;
|
|
59
59
|
app.addHook("preHandler", (req, reply, done) => {
|
|
60
|
-
const
|
|
60
|
+
const path46 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
61
61
|
for (const suffix of suffixes) {
|
|
62
|
-
if (
|
|
62
|
+
if (path46 === suffix || path46.endsWith(suffix)) {
|
|
63
63
|
done();
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
@@ -189,8 +189,8 @@ function reshapeGrpcRequest(req) {
|
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
191
|
function resolveProtoRoot() {
|
|
192
|
-
const here =
|
|
193
|
-
return
|
|
192
|
+
const here = import_node_path42.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
193
|
+
return import_node_path42.default.resolve(here, "..", "proto");
|
|
194
194
|
}
|
|
195
195
|
function loadTraceService() {
|
|
196
196
|
const protoRoot = resolveProtoRoot();
|
|
@@ -258,13 +258,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
258
258
|
})
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
|
-
var import_node_url,
|
|
261
|
+
var import_node_url, import_node_path42, import_node_crypto2, grpc, protoLoader;
|
|
262
262
|
var init_otel_grpc = __esm({
|
|
263
263
|
"src/otel-grpc.ts"() {
|
|
264
264
|
"use strict";
|
|
265
265
|
init_cjs_shims();
|
|
266
266
|
import_node_url = require("url");
|
|
267
|
-
|
|
267
|
+
import_node_path42 = __toESM(require("path"), 1);
|
|
268
268
|
import_node_crypto2 = require("crypto");
|
|
269
269
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
270
270
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -344,6 +344,29 @@ function messagingDestinationOf(attrs) {
|
|
|
344
344
|
}
|
|
345
345
|
return void 0;
|
|
346
346
|
}
|
|
347
|
+
function hasWebsocketUpgradeHeader(attrs) {
|
|
348
|
+
const v = attrs["http.request.header.upgrade"];
|
|
349
|
+
const matches = (s) => typeof s === "string" && s.trim().toLowerCase() === "websocket";
|
|
350
|
+
if (Array.isArray(v)) return v.some(matches);
|
|
351
|
+
return matches(v);
|
|
352
|
+
}
|
|
353
|
+
function websocketChannelPathOf(attrs) {
|
|
354
|
+
const route = attrs["http.route"];
|
|
355
|
+
if (typeof route === "string" && route.length > 0) return route;
|
|
356
|
+
for (const key of ["url.path", "http.target"]) {
|
|
357
|
+
const v = attrs[key];
|
|
358
|
+
if (typeof v === "string" && v.length > 0) {
|
|
359
|
+
const q = v.indexOf("?");
|
|
360
|
+
const path46 = q === -1 ? v : v.slice(0, q);
|
|
361
|
+
if (path46.length > 0) return path46;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
function websocketChannelOf(attrs) {
|
|
367
|
+
if (!hasWebsocketUpgradeHeader(attrs)) return void 0;
|
|
368
|
+
return websocketChannelPathOf(attrs);
|
|
369
|
+
}
|
|
347
370
|
function parseOtlpRequest(body) {
|
|
348
371
|
const out = [];
|
|
349
372
|
for (const rs of body.resourceSpans ?? []) {
|
|
@@ -374,6 +397,10 @@ function parseOtlpRequest(body) {
|
|
|
374
397
|
messagingDestination: messagingDestinationOf(attrs),
|
|
375
398
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
376
399
|
graphqlOperationType: typeof attrs["graphql.operation.type"] === "string" && attrs["graphql.operation.type"].length > 0 ? attrs["graphql.operation.type"] : void 0,
|
|
400
|
+
rpcSystem: typeof attrs["rpc.system"] === "string" && attrs["rpc.system"].length > 0 ? attrs["rpc.system"] : void 0,
|
|
401
|
+
rpcService: typeof attrs["rpc.service"] === "string" && attrs["rpc.service"].length > 0 ? attrs["rpc.service"] : void 0,
|
|
402
|
+
rpcMethod: typeof attrs["rpc.method"] === "string" && attrs["rpc.method"].length > 0 ? attrs["rpc.method"] : void 0,
|
|
403
|
+
websocketChannel: websocketChannelOf(attrs),
|
|
377
404
|
statusCode: span.status?.code,
|
|
378
405
|
errorMessage: span.status?.message,
|
|
379
406
|
exception: extractExceptionFromEvents(span.events)
|
|
@@ -385,10 +412,10 @@ function parseOtlpRequest(body) {
|
|
|
385
412
|
return out;
|
|
386
413
|
}
|
|
387
414
|
function loadProtoRoot() {
|
|
388
|
-
const here =
|
|
389
|
-
const protoRoot =
|
|
415
|
+
const here = import_node_path43.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
416
|
+
const protoRoot = import_node_path43.default.resolve(here, "..", "proto");
|
|
390
417
|
const root = new import_protobufjs.default.Root();
|
|
391
|
-
root.resolvePath = (_origin, target) =>
|
|
418
|
+
root.resolvePath = (_origin, target) => import_node_path43.default.resolve(protoRoot, target);
|
|
392
419
|
root.loadSync(
|
|
393
420
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
394
421
|
{ keepCase: true }
|
|
@@ -624,12 +651,12 @@ function logSpanHandler(span) {
|
|
|
624
651
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
625
652
|
);
|
|
626
653
|
}
|
|
627
|
-
var
|
|
654
|
+
var import_node_path43, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
628
655
|
var init_otel = __esm({
|
|
629
656
|
"src/otel.ts"() {
|
|
630
657
|
"use strict";
|
|
631
658
|
init_cjs_shims();
|
|
632
|
-
|
|
659
|
+
import_node_path43 = __toESM(require("path"), 1);
|
|
633
660
|
import_node_url2 = require("url");
|
|
634
661
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
635
662
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -1219,19 +1246,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1219
1246
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1220
1247
|
let best = { path: [start], edges: [] };
|
|
1221
1248
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1222
|
-
function step(node,
|
|
1223
|
-
if (
|
|
1224
|
-
best = { path: [...
|
|
1249
|
+
function step(node, path46, edges) {
|
|
1250
|
+
if (path46.length > best.path.length) {
|
|
1251
|
+
best = { path: [...path46], edges: [...edges] };
|
|
1225
1252
|
}
|
|
1226
|
-
if (
|
|
1253
|
+
if (path46.length - 1 >= maxDepth) return;
|
|
1227
1254
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1228
1255
|
for (const [srcId, edge] of incoming) {
|
|
1229
1256
|
if (visited.has(srcId)) continue;
|
|
1230
1257
|
visited.add(srcId);
|
|
1231
|
-
|
|
1258
|
+
path46.push(srcId);
|
|
1232
1259
|
edges.push(edge);
|
|
1233
|
-
step(srcId,
|
|
1234
|
-
|
|
1260
|
+
step(srcId, path46, edges);
|
|
1261
|
+
path46.pop();
|
|
1235
1262
|
edges.pop();
|
|
1236
1263
|
visited.delete(srcId);
|
|
1237
1264
|
}
|
|
@@ -1425,26 +1452,26 @@ function dominantFailingCall(graph, serviceId4, visited) {
|
|
|
1425
1452
|
return best;
|
|
1426
1453
|
}
|
|
1427
1454
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1428
|
-
const
|
|
1455
|
+
const path46 = [originServiceId];
|
|
1429
1456
|
const edges = [];
|
|
1430
1457
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1431
1458
|
let current = originServiceId;
|
|
1432
1459
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1433
1460
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1434
1461
|
if (!hop) break;
|
|
1435
|
-
|
|
1462
|
+
path46.push(hop.nextService);
|
|
1436
1463
|
edges.push(hop.edge);
|
|
1437
1464
|
visited.add(hop.nextService);
|
|
1438
1465
|
current = hop.nextService;
|
|
1439
1466
|
}
|
|
1440
1467
|
if (edges.length === 0) return null;
|
|
1441
|
-
return { path:
|
|
1468
|
+
return { path: path46, edges, culprit: current };
|
|
1442
1469
|
}
|
|
1443
1470
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1444
1471
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1445
1472
|
if (!chain) return null;
|
|
1446
1473
|
const culprit = chain.culprit;
|
|
1447
|
-
const
|
|
1474
|
+
const path46 = [...chain.path];
|
|
1448
1475
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1449
1476
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1450
1477
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1452,14 +1479,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1452
1479
|
if (loc) {
|
|
1453
1480
|
let rootCauseNode = culprit;
|
|
1454
1481
|
if (loc.fileNode) {
|
|
1455
|
-
|
|
1482
|
+
path46.push(loc.fileNode);
|
|
1456
1483
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1457
1484
|
rootCauseNode = loc.fileNode;
|
|
1458
1485
|
}
|
|
1459
1486
|
return import_types.RootCauseResultSchema.parse({
|
|
1460
1487
|
rootCauseNode,
|
|
1461
1488
|
rootCauseReason: loc.rootCauseReason,
|
|
1462
|
-
traversalPath:
|
|
1489
|
+
traversalPath: path46,
|
|
1463
1490
|
edgeProvenances,
|
|
1464
1491
|
confidence,
|
|
1465
1492
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1471,7 +1498,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1471
1498
|
return import_types.RootCauseResultSchema.parse({
|
|
1472
1499
|
rootCauseNode: culprit,
|
|
1473
1500
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1474
|
-
traversalPath:
|
|
1501
|
+
traversalPath: path46,
|
|
1475
1502
|
edgeProvenances,
|
|
1476
1503
|
confidence,
|
|
1477
1504
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2421,6 +2448,40 @@ function ensureGraphqlOperationNode(graph, serviceName, operationType, operation
|
|
|
2421
2448
|
graph.addNode(id, node);
|
|
2422
2449
|
return id;
|
|
2423
2450
|
}
|
|
2451
|
+
function spanServesGrpcMethod(kind) {
|
|
2452
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2453
|
+
}
|
|
2454
|
+
function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
|
|
2455
|
+
const id = (0, import_types3.grpcMethodId)(rpcService, rpcMethod);
|
|
2456
|
+
if (graph.hasNode(id)) return id;
|
|
2457
|
+
const node = {
|
|
2458
|
+
id,
|
|
2459
|
+
type: import_types3.NodeType.GrpcMethodNode,
|
|
2460
|
+
name: `${rpcService}/${rpcMethod}`,
|
|
2461
|
+
rpcService,
|
|
2462
|
+
rpcMethod,
|
|
2463
|
+
discoveredVia: "otel"
|
|
2464
|
+
};
|
|
2465
|
+
graph.addNode(id, node);
|
|
2466
|
+
return id;
|
|
2467
|
+
}
|
|
2468
|
+
function spanServesWebsocketChannel(kind) {
|
|
2469
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2470
|
+
}
|
|
2471
|
+
function ensureWebsocketChannelNode(graph, serviceName, channel) {
|
|
2472
|
+
const id = (0, import_types3.websocketChannelId)(serviceName, channel);
|
|
2473
|
+
if (graph.hasNode(id)) return id;
|
|
2474
|
+
const node = {
|
|
2475
|
+
id,
|
|
2476
|
+
type: import_types3.NodeType.WebSocketChannelNode,
|
|
2477
|
+
name: channel,
|
|
2478
|
+
service: serviceName,
|
|
2479
|
+
channel,
|
|
2480
|
+
discoveredVia: "otel"
|
|
2481
|
+
};
|
|
2482
|
+
graph.addNode(id, node);
|
|
2483
|
+
return id;
|
|
2484
|
+
}
|
|
2424
2485
|
function messagingDestinationKind(system) {
|
|
2425
2486
|
return `${system}-topic`;
|
|
2426
2487
|
}
|
|
@@ -2856,6 +2917,34 @@ async function handleSpan(ctx, span) {
|
|
|
2856
2917
|
callSiteEvidence
|
|
2857
2918
|
);
|
|
2858
2919
|
if (result) affectedNode = targetId;
|
|
2920
|
+
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
2921
|
+
const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
|
|
2922
|
+
const result = upsertObservedEdge(
|
|
2923
|
+
ctx.graph,
|
|
2924
|
+
import_types3.EdgeType.CONTAINS,
|
|
2925
|
+
observedSource(),
|
|
2926
|
+
targetId,
|
|
2927
|
+
ts,
|
|
2928
|
+
isError,
|
|
2929
|
+
callSiteEvidence
|
|
2930
|
+
);
|
|
2931
|
+
if (result) affectedNode = targetId;
|
|
2932
|
+
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
2933
|
+
const targetId = ensureWebsocketChannelNode(
|
|
2934
|
+
ctx.graph,
|
|
2935
|
+
span.service,
|
|
2936
|
+
span.websocketChannel
|
|
2937
|
+
);
|
|
2938
|
+
const result = upsertObservedEdge(
|
|
2939
|
+
ctx.graph,
|
|
2940
|
+
import_types3.EdgeType.CONNECTS_TO,
|
|
2941
|
+
observedSource(),
|
|
2942
|
+
targetId,
|
|
2943
|
+
ts,
|
|
2944
|
+
isError,
|
|
2945
|
+
callSiteEvidence
|
|
2946
|
+
);
|
|
2947
|
+
if (result) affectedNode = targetId;
|
|
2859
2948
|
} else {
|
|
2860
2949
|
const host = pickAddress(span);
|
|
2861
2950
|
let resolvedViaAddress = false;
|
|
@@ -5426,17 +5515,138 @@ async function addRoutes(graph, services) {
|
|
|
5426
5515
|
return { nodesAdded, edgesAdded };
|
|
5427
5516
|
}
|
|
5428
5517
|
|
|
5518
|
+
// src/extract/proto.ts
|
|
5519
|
+
init_cjs_shims();
|
|
5520
|
+
var import_node_fs15 = require("fs");
|
|
5521
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
5522
|
+
var import_types12 = require("@neat.is/types");
|
|
5523
|
+
var PROTO_EXTENSION = ".proto";
|
|
5524
|
+
function packageOf(content) {
|
|
5525
|
+
const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
|
|
5526
|
+
return m ? m[1] : null;
|
|
5527
|
+
}
|
|
5528
|
+
function lineAt(content, offset) {
|
|
5529
|
+
return content.slice(0, offset).split("\n").length;
|
|
5530
|
+
}
|
|
5531
|
+
function grpcMethodsFromProto(content, fqPackage) {
|
|
5532
|
+
const out = [];
|
|
5533
|
+
const serviceRe = /\bservice\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/g;
|
|
5534
|
+
let sm;
|
|
5535
|
+
while ((sm = serviceRe.exec(content)) !== null) {
|
|
5536
|
+
const serviceName = sm[1];
|
|
5537
|
+
const rpcService = fqPackage ? `${fqPackage}.${serviceName}` : serviceName;
|
|
5538
|
+
const bodyStart = serviceRe.lastIndex;
|
|
5539
|
+
let depth = 1;
|
|
5540
|
+
let i = bodyStart;
|
|
5541
|
+
for (; i < content.length && depth > 0; i++) {
|
|
5542
|
+
const ch = content[i];
|
|
5543
|
+
if (ch === "{") depth++;
|
|
5544
|
+
else if (ch === "}") depth--;
|
|
5545
|
+
}
|
|
5546
|
+
const body = content.slice(bodyStart, i - 1);
|
|
5547
|
+
const rpcRe = /\brpc\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
5548
|
+
let rm;
|
|
5549
|
+
while ((rm = rpcRe.exec(body)) !== null) {
|
|
5550
|
+
const rpcMethod = rm[1];
|
|
5551
|
+
const line = lineAt(content, bodyStart + rm.index);
|
|
5552
|
+
out.push({ rpcService, rpcMethod, line });
|
|
5553
|
+
}
|
|
5554
|
+
serviceRe.lastIndex = i;
|
|
5555
|
+
}
|
|
5556
|
+
return out;
|
|
5557
|
+
}
|
|
5558
|
+
async function walkProtoFiles(dir) {
|
|
5559
|
+
const out = [];
|
|
5560
|
+
async function walk3(current) {
|
|
5561
|
+
const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
5562
|
+
for (const entry of entries) {
|
|
5563
|
+
const full = import_node_path23.default.join(current, entry.name);
|
|
5564
|
+
if (entry.isDirectory()) {
|
|
5565
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
5566
|
+
if (await isPythonVenvDir(full)) continue;
|
|
5567
|
+
await walk3(full);
|
|
5568
|
+
} else if (entry.isFile() && import_node_path23.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
5569
|
+
out.push(full);
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
await walk3(dir);
|
|
5574
|
+
return out;
|
|
5575
|
+
}
|
|
5576
|
+
async function addGrpcMethods(graph, services) {
|
|
5577
|
+
let nodesAdded = 0;
|
|
5578
|
+
let edgesAdded = 0;
|
|
5579
|
+
for (const service of services) {
|
|
5580
|
+
const protoPaths = await walkProtoFiles(service.dir);
|
|
5581
|
+
for (const protoPath of protoPaths) {
|
|
5582
|
+
if (isTestPath(protoPath)) continue;
|
|
5583
|
+
const relFile = toPosix2(import_node_path23.default.relative(service.dir, protoPath));
|
|
5584
|
+
let content;
|
|
5585
|
+
try {
|
|
5586
|
+
content = await import_node_fs15.promises.readFile(protoPath, "utf8");
|
|
5587
|
+
} catch (err) {
|
|
5588
|
+
recordExtractionError("proto extraction", protoPath, err);
|
|
5589
|
+
continue;
|
|
5590
|
+
}
|
|
5591
|
+
let methods;
|
|
5592
|
+
try {
|
|
5593
|
+
methods = grpcMethodsFromProto(content, packageOf(content));
|
|
5594
|
+
} catch (err) {
|
|
5595
|
+
recordExtractionError("proto extraction", protoPath, err);
|
|
5596
|
+
continue;
|
|
5597
|
+
}
|
|
5598
|
+
if (methods.length === 0) continue;
|
|
5599
|
+
for (const method of methods) {
|
|
5600
|
+
const mid = (0, import_types12.grpcMethodId)(method.rpcService, method.rpcMethod);
|
|
5601
|
+
if (!graph.hasNode(mid)) {
|
|
5602
|
+
const node = {
|
|
5603
|
+
id: mid,
|
|
5604
|
+
type: import_types12.NodeType.GrpcMethodNode,
|
|
5605
|
+
name: `${method.rpcService}/${method.rpcMethod}`,
|
|
5606
|
+
rpcService: method.rpcService,
|
|
5607
|
+
rpcMethod: method.rpcMethod,
|
|
5608
|
+
path: relFile,
|
|
5609
|
+
line: method.line,
|
|
5610
|
+
discoveredVia: "static"
|
|
5611
|
+
};
|
|
5612
|
+
graph.addNode(mid, node);
|
|
5613
|
+
nodesAdded++;
|
|
5614
|
+
}
|
|
5615
|
+
const containsId = (0, import_types12.extractedEdgeId)(service.node.id, mid, import_types12.EdgeType.CONTAINS);
|
|
5616
|
+
if (!graph.hasEdge(containsId)) {
|
|
5617
|
+
const edge = {
|
|
5618
|
+
id: containsId,
|
|
5619
|
+
source: service.node.id,
|
|
5620
|
+
target: mid,
|
|
5621
|
+
type: import_types12.EdgeType.CONTAINS,
|
|
5622
|
+
provenance: import_types12.Provenance.EXTRACTED,
|
|
5623
|
+
confidence: (0, import_types12.confidenceForExtracted)("structural"),
|
|
5624
|
+
evidence: {
|
|
5625
|
+
file: relFile,
|
|
5626
|
+
line: method.line,
|
|
5627
|
+
snippet: snippet(content, method.line)
|
|
5628
|
+
}
|
|
5629
|
+
};
|
|
5630
|
+
graph.addEdgeWithKey(containsId, service.node.id, mid, edge);
|
|
5631
|
+
edgesAdded++;
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
return { nodesAdded, edgesAdded };
|
|
5637
|
+
}
|
|
5638
|
+
|
|
5429
5639
|
// src/extract/calls/index.ts
|
|
5430
5640
|
init_cjs_shims();
|
|
5431
|
-
var
|
|
5641
|
+
var import_types20 = require("@neat.is/types");
|
|
5432
5642
|
|
|
5433
5643
|
// src/extract/calls/http.ts
|
|
5434
5644
|
init_cjs_shims();
|
|
5435
|
-
var
|
|
5645
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
5436
5646
|
var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
5437
5647
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5438
5648
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
5439
|
-
var
|
|
5649
|
+
var import_types13 = require("@neat.is/types");
|
|
5440
5650
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
5441
5651
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
5442
5652
|
function isInsideJsxExternalLink(node) {
|
|
@@ -5506,7 +5716,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5506
5716
|
const seen = /* @__PURE__ */ new Set();
|
|
5507
5717
|
for (const file of files) {
|
|
5508
5718
|
if (isTestPath(file.path)) continue;
|
|
5509
|
-
const parser =
|
|
5719
|
+
const parser = import_node_path24.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
5510
5720
|
let sites;
|
|
5511
5721
|
try {
|
|
5512
5722
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -5515,14 +5725,14 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5515
5725
|
continue;
|
|
5516
5726
|
}
|
|
5517
5727
|
if (sites.length === 0) continue;
|
|
5518
|
-
const relFile = toPosix2(
|
|
5728
|
+
const relFile = toPosix2(import_node_path24.default.relative(service.dir, file.path));
|
|
5519
5729
|
for (const site of sites) {
|
|
5520
5730
|
const targetId = hostToNodeId.get(site.host);
|
|
5521
5731
|
if (!targetId || targetId === service.node.id) continue;
|
|
5522
5732
|
const dedupKey = `${relFile}|${targetId}`;
|
|
5523
5733
|
if (seen.has(dedupKey)) continue;
|
|
5524
5734
|
seen.add(dedupKey);
|
|
5525
|
-
const confidence = (0,
|
|
5735
|
+
const confidence = (0, import_types13.confidenceForExtracted)("url-literal-service-target");
|
|
5526
5736
|
const ev = {
|
|
5527
5737
|
file: relFile,
|
|
5528
5738
|
line: site.line,
|
|
@@ -5536,25 +5746,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5536
5746
|
);
|
|
5537
5747
|
nodesAdded += n;
|
|
5538
5748
|
edgesAdded += e;
|
|
5539
|
-
if (!(0,
|
|
5749
|
+
if (!(0, import_types13.passesExtractedFloor)(confidence)) {
|
|
5540
5750
|
noteExtractedDropped({
|
|
5541
5751
|
source: fileNodeId,
|
|
5542
5752
|
target: targetId,
|
|
5543
|
-
type:
|
|
5753
|
+
type: import_types13.EdgeType.CALLS,
|
|
5544
5754
|
confidence,
|
|
5545
5755
|
confidenceKind: "url-literal-service-target",
|
|
5546
5756
|
evidence: ev
|
|
5547
5757
|
});
|
|
5548
5758
|
continue;
|
|
5549
5759
|
}
|
|
5550
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId,
|
|
5760
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types13.EdgeType.CALLS);
|
|
5551
5761
|
if (!graph.hasEdge(edgeId)) {
|
|
5552
5762
|
const edge = {
|
|
5553
5763
|
id: edgeId,
|
|
5554
5764
|
source: fileNodeId,
|
|
5555
5765
|
target: targetId,
|
|
5556
|
-
type:
|
|
5557
|
-
provenance:
|
|
5766
|
+
type: import_types13.EdgeType.CALLS,
|
|
5767
|
+
provenance: import_types13.Provenance.EXTRACTED,
|
|
5558
5768
|
confidence,
|
|
5559
5769
|
evidence: ev
|
|
5560
5770
|
};
|
|
@@ -5569,10 +5779,10 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5569
5779
|
|
|
5570
5780
|
// src/extract/calls/route-match.ts
|
|
5571
5781
|
init_cjs_shims();
|
|
5572
|
-
var
|
|
5782
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
5573
5783
|
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
5574
5784
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5575
|
-
var
|
|
5785
|
+
var import_types14 = require("@neat.is/types");
|
|
5576
5786
|
var PARSE_CHUNK4 = 16384;
|
|
5577
5787
|
function parseSource4(parser, source) {
|
|
5578
5788
|
return parser.parse(
|
|
@@ -5737,9 +5947,9 @@ function buildRouteIndex(graph) {
|
|
|
5737
5947
|
const index = /* @__PURE__ */ new Map();
|
|
5738
5948
|
graph.forEachNode((_id, attrs) => {
|
|
5739
5949
|
const node = attrs;
|
|
5740
|
-
if (node.type !==
|
|
5950
|
+
if (node.type !== import_types14.NodeType.RouteNode) return;
|
|
5741
5951
|
const route = attrs;
|
|
5742
|
-
const owner = (0,
|
|
5952
|
+
const owner = (0, import_types14.serviceId)(route.service);
|
|
5743
5953
|
const entry = {
|
|
5744
5954
|
method: route.method.toUpperCase(),
|
|
5745
5955
|
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
@@ -5768,7 +5978,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5768
5978
|
const seen = /* @__PURE__ */ new Set();
|
|
5769
5979
|
for (const file of files) {
|
|
5770
5980
|
if (isTestPath(file.path)) continue;
|
|
5771
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
5981
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path25.default.extname(file.path))) continue;
|
|
5772
5982
|
let sites;
|
|
5773
5983
|
try {
|
|
5774
5984
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -5777,7 +5987,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5777
5987
|
continue;
|
|
5778
5988
|
}
|
|
5779
5989
|
if (sites.length === 0) continue;
|
|
5780
|
-
const relFile = toPosix2(
|
|
5990
|
+
const relFile = toPosix2(import_node_path25.default.relative(service.dir, file.path));
|
|
5781
5991
|
for (const site of sites) {
|
|
5782
5992
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
5783
5993
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -5797,7 +6007,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5797
6007
|
);
|
|
5798
6008
|
nodesAdded += n;
|
|
5799
6009
|
edgesAdded += e;
|
|
5800
|
-
const confidence = (0,
|
|
6010
|
+
const confidence = (0, import_types14.confidenceForExtracted)("verified-call-site");
|
|
5801
6011
|
const ev = {
|
|
5802
6012
|
file: relFile,
|
|
5803
6013
|
line: site.line,
|
|
@@ -5805,25 +6015,25 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5805
6015
|
method: site.method ?? match.method,
|
|
5806
6016
|
pathTemplate: site.pathTemplate
|
|
5807
6017
|
};
|
|
5808
|
-
if (!(0,
|
|
6018
|
+
if (!(0, import_types14.passesExtractedFloor)(confidence)) {
|
|
5809
6019
|
noteExtractedDropped({
|
|
5810
6020
|
source: fileNodeId,
|
|
5811
6021
|
target: match.routeNodeId,
|
|
5812
|
-
type:
|
|
6022
|
+
type: import_types14.EdgeType.CALLS,
|
|
5813
6023
|
confidence,
|
|
5814
6024
|
confidenceKind: "verified-call-site",
|
|
5815
6025
|
evidence: ev
|
|
5816
6026
|
});
|
|
5817
6027
|
continue;
|
|
5818
6028
|
}
|
|
5819
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, match.routeNodeId,
|
|
6029
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types14.EdgeType.CALLS);
|
|
5820
6030
|
if (!graph.hasEdge(edgeId)) {
|
|
5821
6031
|
const edge = {
|
|
5822
6032
|
id: edgeId,
|
|
5823
6033
|
source: fileNodeId,
|
|
5824
6034
|
target: match.routeNodeId,
|
|
5825
|
-
type:
|
|
5826
|
-
provenance:
|
|
6035
|
+
type: import_types14.EdgeType.CALLS,
|
|
6036
|
+
provenance: import_types14.Provenance.EXTRACTED,
|
|
5827
6037
|
confidence,
|
|
5828
6038
|
evidence: ev
|
|
5829
6039
|
};
|
|
@@ -5838,8 +6048,8 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5838
6048
|
|
|
5839
6049
|
// src/extract/calls/kafka.ts
|
|
5840
6050
|
init_cjs_shims();
|
|
5841
|
-
var
|
|
5842
|
-
var
|
|
6051
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
6052
|
+
var import_types15 = require("@neat.is/types");
|
|
5843
6053
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5844
6054
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
5845
6055
|
function findAll(re, text) {
|
|
@@ -5860,7 +6070,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5860
6070
|
seen.add(key);
|
|
5861
6071
|
const line = lineOf(file.content, topic);
|
|
5862
6072
|
out.push({
|
|
5863
|
-
infraId: (0,
|
|
6073
|
+
infraId: (0, import_types15.infraId)("kafka-topic", topic),
|
|
5864
6074
|
name: topic,
|
|
5865
6075
|
kind: "kafka-topic",
|
|
5866
6076
|
edgeType,
|
|
@@ -5869,7 +6079,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5869
6079
|
// tier (ADR-066).
|
|
5870
6080
|
confidenceKind: "verified-call-site",
|
|
5871
6081
|
evidence: {
|
|
5872
|
-
file:
|
|
6082
|
+
file: import_node_path26.default.relative(serviceDir, file.path),
|
|
5873
6083
|
line,
|
|
5874
6084
|
snippet: snippet(file.content, line)
|
|
5875
6085
|
}
|
|
@@ -5882,8 +6092,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5882
6092
|
|
|
5883
6093
|
// src/extract/calls/redis.ts
|
|
5884
6094
|
init_cjs_shims();
|
|
5885
|
-
var
|
|
5886
|
-
var
|
|
6095
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
6096
|
+
var import_types16 = require("@neat.is/types");
|
|
5887
6097
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
5888
6098
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
5889
6099
|
const out = [];
|
|
@@ -5896,7 +6106,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5896
6106
|
seen.add(host);
|
|
5897
6107
|
const line = lineOf(file.content, host);
|
|
5898
6108
|
out.push({
|
|
5899
|
-
infraId: (0,
|
|
6109
|
+
infraId: (0, import_types16.infraId)("redis", host),
|
|
5900
6110
|
name: host,
|
|
5901
6111
|
kind: "redis",
|
|
5902
6112
|
edgeType: "CALLS",
|
|
@@ -5905,7 +6115,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5905
6115
|
// support tier (ADR-066).
|
|
5906
6116
|
confidenceKind: "url-with-structural-support",
|
|
5907
6117
|
evidence: {
|
|
5908
|
-
file:
|
|
6118
|
+
file: import_node_path27.default.relative(serviceDir, file.path),
|
|
5909
6119
|
line,
|
|
5910
6120
|
snippet: snippet(file.content, line)
|
|
5911
6121
|
}
|
|
@@ -5916,8 +6126,8 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5916
6126
|
|
|
5917
6127
|
// src/extract/calls/aws.ts
|
|
5918
6128
|
init_cjs_shims();
|
|
5919
|
-
var
|
|
5920
|
-
var
|
|
6129
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
6130
|
+
var import_types17 = require("@neat.is/types");
|
|
5921
6131
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5922
6132
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5923
6133
|
function hasMarker(text, markers) {
|
|
@@ -5941,7 +6151,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5941
6151
|
seen.add(key);
|
|
5942
6152
|
const line = lineOf(file.content, name);
|
|
5943
6153
|
out.push({
|
|
5944
|
-
infraId: (0,
|
|
6154
|
+
infraId: (0, import_types17.infraId)(kind, name),
|
|
5945
6155
|
name,
|
|
5946
6156
|
kind,
|
|
5947
6157
|
edgeType: "CALLS",
|
|
@@ -5950,7 +6160,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5950
6160
|
// (ADR-066).
|
|
5951
6161
|
confidenceKind: "verified-call-site",
|
|
5952
6162
|
evidence: {
|
|
5953
|
-
file:
|
|
6163
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
5954
6164
|
line,
|
|
5955
6165
|
snippet: snippet(file.content, line)
|
|
5956
6166
|
}
|
|
@@ -5975,8 +6185,8 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5975
6185
|
|
|
5976
6186
|
// src/extract/calls/grpc.ts
|
|
5977
6187
|
init_cjs_shims();
|
|
5978
|
-
var
|
|
5979
|
-
var
|
|
6188
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
6189
|
+
var import_types18 = require("@neat.is/types");
|
|
5980
6190
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
5981
6191
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
5982
6192
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -6025,7 +6235,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6025
6235
|
const { kind } = classified;
|
|
6026
6236
|
const line = lineOf(file.content, m[0]);
|
|
6027
6237
|
out.push({
|
|
6028
|
-
infraId: (0,
|
|
6238
|
+
infraId: (0, import_types18.infraId)(kind, name),
|
|
6029
6239
|
name,
|
|
6030
6240
|
kind,
|
|
6031
6241
|
edgeType: "CALLS",
|
|
@@ -6034,7 +6244,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6034
6244
|
// tier (ADR-066).
|
|
6035
6245
|
confidenceKind: "verified-call-site",
|
|
6036
6246
|
evidence: {
|
|
6037
|
-
file:
|
|
6247
|
+
file: import_node_path29.default.relative(serviceDir, file.path),
|
|
6038
6248
|
line,
|
|
6039
6249
|
snippet: snippet(file.content, line)
|
|
6040
6250
|
}
|
|
@@ -6045,8 +6255,8 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6045
6255
|
|
|
6046
6256
|
// src/extract/calls/supabase.ts
|
|
6047
6257
|
init_cjs_shims();
|
|
6048
|
-
var
|
|
6049
|
-
var
|
|
6258
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
6259
|
+
var import_types19 = require("@neat.is/types");
|
|
6050
6260
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
6051
6261
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
6052
6262
|
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
@@ -6084,7 +6294,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6084
6294
|
seen.add(name);
|
|
6085
6295
|
const line = lineOf(file.content, m[0]);
|
|
6086
6296
|
out.push({
|
|
6087
|
-
infraId: (0,
|
|
6297
|
+
infraId: (0, import_types19.infraId)("supabase", name),
|
|
6088
6298
|
name,
|
|
6089
6299
|
kind: "supabase",
|
|
6090
6300
|
edgeType: "CALLS",
|
|
@@ -6094,7 +6304,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6094
6304
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
6095
6305
|
confidenceKind: "verified-call-site",
|
|
6096
6306
|
evidence: {
|
|
6097
|
-
file:
|
|
6307
|
+
file: import_node_path30.default.relative(serviceDir, file.path),
|
|
6098
6308
|
line,
|
|
6099
6309
|
snippet: snippet(file.content, line)
|
|
6100
6310
|
}
|
|
@@ -6107,11 +6317,11 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6107
6317
|
function edgeTypeFromEndpoint(ep) {
|
|
6108
6318
|
switch (ep.edgeType) {
|
|
6109
6319
|
case "PUBLISHES_TO":
|
|
6110
|
-
return
|
|
6320
|
+
return import_types20.EdgeType.PUBLISHES_TO;
|
|
6111
6321
|
case "CONSUMES_FROM":
|
|
6112
|
-
return
|
|
6322
|
+
return import_types20.EdgeType.CONSUMES_FROM;
|
|
6113
6323
|
default:
|
|
6114
|
-
return
|
|
6324
|
+
return import_types20.EdgeType.CALLS;
|
|
6115
6325
|
}
|
|
6116
6326
|
}
|
|
6117
6327
|
function isAwsKind(kind) {
|
|
@@ -6139,7 +6349,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6139
6349
|
if (!graph.hasNode(ep.infraId)) {
|
|
6140
6350
|
const node = {
|
|
6141
6351
|
id: ep.infraId,
|
|
6142
|
-
type:
|
|
6352
|
+
type: import_types20.NodeType.InfraNode,
|
|
6143
6353
|
name: ep.name,
|
|
6144
6354
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6145
6355
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6151,7 +6361,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6151
6361
|
nodesAdded++;
|
|
6152
6362
|
}
|
|
6153
6363
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6154
|
-
const confidence = (0,
|
|
6364
|
+
const confidence = (0, import_types20.confidenceForExtracted)(ep.confidenceKind);
|
|
6155
6365
|
const relFile = toPosix2(ep.evidence.file);
|
|
6156
6366
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6157
6367
|
graph,
|
|
@@ -6161,7 +6371,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6161
6371
|
);
|
|
6162
6372
|
nodesAdded += n;
|
|
6163
6373
|
edgesAdded += e;
|
|
6164
|
-
if (!(0,
|
|
6374
|
+
if (!(0, import_types20.passesExtractedFloor)(confidence)) {
|
|
6165
6375
|
noteExtractedDropped({
|
|
6166
6376
|
source: fileNodeId,
|
|
6167
6377
|
target: ep.infraId,
|
|
@@ -6181,7 +6391,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6181
6391
|
source: fileNodeId,
|
|
6182
6392
|
target: ep.infraId,
|
|
6183
6393
|
type: edgeType,
|
|
6184
|
-
provenance:
|
|
6394
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
6185
6395
|
confidence,
|
|
6186
6396
|
evidence: ep.evidence
|
|
6187
6397
|
};
|
|
@@ -6207,16 +6417,16 @@ init_cjs_shims();
|
|
|
6207
6417
|
|
|
6208
6418
|
// src/extract/infra/docker-compose.ts
|
|
6209
6419
|
init_cjs_shims();
|
|
6210
|
-
var
|
|
6211
|
-
var
|
|
6420
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6421
|
+
var import_types22 = require("@neat.is/types");
|
|
6212
6422
|
|
|
6213
6423
|
// src/extract/infra/shared.ts
|
|
6214
6424
|
init_cjs_shims();
|
|
6215
|
-
var
|
|
6425
|
+
var import_types21 = require("@neat.is/types");
|
|
6216
6426
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6217
6427
|
return {
|
|
6218
|
-
id: (0,
|
|
6219
|
-
type:
|
|
6428
|
+
id: (0, import_types21.infraId)(kind, name),
|
|
6429
|
+
type: import_types21.NodeType.InfraNode,
|
|
6220
6430
|
name,
|
|
6221
6431
|
provider,
|
|
6222
6432
|
kind,
|
|
@@ -6245,7 +6455,7 @@ function dependsOnList(value) {
|
|
|
6245
6455
|
}
|
|
6246
6456
|
function serviceNameToServiceNode(name, services) {
|
|
6247
6457
|
for (const s of services) {
|
|
6248
|
-
if (s.node.name === name ||
|
|
6458
|
+
if (s.node.name === name || import_node_path31.default.basename(s.dir) === name) return s.node.id;
|
|
6249
6459
|
}
|
|
6250
6460
|
return null;
|
|
6251
6461
|
}
|
|
@@ -6254,7 +6464,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6254
6464
|
let edgesAdded = 0;
|
|
6255
6465
|
let composePath = null;
|
|
6256
6466
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6257
|
-
const abs =
|
|
6467
|
+
const abs = import_node_path31.default.join(scanPath, name);
|
|
6258
6468
|
if (await exists(abs)) {
|
|
6259
6469
|
composePath = abs;
|
|
6260
6470
|
break;
|
|
@@ -6267,13 +6477,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6267
6477
|
} catch (err) {
|
|
6268
6478
|
recordExtractionError(
|
|
6269
6479
|
"infra docker-compose",
|
|
6270
|
-
|
|
6480
|
+
import_node_path31.default.relative(scanPath, composePath),
|
|
6271
6481
|
err
|
|
6272
6482
|
);
|
|
6273
6483
|
return { nodesAdded, edgesAdded };
|
|
6274
6484
|
}
|
|
6275
6485
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6276
|
-
const evidenceFile =
|
|
6486
|
+
const evidenceFile = import_node_path31.default.relative(scanPath, composePath).split(import_node_path31.default.sep).join("/");
|
|
6277
6487
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6278
6488
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6279
6489
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6295,15 +6505,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6295
6505
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6296
6506
|
const targetId = composeNameToNodeId.get(dep);
|
|
6297
6507
|
if (!targetId) continue;
|
|
6298
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
6508
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types22.EdgeType.DEPENDS_ON);
|
|
6299
6509
|
if (graph.hasEdge(edgeId)) continue;
|
|
6300
6510
|
const edge = {
|
|
6301
6511
|
id: edgeId,
|
|
6302
6512
|
source: sourceId,
|
|
6303
6513
|
target: targetId,
|
|
6304
|
-
type:
|
|
6305
|
-
provenance:
|
|
6306
|
-
confidence: (0,
|
|
6514
|
+
type: import_types22.EdgeType.DEPENDS_ON,
|
|
6515
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6516
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6307
6517
|
evidence: { file: evidenceFile }
|
|
6308
6518
|
};
|
|
6309
6519
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6315,9 +6525,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6315
6525
|
|
|
6316
6526
|
// src/extract/infra/dockerfile.ts
|
|
6317
6527
|
init_cjs_shims();
|
|
6318
|
-
var
|
|
6319
|
-
var
|
|
6320
|
-
var
|
|
6528
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6529
|
+
var import_node_fs16 = require("fs");
|
|
6530
|
+
var import_types23 = require("@neat.is/types");
|
|
6321
6531
|
function readDockerfile(content) {
|
|
6322
6532
|
let image = null;
|
|
6323
6533
|
const ports = [];
|
|
@@ -6346,15 +6556,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6346
6556
|
let nodesAdded = 0;
|
|
6347
6557
|
let edgesAdded = 0;
|
|
6348
6558
|
for (const service of services) {
|
|
6349
|
-
const dockerfilePath =
|
|
6559
|
+
const dockerfilePath = import_node_path32.default.join(service.dir, "Dockerfile");
|
|
6350
6560
|
if (!await exists(dockerfilePath)) continue;
|
|
6351
6561
|
let content;
|
|
6352
6562
|
try {
|
|
6353
|
-
content = await
|
|
6563
|
+
content = await import_node_fs16.promises.readFile(dockerfilePath, "utf8");
|
|
6354
6564
|
} catch (err) {
|
|
6355
6565
|
recordExtractionError(
|
|
6356
6566
|
"infra dockerfile",
|
|
6357
|
-
|
|
6567
|
+
import_node_path32.default.relative(scanPath, dockerfilePath),
|
|
6358
6568
|
err
|
|
6359
6569
|
);
|
|
6360
6570
|
continue;
|
|
@@ -6366,8 +6576,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6366
6576
|
graph.addNode(node.id, node);
|
|
6367
6577
|
nodesAdded++;
|
|
6368
6578
|
}
|
|
6369
|
-
const relDockerfile = toPosix2(
|
|
6370
|
-
const evidenceFile = toPosix2(
|
|
6579
|
+
const relDockerfile = toPosix2(import_node_path32.default.relative(service.dir, dockerfilePath));
|
|
6580
|
+
const evidenceFile = toPosix2(import_node_path32.default.relative(scanPath, dockerfilePath));
|
|
6371
6581
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6372
6582
|
graph,
|
|
6373
6583
|
service.pkg.name,
|
|
@@ -6376,15 +6586,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6376
6586
|
);
|
|
6377
6587
|
nodesAdded += fn;
|
|
6378
6588
|
edgesAdded += fe;
|
|
6379
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
6589
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types23.EdgeType.RUNS_ON);
|
|
6380
6590
|
if (!graph.hasEdge(edgeId)) {
|
|
6381
6591
|
const edge = {
|
|
6382
6592
|
id: edgeId,
|
|
6383
6593
|
source: fileNodeId,
|
|
6384
6594
|
target: node.id,
|
|
6385
|
-
type:
|
|
6386
|
-
provenance:
|
|
6387
|
-
confidence: (0,
|
|
6595
|
+
type: import_types23.EdgeType.RUNS_ON,
|
|
6596
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6597
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6388
6598
|
evidence: {
|
|
6389
6599
|
file: evidenceFile,
|
|
6390
6600
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6399,15 +6609,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6399
6609
|
graph.addNode(portNode.id, portNode);
|
|
6400
6610
|
nodesAdded++;
|
|
6401
6611
|
}
|
|
6402
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
6612
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types23.EdgeType.CONNECTS_TO);
|
|
6403
6613
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6404
6614
|
const portEdge = {
|
|
6405
6615
|
id: portEdgeId,
|
|
6406
6616
|
source: fileNodeId,
|
|
6407
6617
|
target: portNode.id,
|
|
6408
|
-
type:
|
|
6409
|
-
provenance:
|
|
6410
|
-
confidence: (0,
|
|
6618
|
+
type: import_types23.EdgeType.CONNECTS_TO,
|
|
6619
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6620
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6411
6621
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6412
6622
|
};
|
|
6413
6623
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6419,23 +6629,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6419
6629
|
|
|
6420
6630
|
// src/extract/infra/terraform.ts
|
|
6421
6631
|
init_cjs_shims();
|
|
6422
|
-
var
|
|
6423
|
-
var
|
|
6424
|
-
var
|
|
6632
|
+
var import_node_fs17 = require("fs");
|
|
6633
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6634
|
+
var import_types24 = require("@neat.is/types");
|
|
6425
6635
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6426
6636
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6427
6637
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
6428
6638
|
if (depth > max) return [];
|
|
6429
6639
|
const out = [];
|
|
6430
|
-
const entries = await
|
|
6640
|
+
const entries = await import_node_fs17.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6431
6641
|
for (const entry of entries) {
|
|
6432
6642
|
if (entry.isDirectory()) {
|
|
6433
6643
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
6434
|
-
const child =
|
|
6644
|
+
const child = import_node_path33.default.join(start, entry.name);
|
|
6435
6645
|
if (await isPythonVenvDir(child)) continue;
|
|
6436
6646
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6437
6647
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
6438
|
-
out.push(
|
|
6648
|
+
out.push(import_node_path33.default.join(start, entry.name));
|
|
6439
6649
|
}
|
|
6440
6650
|
}
|
|
6441
6651
|
return out;
|
|
@@ -6454,7 +6664,7 @@ function blockBody(content, from) {
|
|
|
6454
6664
|
}
|
|
6455
6665
|
return null;
|
|
6456
6666
|
}
|
|
6457
|
-
function
|
|
6667
|
+
function lineAt2(content, index) {
|
|
6458
6668
|
let line = 1;
|
|
6459
6669
|
for (let i = 0; i < index && i < content.length; i++) {
|
|
6460
6670
|
if (content[i] === "\n") line++;
|
|
@@ -6466,8 +6676,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6466
6676
|
let edgesAdded = 0;
|
|
6467
6677
|
const files = await walkTfFiles(scanPath);
|
|
6468
6678
|
for (const file of files) {
|
|
6469
|
-
const content = await
|
|
6470
|
-
const evidenceFile = toPosix2(
|
|
6679
|
+
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
6680
|
+
const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, file));
|
|
6471
6681
|
const resources = [];
|
|
6472
6682
|
const byKey = /* @__PURE__ */ new Map();
|
|
6473
6683
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6502,16 +6712,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6502
6712
|
if (!target) continue;
|
|
6503
6713
|
if (seen.has(target.nodeId)) continue;
|
|
6504
6714
|
seen.add(target.nodeId);
|
|
6505
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
6715
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types24.EdgeType.DEPENDS_ON);
|
|
6506
6716
|
if (graph.hasEdge(edgeId)) continue;
|
|
6507
|
-
const line =
|
|
6717
|
+
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6508
6718
|
const edge = {
|
|
6509
6719
|
id: edgeId,
|
|
6510
6720
|
source: resource.nodeId,
|
|
6511
6721
|
target: target.nodeId,
|
|
6512
|
-
type:
|
|
6513
|
-
provenance:
|
|
6514
|
-
confidence: (0,
|
|
6722
|
+
type: import_types24.EdgeType.DEPENDS_ON,
|
|
6723
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
6724
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6515
6725
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6516
6726
|
};
|
|
6517
6727
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6524,8 +6734,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6524
6734
|
|
|
6525
6735
|
// src/extract/infra/k8s.ts
|
|
6526
6736
|
init_cjs_shims();
|
|
6527
|
-
var
|
|
6528
|
-
var
|
|
6737
|
+
var import_node_fs18 = require("fs");
|
|
6738
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
6529
6739
|
var import_yaml3 = require("yaml");
|
|
6530
6740
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6531
6741
|
Service: "k8s-service",
|
|
@@ -6539,15 +6749,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
6539
6749
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
6540
6750
|
if (depth > max) return [];
|
|
6541
6751
|
const out = [];
|
|
6542
|
-
const entries = await
|
|
6752
|
+
const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6543
6753
|
for (const entry of entries) {
|
|
6544
6754
|
if (entry.isDirectory()) {
|
|
6545
6755
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6546
|
-
const child =
|
|
6756
|
+
const child = import_node_path34.default.join(start, entry.name);
|
|
6547
6757
|
if (await isPythonVenvDir(child)) continue;
|
|
6548
6758
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6549
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6550
|
-
out.push(
|
|
6759
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path34.default.extname(entry.name))) {
|
|
6760
|
+
out.push(import_node_path34.default.join(start, entry.name));
|
|
6551
6761
|
}
|
|
6552
6762
|
}
|
|
6553
6763
|
return out;
|
|
@@ -6556,7 +6766,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6556
6766
|
let nodesAdded = 0;
|
|
6557
6767
|
const files = await walkYamlFiles2(scanPath);
|
|
6558
6768
|
for (const file of files) {
|
|
6559
|
-
const content = await
|
|
6769
|
+
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
6560
6770
|
let docs;
|
|
6561
6771
|
try {
|
|
6562
6772
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -6591,17 +6801,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
6591
6801
|
}
|
|
6592
6802
|
|
|
6593
6803
|
// src/extract/index.ts
|
|
6594
|
-
var
|
|
6804
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6595
6805
|
|
|
6596
6806
|
// src/extract/retire.ts
|
|
6597
6807
|
init_cjs_shims();
|
|
6598
|
-
var
|
|
6599
|
-
var
|
|
6600
|
-
var
|
|
6808
|
+
var import_node_fs19 = require("fs");
|
|
6809
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6810
|
+
var import_types25 = require("@neat.is/types");
|
|
6601
6811
|
function dropOrphanedFileNodes(graph) {
|
|
6602
6812
|
const orphans = [];
|
|
6603
6813
|
graph.forEachNode((id, attrs) => {
|
|
6604
|
-
if (attrs.type !==
|
|
6814
|
+
if (attrs.type !== import_types25.NodeType.FileNode) return;
|
|
6605
6815
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
6606
6816
|
orphans.push(id);
|
|
6607
6817
|
}
|
|
@@ -6614,14 +6824,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
6614
6824
|
const bases = [scanPath, ...serviceDirs];
|
|
6615
6825
|
graph.forEachEdge((id, attrs) => {
|
|
6616
6826
|
const edge = attrs;
|
|
6617
|
-
if (edge.provenance !==
|
|
6827
|
+
if (edge.provenance !== import_types25.Provenance.EXTRACTED) return;
|
|
6618
6828
|
const evidenceFile = edge.evidence?.file;
|
|
6619
6829
|
if (!evidenceFile) return;
|
|
6620
|
-
if (
|
|
6621
|
-
if (!(0,
|
|
6830
|
+
if (import_node_path35.default.isAbsolute(evidenceFile)) {
|
|
6831
|
+
if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
|
|
6622
6832
|
return;
|
|
6623
6833
|
}
|
|
6624
|
-
const found = bases.some((base) => (0,
|
|
6834
|
+
const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path35.default.join(base, evidenceFile)));
|
|
6625
6835
|
if (!found) toDrop.push(id);
|
|
6626
6836
|
});
|
|
6627
6837
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -6641,6 +6851,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6641
6851
|
const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
|
|
6642
6852
|
const phase3 = await addConfigNodes(graph, services, scanPath);
|
|
6643
6853
|
const routePhase = await addRoutes(graph, services);
|
|
6854
|
+
const grpcPhase = await addGrpcMethods(graph, services);
|
|
6644
6855
|
const phase4 = await addCallEdges(graph, services);
|
|
6645
6856
|
const phase5 = await addInfra(graph, scanPath, services);
|
|
6646
6857
|
const ghostsRetired = retireExtractedEdgesByMissingFile(
|
|
@@ -6662,7 +6873,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6662
6873
|
}
|
|
6663
6874
|
const droppedEntries = drainDroppedExtracted();
|
|
6664
6875
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
6665
|
-
const rejectedPath =
|
|
6876
|
+
const rejectedPath = import_node_path36.default.join(import_node_path36.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
6666
6877
|
try {
|
|
6667
6878
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
6668
6879
|
} catch (err) {
|
|
@@ -6672,8 +6883,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6672
6883
|
}
|
|
6673
6884
|
}
|
|
6674
6885
|
const result = {
|
|
6675
|
-
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
6676
|
-
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
6886
|
+
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + grpcPhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
6887
|
+
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + grpcPhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
6677
6888
|
frontiersPromoted,
|
|
6678
6889
|
extractionErrors: errorEntries.length,
|
|
6679
6890
|
errorEntries,
|
|
@@ -6696,9 +6907,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6696
6907
|
|
|
6697
6908
|
// src/persist.ts
|
|
6698
6909
|
init_cjs_shims();
|
|
6699
|
-
var
|
|
6700
|
-
var
|
|
6701
|
-
var
|
|
6910
|
+
var import_node_fs20 = require("fs");
|
|
6911
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
6912
|
+
var import_types26 = require("@neat.is/types");
|
|
6702
6913
|
var SCHEMA_VERSION = 4;
|
|
6703
6914
|
function migrateV1ToV2(payload) {
|
|
6704
6915
|
const nodes = payload.graph.nodes;
|
|
@@ -6720,12 +6931,12 @@ function migrateV2ToV3(payload) {
|
|
|
6720
6931
|
for (const edge of edges) {
|
|
6721
6932
|
const attrs = edge.attributes;
|
|
6722
6933
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
6723
|
-
attrs.provenance =
|
|
6934
|
+
attrs.provenance = import_types26.Provenance.OBSERVED;
|
|
6724
6935
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
6725
6936
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
6726
6937
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
6727
6938
|
if (type && source && target) {
|
|
6728
|
-
const newId = (0,
|
|
6939
|
+
const newId = (0, import_types26.observedEdgeId)(source, target, type);
|
|
6729
6940
|
attrs.id = newId;
|
|
6730
6941
|
if (edge.key) edge.key = newId;
|
|
6731
6942
|
}
|
|
@@ -6734,7 +6945,7 @@ function migrateV2ToV3(payload) {
|
|
|
6734
6945
|
return { ...payload, schemaVersion: 3 };
|
|
6735
6946
|
}
|
|
6736
6947
|
async function ensureDir(filePath) {
|
|
6737
|
-
await
|
|
6948
|
+
await import_node_fs20.promises.mkdir(import_node_path37.default.dirname(filePath), { recursive: true });
|
|
6738
6949
|
}
|
|
6739
6950
|
async function saveGraphToDisk(graph, outPath) {
|
|
6740
6951
|
await ensureDir(outPath);
|
|
@@ -6744,13 +6955,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
6744
6955
|
graph: graph.export()
|
|
6745
6956
|
};
|
|
6746
6957
|
const tmp = `${outPath}.tmp`;
|
|
6747
|
-
await
|
|
6748
|
-
await
|
|
6958
|
+
await import_node_fs20.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
6959
|
+
await import_node_fs20.promises.rename(tmp, outPath);
|
|
6749
6960
|
}
|
|
6750
6961
|
async function loadGraphFromDisk(graph, outPath) {
|
|
6751
6962
|
let raw;
|
|
6752
6963
|
try {
|
|
6753
|
-
raw = await
|
|
6964
|
+
raw = await import_node_fs20.promises.readFile(outPath, "utf8");
|
|
6754
6965
|
} catch (err) {
|
|
6755
6966
|
if (err.code === "ENOENT") return;
|
|
6756
6967
|
throw err;
|
|
@@ -6817,19 +7028,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
6817
7028
|
init_cjs_shims();
|
|
6818
7029
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
6819
7030
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
6820
|
-
var
|
|
7031
|
+
var import_types29 = require("@neat.is/types");
|
|
6821
7032
|
|
|
6822
7033
|
// src/extend/index.ts
|
|
6823
7034
|
init_cjs_shims();
|
|
6824
|
-
var
|
|
6825
|
-
var
|
|
7035
|
+
var import_node_fs22 = require("fs");
|
|
7036
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
6826
7037
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
6827
7038
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
6828
7039
|
|
|
6829
7040
|
// src/installers/package-manager.ts
|
|
6830
7041
|
init_cjs_shims();
|
|
6831
|
-
var
|
|
6832
|
-
var
|
|
7042
|
+
var import_node_fs21 = require("fs");
|
|
7043
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
6833
7044
|
var import_node_child_process = require("child_process");
|
|
6834
7045
|
var LOCKFILE_PRIORITY = [
|
|
6835
7046
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -6844,29 +7055,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
6844
7055
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
6845
7056
|
async function exists2(p) {
|
|
6846
7057
|
try {
|
|
6847
|
-
await
|
|
7058
|
+
await import_node_fs21.promises.access(p);
|
|
6848
7059
|
return true;
|
|
6849
7060
|
} catch {
|
|
6850
7061
|
return false;
|
|
6851
7062
|
}
|
|
6852
7063
|
}
|
|
6853
7064
|
async function detectPackageManager(serviceDir) {
|
|
6854
|
-
let dir =
|
|
7065
|
+
let dir = import_node_path38.default.resolve(serviceDir);
|
|
6855
7066
|
const stops = /* @__PURE__ */ new Set();
|
|
6856
7067
|
for (let i = 0; i < 64; i++) {
|
|
6857
7068
|
if (stops.has(dir)) break;
|
|
6858
7069
|
stops.add(dir);
|
|
6859
7070
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
6860
|
-
const lockPath =
|
|
7071
|
+
const lockPath = import_node_path38.default.join(dir, candidate.lockfile);
|
|
6861
7072
|
if (await exists2(lockPath)) {
|
|
6862
7073
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
6863
7074
|
}
|
|
6864
7075
|
}
|
|
6865
|
-
const parent =
|
|
7076
|
+
const parent = import_node_path38.default.dirname(dir);
|
|
6866
7077
|
if (parent === dir) break;
|
|
6867
7078
|
dir = parent;
|
|
6868
7079
|
}
|
|
6869
|
-
return { pm: "npm", cwd:
|
|
7080
|
+
return { pm: "npm", cwd: import_node_path38.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
6870
7081
|
}
|
|
6871
7082
|
async function runPackageManagerInstall(cmd) {
|
|
6872
7083
|
return new Promise((resolve) => {
|
|
@@ -6908,30 +7119,30 @@ ${err.message}`
|
|
|
6908
7119
|
// src/extend/index.ts
|
|
6909
7120
|
async function fileExists2(p) {
|
|
6910
7121
|
try {
|
|
6911
|
-
await
|
|
7122
|
+
await import_node_fs22.promises.access(p);
|
|
6912
7123
|
return true;
|
|
6913
7124
|
} catch {
|
|
6914
7125
|
return false;
|
|
6915
7126
|
}
|
|
6916
7127
|
}
|
|
6917
7128
|
async function readPackageJson(scanPath) {
|
|
6918
|
-
const pkgPath =
|
|
6919
|
-
const raw = await
|
|
7129
|
+
const pkgPath = import_node_path39.default.join(scanPath, "package.json");
|
|
7130
|
+
const raw = await import_node_fs22.promises.readFile(pkgPath, "utf8");
|
|
6920
7131
|
return JSON.parse(raw);
|
|
6921
7132
|
}
|
|
6922
7133
|
async function findHookFiles(scanPath) {
|
|
6923
|
-
const entries = await
|
|
7134
|
+
const entries = await import_node_fs22.promises.readdir(scanPath);
|
|
6924
7135
|
return entries.filter(
|
|
6925
7136
|
(e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
|
|
6926
7137
|
).sort();
|
|
6927
7138
|
}
|
|
6928
7139
|
function extendLogPath() {
|
|
6929
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
7140
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path39.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
6930
7141
|
}
|
|
6931
7142
|
async function appendExtendLog(entry) {
|
|
6932
7143
|
const logPath = extendLogPath();
|
|
6933
|
-
await
|
|
6934
|
-
await
|
|
7144
|
+
await import_node_fs22.promises.mkdir(import_node_path39.default.dirname(logPath), { recursive: true });
|
|
7145
|
+
await import_node_fs22.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
6935
7146
|
}
|
|
6936
7147
|
function splicedContent(fileContent, snippet2) {
|
|
6937
7148
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -6989,7 +7200,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
6989
7200
|
}
|
|
6990
7201
|
async function describeProjectInstrumentation(ctx) {
|
|
6991
7202
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
6992
|
-
const envNeat = await fileExists2(
|
|
7203
|
+
const envNeat = await fileExists2(import_node_path39.default.join(ctx.scanPath, ".env.neat"));
|
|
6993
7204
|
const registryInstrPackages = new Set(
|
|
6994
7205
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
6995
7206
|
);
|
|
@@ -7011,31 +7222,31 @@ async function applyExtension(ctx, args, options) {
|
|
|
7011
7222
|
);
|
|
7012
7223
|
}
|
|
7013
7224
|
for (const file of hookFiles) {
|
|
7014
|
-
const content = await
|
|
7225
|
+
const content = await import_node_fs22.promises.readFile(import_node_path39.default.join(ctx.scanPath, file), "utf8");
|
|
7015
7226
|
if (content.includes(args.registration_snippet)) {
|
|
7016
7227
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
7017
7228
|
}
|
|
7018
7229
|
}
|
|
7019
7230
|
const primaryFile = hookFiles[0];
|
|
7020
|
-
const primaryPath =
|
|
7231
|
+
const primaryPath = import_node_path39.default.join(ctx.scanPath, primaryFile);
|
|
7021
7232
|
const filesTouched = [];
|
|
7022
7233
|
const depsAdded = [];
|
|
7023
|
-
const pkgPath =
|
|
7234
|
+
const pkgPath = import_node_path39.default.join(ctx.scanPath, "package.json");
|
|
7024
7235
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7025
7236
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
7026
7237
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
7027
|
-
await
|
|
7238
|
+
await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
7028
7239
|
filesTouched.push("package.json");
|
|
7029
7240
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
7030
7241
|
}
|
|
7031
|
-
const hookContent = await
|
|
7242
|
+
const hookContent = await import_node_fs22.promises.readFile(primaryPath, "utf8");
|
|
7032
7243
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
7033
7244
|
if (!patched) {
|
|
7034
7245
|
throw new Error(
|
|
7035
7246
|
`Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
7036
7247
|
);
|
|
7037
7248
|
}
|
|
7038
|
-
await
|
|
7249
|
+
await import_node_fs22.promises.writeFile(primaryPath, patched, "utf8");
|
|
7039
7250
|
filesTouched.push(primaryFile);
|
|
7040
7251
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
7041
7252
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -7066,7 +7277,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7066
7277
|
};
|
|
7067
7278
|
}
|
|
7068
7279
|
for (const file of hookFiles) {
|
|
7069
|
-
const content = await
|
|
7280
|
+
const content = await import_node_fs22.promises.readFile(import_node_path39.default.join(ctx.scanPath, file), "utf8");
|
|
7070
7281
|
if (content.includes(args.registration_snippet)) {
|
|
7071
7282
|
return {
|
|
7072
7283
|
library: args.library,
|
|
@@ -7088,7 +7299,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7088
7299
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
7089
7300
|
filesTouched.push("package.json");
|
|
7090
7301
|
}
|
|
7091
|
-
const hookContent = await
|
|
7302
|
+
const hookContent = await import_node_fs22.promises.readFile(import_node_path39.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
7092
7303
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
7093
7304
|
if (patched) {
|
|
7094
7305
|
filesTouched.push(primaryFile);
|
|
@@ -7103,28 +7314,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
7103
7314
|
if (!await fileExists2(logPath)) {
|
|
7104
7315
|
return { undone: false, message: "no apply found for library" };
|
|
7105
7316
|
}
|
|
7106
|
-
const raw = await
|
|
7317
|
+
const raw = await import_node_fs22.promises.readFile(logPath, "utf8");
|
|
7107
7318
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
7108
7319
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
7109
7320
|
if (!match) {
|
|
7110
7321
|
return { undone: false, message: "no apply found for library" };
|
|
7111
7322
|
}
|
|
7112
|
-
const pkgPath =
|
|
7323
|
+
const pkgPath = import_node_path39.default.join(ctx.scanPath, "package.json");
|
|
7113
7324
|
if (await fileExists2(pkgPath)) {
|
|
7114
7325
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7115
7326
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
7116
7327
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
7117
7328
|
pkg.dependencies = rest;
|
|
7118
|
-
await
|
|
7329
|
+
await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
7119
7330
|
}
|
|
7120
7331
|
}
|
|
7121
7332
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7122
7333
|
for (const file of hookFiles) {
|
|
7123
|
-
const filePath =
|
|
7124
|
-
const content = await
|
|
7334
|
+
const filePath = import_node_path39.default.join(ctx.scanPath, file);
|
|
7335
|
+
const content = await import_node_fs22.promises.readFile(filePath, "utf8");
|
|
7125
7336
|
if (content.includes(match.registration_snippet)) {
|
|
7126
7337
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
7127
|
-
await
|
|
7338
|
+
await import_node_fs22.promises.writeFile(filePath, filtered, "utf8");
|
|
7128
7339
|
break;
|
|
7129
7340
|
}
|
|
7130
7341
|
}
|
|
@@ -7136,7 +7347,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
7136
7347
|
|
|
7137
7348
|
// src/divergences.ts
|
|
7138
7349
|
init_cjs_shims();
|
|
7139
|
-
var
|
|
7350
|
+
var import_types27 = require("@neat.is/types");
|
|
7140
7351
|
function bucketKey(source, target, type) {
|
|
7141
7352
|
return `${type}|${source}|${target}`;
|
|
7142
7353
|
}
|
|
@@ -7144,22 +7355,22 @@ function bucketEdges(graph) {
|
|
|
7144
7355
|
const buckets = /* @__PURE__ */ new Map();
|
|
7145
7356
|
graph.forEachEdge((id, attrs) => {
|
|
7146
7357
|
const e = attrs;
|
|
7147
|
-
const parsed = (0,
|
|
7358
|
+
const parsed = (0, import_types27.parseEdgeId)(id);
|
|
7148
7359
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7149
7360
|
const key = bucketKey(e.source, e.target, e.type);
|
|
7150
7361
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
7151
7362
|
switch (provenance) {
|
|
7152
|
-
case
|
|
7363
|
+
case import_types27.Provenance.EXTRACTED:
|
|
7153
7364
|
cur.extracted = e;
|
|
7154
7365
|
break;
|
|
7155
|
-
case
|
|
7366
|
+
case import_types27.Provenance.OBSERVED:
|
|
7156
7367
|
cur.observed = e;
|
|
7157
7368
|
break;
|
|
7158
|
-
case
|
|
7369
|
+
case import_types27.Provenance.INFERRED:
|
|
7159
7370
|
cur.inferred = e;
|
|
7160
7371
|
break;
|
|
7161
7372
|
default:
|
|
7162
|
-
if (e.provenance ===
|
|
7373
|
+
if (e.provenance === import_types27.Provenance.STALE) cur.stale = e;
|
|
7163
7374
|
}
|
|
7164
7375
|
buckets.set(key, cur);
|
|
7165
7376
|
});
|
|
@@ -7168,7 +7379,12 @@ function bucketEdges(graph) {
|
|
|
7168
7379
|
function nodeIsFrontier(graph, nodeId) {
|
|
7169
7380
|
if (!graph.hasNode(nodeId)) return false;
|
|
7170
7381
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7171
|
-
return attrs.type ===
|
|
7382
|
+
return attrs.type === import_types27.NodeType.FrontierNode;
|
|
7383
|
+
}
|
|
7384
|
+
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
7385
|
+
if (!graph.hasNode(nodeId)) return false;
|
|
7386
|
+
const attrs = graph.getNodeAttributes(nodeId);
|
|
7387
|
+
return attrs.type === import_types27.NodeType.WebSocketChannelNode;
|
|
7172
7388
|
}
|
|
7173
7389
|
function clampConfidence(n) {
|
|
7174
7390
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -7188,14 +7404,14 @@ function gradedConfidence(edge) {
|
|
|
7188
7404
|
return clampConfidence(confidenceForEdge(edge));
|
|
7189
7405
|
}
|
|
7190
7406
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7407
|
+
import_types27.EdgeType.CALLS,
|
|
7408
|
+
import_types27.EdgeType.CONNECTS_TO,
|
|
7409
|
+
import_types27.EdgeType.PUBLISHES_TO,
|
|
7410
|
+
import_types27.EdgeType.CONSUMES_FROM
|
|
7195
7411
|
]);
|
|
7196
7412
|
function detectMissingDivergences(graph, bucket) {
|
|
7197
7413
|
const out = [];
|
|
7198
|
-
if (bucket.type ===
|
|
7414
|
+
if (bucket.type === import_types27.EdgeType.CONTAINS) return out;
|
|
7199
7415
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
7200
7416
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
7201
7417
|
out.push({
|
|
@@ -7210,7 +7426,7 @@ function detectMissingDivergences(graph, bucket) {
|
|
|
7210
7426
|
});
|
|
7211
7427
|
}
|
|
7212
7428
|
}
|
|
7213
|
-
if (bucket.observed && !bucket.extracted) {
|
|
7429
|
+
if (bucket.observed && !bucket.extracted && !nodeIsWebsocketChannel(graph, bucket.target)) {
|
|
7214
7430
|
out.push({
|
|
7215
7431
|
type: "missing-extracted",
|
|
7216
7432
|
source: bucket.source,
|
|
@@ -7236,7 +7452,7 @@ function declaredHostFor(svc) {
|
|
|
7236
7452
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
7237
7453
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7238
7454
|
const e = graph.getEdgeAttributes(edgeId);
|
|
7239
|
-
if (e.type ===
|
|
7455
|
+
if (e.type === import_types27.EdgeType.CONFIGURED_BY && e.provenance === import_types27.Provenance.EXTRACTED) {
|
|
7240
7456
|
return true;
|
|
7241
7457
|
}
|
|
7242
7458
|
}
|
|
@@ -7249,10 +7465,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
7249
7465
|
const out = [];
|
|
7250
7466
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7251
7467
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7252
|
-
if (edge.type !==
|
|
7253
|
-
if (edge.provenance !==
|
|
7468
|
+
if (edge.type !== import_types27.EdgeType.CONNECTS_TO) continue;
|
|
7469
|
+
if (edge.provenance !== import_types27.Provenance.OBSERVED) continue;
|
|
7254
7470
|
const target = graph.getNodeAttributes(edge.target);
|
|
7255
|
-
if (target.type !==
|
|
7471
|
+
if (target.type !== import_types27.NodeType.DatabaseNode) continue;
|
|
7256
7472
|
const observedHost = target.host?.trim();
|
|
7257
7473
|
if (!observedHost) continue;
|
|
7258
7474
|
if (observedHost === declaredHost) continue;
|
|
@@ -7274,10 +7490,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
7274
7490
|
const deps = svc.dependencies ?? {};
|
|
7275
7491
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7276
7492
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7277
|
-
if (edge.type !==
|
|
7278
|
-
if (edge.provenance !==
|
|
7493
|
+
if (edge.type !== import_types27.EdgeType.CONNECTS_TO) continue;
|
|
7494
|
+
if (edge.provenance !== import_types27.Provenance.OBSERVED) continue;
|
|
7279
7495
|
const target = graph.getNodeAttributes(edge.target);
|
|
7280
|
-
if (target.type !==
|
|
7496
|
+
if (target.type !== import_types27.NodeType.DatabaseNode) continue;
|
|
7281
7497
|
for (const pair of compatPairs()) {
|
|
7282
7498
|
if (pair.engine !== target.engine) continue;
|
|
7283
7499
|
const declared = deps[pair.driver];
|
|
@@ -7336,7 +7552,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
7336
7552
|
for (const d of all) {
|
|
7337
7553
|
if (d.type !== "host-mismatch") continue;
|
|
7338
7554
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
7339
|
-
declaredHalf.add((0,
|
|
7555
|
+
declaredHalf.add((0, import_types27.databaseId)(d.extractedHost));
|
|
7340
7556
|
}
|
|
7341
7557
|
if (observedHalf.size === 0) return all;
|
|
7342
7558
|
return all.filter((d) => {
|
|
@@ -7355,7 +7571,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7355
7571
|
}
|
|
7356
7572
|
graph.forEachNode((nodeId, attrs) => {
|
|
7357
7573
|
const n = attrs;
|
|
7358
|
-
if (n.type !==
|
|
7574
|
+
if (n.type !== import_types27.NodeType.ServiceNode) return;
|
|
7359
7575
|
const svc = n;
|
|
7360
7576
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
7361
7577
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -7389,7 +7605,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7389
7605
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
7390
7606
|
return a.target.localeCompare(b.target);
|
|
7391
7607
|
});
|
|
7392
|
-
return
|
|
7608
|
+
return import_types27.DivergenceResultSchema.parse({
|
|
7393
7609
|
divergences: filtered,
|
|
7394
7610
|
totalAffected: filtered.length,
|
|
7395
7611
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7398,7 +7614,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7398
7614
|
|
|
7399
7615
|
// src/diff.ts
|
|
7400
7616
|
init_cjs_shims();
|
|
7401
|
-
var
|
|
7617
|
+
var import_node_fs23 = require("fs");
|
|
7402
7618
|
async function loadSnapshotForDiff(target) {
|
|
7403
7619
|
if (/^https?:\/\//i.test(target)) {
|
|
7404
7620
|
const res = await fetch(target);
|
|
@@ -7407,7 +7623,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
7407
7623
|
}
|
|
7408
7624
|
return await res.json();
|
|
7409
7625
|
}
|
|
7410
|
-
const raw = await
|
|
7626
|
+
const raw = await import_node_fs23.promises.readFile(target, "utf8");
|
|
7411
7627
|
return JSON.parse(raw);
|
|
7412
7628
|
}
|
|
7413
7629
|
function indexEntries(entries) {
|
|
@@ -7475,23 +7691,23 @@ function canonicalJson(value) {
|
|
|
7475
7691
|
|
|
7476
7692
|
// src/projects.ts
|
|
7477
7693
|
init_cjs_shims();
|
|
7478
|
-
var
|
|
7694
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7479
7695
|
function pathsForProject(project, baseDir) {
|
|
7480
7696
|
if (project === DEFAULT_PROJECT) {
|
|
7481
7697
|
return {
|
|
7482
|
-
snapshotPath:
|
|
7483
|
-
errorsPath:
|
|
7484
|
-
staleEventsPath:
|
|
7485
|
-
embeddingsCachePath:
|
|
7486
|
-
policyViolationsPath:
|
|
7698
|
+
snapshotPath: import_node_path40.default.join(baseDir, "graph.json"),
|
|
7699
|
+
errorsPath: import_node_path40.default.join(baseDir, "errors.ndjson"),
|
|
7700
|
+
staleEventsPath: import_node_path40.default.join(baseDir, "stale-events.ndjson"),
|
|
7701
|
+
embeddingsCachePath: import_node_path40.default.join(baseDir, "embeddings.json"),
|
|
7702
|
+
policyViolationsPath: import_node_path40.default.join(baseDir, "policy-violations.ndjson")
|
|
7487
7703
|
};
|
|
7488
7704
|
}
|
|
7489
7705
|
return {
|
|
7490
|
-
snapshotPath:
|
|
7491
|
-
errorsPath:
|
|
7492
|
-
staleEventsPath:
|
|
7493
|
-
embeddingsCachePath:
|
|
7494
|
-
policyViolationsPath:
|
|
7706
|
+
snapshotPath: import_node_path40.default.join(baseDir, `${project}.json`),
|
|
7707
|
+
errorsPath: import_node_path40.default.join(baseDir, `errors.${project}.ndjson`),
|
|
7708
|
+
staleEventsPath: import_node_path40.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
7709
|
+
embeddingsCachePath: import_node_path40.default.join(baseDir, `embeddings.${project}.json`),
|
|
7710
|
+
policyViolationsPath: import_node_path40.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
7495
7711
|
};
|
|
7496
7712
|
}
|
|
7497
7713
|
var Projects = class {
|
|
@@ -7527,25 +7743,25 @@ var Projects = class {
|
|
|
7527
7743
|
|
|
7528
7744
|
// src/registry.ts
|
|
7529
7745
|
init_cjs_shims();
|
|
7530
|
-
var
|
|
7746
|
+
var import_node_fs24 = require("fs");
|
|
7531
7747
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
7532
|
-
var
|
|
7533
|
-
var
|
|
7748
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7749
|
+
var import_types28 = require("@neat.is/types");
|
|
7534
7750
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
7535
7751
|
var LOCK_RETRY_MS = 50;
|
|
7536
7752
|
function neatHome() {
|
|
7537
7753
|
const override = process.env.NEAT_HOME;
|
|
7538
|
-
if (override && override.length > 0) return
|
|
7539
|
-
return
|
|
7754
|
+
if (override && override.length > 0) return import_node_path41.default.resolve(override);
|
|
7755
|
+
return import_node_path41.default.join(import_node_os3.default.homedir(), ".neat");
|
|
7540
7756
|
}
|
|
7541
7757
|
function registryPath() {
|
|
7542
|
-
return
|
|
7758
|
+
return import_node_path41.default.join(neatHome(), "projects.json");
|
|
7543
7759
|
}
|
|
7544
7760
|
function registryLockPath() {
|
|
7545
|
-
return
|
|
7761
|
+
return import_node_path41.default.join(neatHome(), "projects.json.lock");
|
|
7546
7762
|
}
|
|
7547
7763
|
function daemonPidPath() {
|
|
7548
|
-
return
|
|
7764
|
+
return import_node_path41.default.join(neatHome(), "neatd.pid");
|
|
7549
7765
|
}
|
|
7550
7766
|
function isPidAliveDefault(pid) {
|
|
7551
7767
|
try {
|
|
@@ -7557,7 +7773,7 @@ function isPidAliveDefault(pid) {
|
|
|
7557
7773
|
}
|
|
7558
7774
|
async function readPidFile(file) {
|
|
7559
7775
|
try {
|
|
7560
|
-
const raw = await
|
|
7776
|
+
const raw = await import_node_fs24.promises.readFile(file, "utf8");
|
|
7561
7777
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
7562
7778
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
7563
7779
|
} catch {
|
|
@@ -7605,32 +7821,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
7605
7821
|
}
|
|
7606
7822
|
}
|
|
7607
7823
|
async function normalizeProjectPath(input) {
|
|
7608
|
-
const resolved =
|
|
7824
|
+
const resolved = import_node_path41.default.resolve(input);
|
|
7609
7825
|
try {
|
|
7610
|
-
return await
|
|
7826
|
+
return await import_node_fs24.promises.realpath(resolved);
|
|
7611
7827
|
} catch {
|
|
7612
7828
|
return resolved;
|
|
7613
7829
|
}
|
|
7614
7830
|
}
|
|
7615
7831
|
async function writeAtomically(target, contents) {
|
|
7616
|
-
await
|
|
7832
|
+
await import_node_fs24.promises.mkdir(import_node_path41.default.dirname(target), { recursive: true });
|
|
7617
7833
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
7618
|
-
const fd = await
|
|
7834
|
+
const fd = await import_node_fs24.promises.open(tmp, "w");
|
|
7619
7835
|
try {
|
|
7620
7836
|
await fd.writeFile(contents, "utf8");
|
|
7621
7837
|
await fd.sync();
|
|
7622
7838
|
} finally {
|
|
7623
7839
|
await fd.close();
|
|
7624
7840
|
}
|
|
7625
|
-
await
|
|
7841
|
+
await import_node_fs24.promises.rename(tmp, target);
|
|
7626
7842
|
}
|
|
7627
7843
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
7628
7844
|
const deadline = Date.now() + timeoutMs;
|
|
7629
|
-
await
|
|
7845
|
+
await import_node_fs24.promises.mkdir(import_node_path41.default.dirname(lockPath), { recursive: true });
|
|
7630
7846
|
let probedHolder = false;
|
|
7631
7847
|
while (true) {
|
|
7632
7848
|
try {
|
|
7633
|
-
const fd = await
|
|
7849
|
+
const fd = await import_node_fs24.promises.open(lockPath, "wx");
|
|
7634
7850
|
try {
|
|
7635
7851
|
await fd.writeFile(`${process.pid}
|
|
7636
7852
|
`, "utf8");
|
|
@@ -7655,7 +7871,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
7655
7871
|
}
|
|
7656
7872
|
}
|
|
7657
7873
|
async function releaseLock(lockPath) {
|
|
7658
|
-
await
|
|
7874
|
+
await import_node_fs24.promises.unlink(lockPath).catch(() => {
|
|
7659
7875
|
});
|
|
7660
7876
|
}
|
|
7661
7877
|
async function withLock(fn) {
|
|
@@ -7671,7 +7887,7 @@ async function readRegistry() {
|
|
|
7671
7887
|
const file = registryPath();
|
|
7672
7888
|
let raw;
|
|
7673
7889
|
try {
|
|
7674
|
-
raw = await
|
|
7890
|
+
raw = await import_node_fs24.promises.readFile(file, "utf8");
|
|
7675
7891
|
} catch (err) {
|
|
7676
7892
|
if (err.code === "ENOENT") {
|
|
7677
7893
|
return { version: 1, projects: [] };
|
|
@@ -7679,10 +7895,10 @@ async function readRegistry() {
|
|
|
7679
7895
|
throw err;
|
|
7680
7896
|
}
|
|
7681
7897
|
const parsed = JSON.parse(raw);
|
|
7682
|
-
return
|
|
7898
|
+
return import_types28.RegistryFileSchema.parse(parsed);
|
|
7683
7899
|
}
|
|
7684
7900
|
async function writeRegistry(reg) {
|
|
7685
|
-
const validated =
|
|
7901
|
+
const validated = import_types28.RegistryFileSchema.parse(reg);
|
|
7686
7902
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
7687
7903
|
}
|
|
7688
7904
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -7776,7 +7992,7 @@ function pruneTtlMs() {
|
|
|
7776
7992
|
}
|
|
7777
7993
|
async function statPathStatus(p) {
|
|
7778
7994
|
try {
|
|
7779
|
-
const stat = await
|
|
7995
|
+
const stat = await import_node_fs24.promises.stat(p);
|
|
7780
7996
|
return stat.isDirectory() ? "present" : "unknown";
|
|
7781
7997
|
} catch (err) {
|
|
7782
7998
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -8023,11 +8239,11 @@ function registerRoutes(scope, ctx) {
|
|
|
8023
8239
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
8024
8240
|
const parsed = [];
|
|
8025
8241
|
for (const c of candidates) {
|
|
8026
|
-
const r =
|
|
8242
|
+
const r = import_types29.DivergenceTypeSchema.safeParse(c);
|
|
8027
8243
|
if (!r.success) {
|
|
8028
8244
|
return reply.code(400).send({
|
|
8029
8245
|
error: `unknown divergence type "${c}"`,
|
|
8030
|
-
allowed:
|
|
8246
|
+
allowed: import_types29.DivergenceTypeSchema.options
|
|
8031
8247
|
});
|
|
8032
8248
|
}
|
|
8033
8249
|
parsed.push(r.data);
|
|
@@ -8246,7 +8462,7 @@ function registerRoutes(scope, ctx) {
|
|
|
8246
8462
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
8247
8463
|
let violations = await log.readAll();
|
|
8248
8464
|
if (req.query.severity) {
|
|
8249
|
-
const sev =
|
|
8465
|
+
const sev = import_types29.PolicySeveritySchema.safeParse(req.query.severity);
|
|
8250
8466
|
if (!sev.success) {
|
|
8251
8467
|
return reply.code(400).send({
|
|
8252
8468
|
error: "invalid severity",
|
|
@@ -8285,7 +8501,7 @@ function registerRoutes(scope, ctx) {
|
|
|
8285
8501
|
scope.post("/policies/check", async (req, reply) => {
|
|
8286
8502
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
8287
8503
|
if (!proj) return;
|
|
8288
|
-
const parsed =
|
|
8504
|
+
const parsed = import_types29.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
8289
8505
|
if (!parsed.success) {
|
|
8290
8506
|
return reply.code(400).send({
|
|
8291
8507
|
error: "invalid /policies/check body",
|
|
@@ -8547,16 +8763,95 @@ init_otel_grpc();
|
|
|
8547
8763
|
|
|
8548
8764
|
// src/daemon.ts
|
|
8549
8765
|
init_cjs_shims();
|
|
8550
|
-
var
|
|
8551
|
-
var
|
|
8766
|
+
var import_node_fs26 = require("fs");
|
|
8767
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8552
8768
|
var import_node_module = require("module");
|
|
8553
8769
|
init_otel();
|
|
8770
|
+
|
|
8771
|
+
// src/connectors/index.ts
|
|
8772
|
+
init_cjs_shims();
|
|
8773
|
+
var NO_ENV = "unknown";
|
|
8774
|
+
async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
8775
|
+
const signals = await connector.poll(ctx);
|
|
8776
|
+
let edgesCreated = 0;
|
|
8777
|
+
let edgesUpdated = 0;
|
|
8778
|
+
let unresolved = 0;
|
|
8779
|
+
for (const signal of signals) {
|
|
8780
|
+
const resolved = resolveTarget(signal, ctx);
|
|
8781
|
+
if (!resolved) {
|
|
8782
|
+
unresolved++;
|
|
8783
|
+
continue;
|
|
8784
|
+
}
|
|
8785
|
+
const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
8786
|
+
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : void 0;
|
|
8787
|
+
const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
|
|
8788
|
+
const evidence = callSite ? {
|
|
8789
|
+
file: reconcileObservedRelPath(graph, resolved.serviceName, callSite.relPath),
|
|
8790
|
+
line: callSite.line
|
|
8791
|
+
} : void 0;
|
|
8792
|
+
const calls = Math.trunc(signal.callCount);
|
|
8793
|
+
if (calls < 1) continue;
|
|
8794
|
+
const errors = Math.min(Math.max(Math.trunc(signal.errorCount), 0), calls);
|
|
8795
|
+
let created = false;
|
|
8796
|
+
let ok = true;
|
|
8797
|
+
for (let i = 0; i < calls; i++) {
|
|
8798
|
+
const result = upsertObservedEdge(
|
|
8799
|
+
graph,
|
|
8800
|
+
resolved.edgeType,
|
|
8801
|
+
sourceId,
|
|
8802
|
+
resolved.targetNodeId,
|
|
8803
|
+
signal.lastObservedIso,
|
|
8804
|
+
i < errors,
|
|
8805
|
+
evidence
|
|
8806
|
+
);
|
|
8807
|
+
if (!result) {
|
|
8808
|
+
ok = false;
|
|
8809
|
+
break;
|
|
8810
|
+
}
|
|
8811
|
+
if (i === 0) created = result.created;
|
|
8812
|
+
}
|
|
8813
|
+
if (!ok) {
|
|
8814
|
+
unresolved++;
|
|
8815
|
+
continue;
|
|
8816
|
+
}
|
|
8817
|
+
if (created) edgesCreated++;
|
|
8818
|
+
else edgesUpdated++;
|
|
8819
|
+
}
|
|
8820
|
+
return { signalCount: signals.length, edgesCreated, edgesUpdated, unresolved };
|
|
8821
|
+
}
|
|
8822
|
+
var DEFAULT_POLL_INTERVAL_MS = 6e4;
|
|
8823
|
+
function startConnectorPollLoop(connector, ctx, graph, resolveTarget, options = {}) {
|
|
8824
|
+
let stopped = false;
|
|
8825
|
+
let since = ctx.since;
|
|
8826
|
+
const intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
8827
|
+
const onError = options.onError ?? ((err) => console.error(`[neatd] connector poll failed (${connector.provider})`, err));
|
|
8828
|
+
const tick = () => {
|
|
8829
|
+
if (stopped) return;
|
|
8830
|
+
void (async () => {
|
|
8831
|
+
const tickStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8832
|
+
try {
|
|
8833
|
+
await runConnectorPoll(connector, { ...ctx, since }, graph, resolveTarget);
|
|
8834
|
+
since = tickStartedAt;
|
|
8835
|
+
} catch (err) {
|
|
8836
|
+
onError(err);
|
|
8837
|
+
}
|
|
8838
|
+
})();
|
|
8839
|
+
};
|
|
8840
|
+
const interval = setInterval(tick, intervalMs);
|
|
8841
|
+
if (typeof interval.unref === "function") interval.unref();
|
|
8842
|
+
return () => {
|
|
8843
|
+
stopped = true;
|
|
8844
|
+
clearInterval(interval);
|
|
8845
|
+
};
|
|
8846
|
+
}
|
|
8847
|
+
|
|
8848
|
+
// src/daemon.ts
|
|
8554
8849
|
init_auth();
|
|
8555
8850
|
|
|
8556
8851
|
// src/unrouted.ts
|
|
8557
8852
|
init_cjs_shims();
|
|
8558
|
-
var
|
|
8559
|
-
var
|
|
8853
|
+
var import_node_fs25 = require("fs");
|
|
8854
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8560
8855
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
8561
8856
|
return {
|
|
8562
8857
|
timestamp: now.toISOString(),
|
|
@@ -8566,34 +8861,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
8566
8861
|
};
|
|
8567
8862
|
}
|
|
8568
8863
|
async function appendUnroutedSpan(neatHome2, record) {
|
|
8569
|
-
const target =
|
|
8570
|
-
await
|
|
8571
|
-
await
|
|
8864
|
+
const target = import_node_path44.default.join(neatHome2, "errors.ndjson");
|
|
8865
|
+
await import_node_fs25.promises.mkdir(neatHome2, { recursive: true });
|
|
8866
|
+
await import_node_fs25.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
8572
8867
|
}
|
|
8573
8868
|
function unroutedErrorsPath(neatHome2) {
|
|
8574
|
-
return
|
|
8869
|
+
return import_node_path44.default.join(neatHome2, "errors.ndjson");
|
|
8575
8870
|
}
|
|
8576
8871
|
|
|
8577
8872
|
// src/daemon.ts
|
|
8578
|
-
var
|
|
8873
|
+
var import_types30 = require("@neat.is/types");
|
|
8579
8874
|
function daemonJsonPath(scanPath) {
|
|
8580
|
-
return
|
|
8875
|
+
return import_node_path45.default.join(scanPath, "neat-out", "daemon.json");
|
|
8581
8876
|
}
|
|
8582
8877
|
function daemonsDiscoveryDir(home) {
|
|
8583
8878
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
8584
|
-
return
|
|
8879
|
+
return import_node_path45.default.join(base, "daemons");
|
|
8585
8880
|
}
|
|
8586
8881
|
function daemonDiscoveryPath(project, home) {
|
|
8587
|
-
return
|
|
8882
|
+
return import_node_path45.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
8588
8883
|
}
|
|
8589
8884
|
function sanitizeDiscoveryName(project) {
|
|
8590
8885
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
8591
8886
|
}
|
|
8592
8887
|
function neatHomeFromEnv() {
|
|
8593
8888
|
const env = process.env.NEAT_HOME;
|
|
8594
|
-
if (env && env.length > 0) return
|
|
8889
|
+
if (env && env.length > 0) return import_node_path45.default.resolve(env);
|
|
8595
8890
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
8596
|
-
return
|
|
8891
|
+
return import_node_path45.default.join(home, ".neat");
|
|
8597
8892
|
}
|
|
8598
8893
|
function resolveNeatVersion() {
|
|
8599
8894
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -8625,7 +8920,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
8625
8920
|
} catch {
|
|
8626
8921
|
}
|
|
8627
8922
|
try {
|
|
8628
|
-
await
|
|
8923
|
+
await import_node_fs26.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
8629
8924
|
} catch {
|
|
8630
8925
|
}
|
|
8631
8926
|
}
|
|
@@ -8638,17 +8933,21 @@ function teardownSlot(slot) {
|
|
|
8638
8933
|
slot.stopStaleness();
|
|
8639
8934
|
} catch {
|
|
8640
8935
|
}
|
|
8936
|
+
try {
|
|
8937
|
+
slot.stopConnectors();
|
|
8938
|
+
} catch {
|
|
8939
|
+
}
|
|
8641
8940
|
try {
|
|
8642
8941
|
slot.detachEvents();
|
|
8643
8942
|
} catch {
|
|
8644
8943
|
}
|
|
8645
8944
|
}
|
|
8646
8945
|
function neatHomeFor(opts) {
|
|
8647
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
8946
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path45.default.resolve(opts.neatHome);
|
|
8648
8947
|
const env = process.env.NEAT_HOME;
|
|
8649
|
-
if (env && env.length > 0) return
|
|
8948
|
+
if (env && env.length > 0) return import_node_path45.default.resolve(env);
|
|
8650
8949
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
8651
|
-
return
|
|
8950
|
+
return import_node_path45.default.join(home, ".neat");
|
|
8652
8951
|
}
|
|
8653
8952
|
function routeSpanToProject(serviceName, projects) {
|
|
8654
8953
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -8692,13 +8991,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
8692
8991
|
if (!serviceName) return true;
|
|
8693
8992
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
8694
8993
|
return graph.someNode(
|
|
8695
|
-
(_id, attrs) => attrs.type ===
|
|
8994
|
+
(_id, attrs) => attrs.type === import_types30.NodeType.ServiceNode && attrs.name === serviceName
|
|
8696
8995
|
);
|
|
8697
8996
|
}
|
|
8698
|
-
async function bootstrapProject(entry) {
|
|
8699
|
-
const paths = pathsForProject(entry.name,
|
|
8997
|
+
async function bootstrapProject(entry, connectors = []) {
|
|
8998
|
+
const paths = pathsForProject(entry.name, import_node_path45.default.join(entry.path, "neat-out"));
|
|
8700
8999
|
try {
|
|
8701
|
-
const stat = await
|
|
9000
|
+
const stat = await import_node_fs26.promises.stat(entry.path);
|
|
8702
9001
|
if (!stat.isDirectory()) {
|
|
8703
9002
|
throw new Error(`registered path ${entry.path} is not a directory`);
|
|
8704
9003
|
}
|
|
@@ -8716,6 +9015,8 @@ async function bootstrapProject(entry) {
|
|
|
8716
9015
|
},
|
|
8717
9016
|
stopStaleness: () => {
|
|
8718
9017
|
},
|
|
9018
|
+
stopConnectors: () => {
|
|
9019
|
+
},
|
|
8719
9020
|
detachEvents: () => {
|
|
8720
9021
|
},
|
|
8721
9022
|
status: "broken",
|
|
@@ -8734,6 +9035,18 @@ async function bootstrapProject(entry) {
|
|
|
8734
9035
|
staleEventsPath: paths.staleEventsPath,
|
|
8735
9036
|
project: entry.name
|
|
8736
9037
|
});
|
|
9038
|
+
const stopFns = connectors.map(
|
|
9039
|
+
(registration) => startConnectorPollLoop(
|
|
9040
|
+
registration.connector,
|
|
9041
|
+
{ projectDir: entry.path, credentials: registration.credentials },
|
|
9042
|
+
graph,
|
|
9043
|
+
registration.resolveTarget,
|
|
9044
|
+
{ intervalMs: registration.intervalMs }
|
|
9045
|
+
)
|
|
9046
|
+
);
|
|
9047
|
+
const stopConnectors = () => {
|
|
9048
|
+
for (const stop of stopFns) stop();
|
|
9049
|
+
};
|
|
8737
9050
|
await touchLastSeen(entry.name).catch(() => {
|
|
8738
9051
|
});
|
|
8739
9052
|
return {
|
|
@@ -8743,6 +9056,7 @@ async function bootstrapProject(entry) {
|
|
|
8743
9056
|
paths,
|
|
8744
9057
|
stopPersist,
|
|
8745
9058
|
stopStaleness,
|
|
9059
|
+
stopConnectors,
|
|
8746
9060
|
detachEvents,
|
|
8747
9061
|
status: "active"
|
|
8748
9062
|
};
|
|
@@ -8799,7 +9113,7 @@ async function startDaemon(opts = {}) {
|
|
|
8799
9113
|
const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
|
|
8800
9114
|
const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
|
|
8801
9115
|
const singleProject = projectArg;
|
|
8802
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
9116
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path45.default.resolve(projectPathArg) : null;
|
|
8803
9117
|
if (singleProject && !singleProjectPath) {
|
|
8804
9118
|
throw new Error(
|
|
8805
9119
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -8807,14 +9121,14 @@ async function startDaemon(opts = {}) {
|
|
|
8807
9121
|
}
|
|
8808
9122
|
if (!singleProject) {
|
|
8809
9123
|
try {
|
|
8810
|
-
await
|
|
9124
|
+
await import_node_fs26.promises.access(regPath);
|
|
8811
9125
|
} catch {
|
|
8812
9126
|
throw new Error(
|
|
8813
9127
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
8814
9128
|
);
|
|
8815
9129
|
}
|
|
8816
9130
|
}
|
|
8817
|
-
const pidPath =
|
|
9131
|
+
const pidPath = import_node_path45.default.join(home, "neatd.pid");
|
|
8818
9132
|
await writeAtomically(pidPath, `${process.pid}
|
|
8819
9133
|
`);
|
|
8820
9134
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -8858,7 +9172,7 @@ async function startDaemon(opts = {}) {
|
|
|
8858
9172
|
}
|
|
8859
9173
|
async function tryRecoverSlot(entry) {
|
|
8860
9174
|
try {
|
|
8861
|
-
const fresh = await bootstrapProject(entry);
|
|
9175
|
+
const fresh = await bootstrapProject(entry, opts.connectors ?? []);
|
|
8862
9176
|
const prior = slots.get(entry.name);
|
|
8863
9177
|
if (prior) teardownSlot(prior);
|
|
8864
9178
|
slots.set(entry.name, fresh);
|
|
@@ -8882,7 +9196,7 @@ async function startDaemon(opts = {}) {
|
|
|
8882
9196
|
bootstrapStatus.set(entry.name, "bootstrapping");
|
|
8883
9197
|
bootstrapStartedAt.set(entry.name, Date.now());
|
|
8884
9198
|
try {
|
|
8885
|
-
const slot = await bootstrapProject(entry);
|
|
9199
|
+
const slot = await bootstrapProject(entry, opts.connectors ?? []);
|
|
8886
9200
|
const prior = slots.get(entry.name);
|
|
8887
9201
|
if (prior) teardownSlot(prior);
|
|
8888
9202
|
slots.set(entry.name, slot);
|
|
@@ -9008,7 +9322,7 @@ async function startDaemon(opts = {}) {
|
|
|
9008
9322
|
}
|
|
9009
9323
|
if (restApp) await restApp.close().catch(() => {
|
|
9010
9324
|
});
|
|
9011
|
-
await
|
|
9325
|
+
await import_node_fs26.promises.unlink(pidPath).catch(() => {
|
|
9012
9326
|
});
|
|
9013
9327
|
throw new Error(
|
|
9014
9328
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -9134,7 +9448,7 @@ async function startDaemon(opts = {}) {
|
|
|
9134
9448
|
});
|
|
9135
9449
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
9136
9450
|
});
|
|
9137
|
-
await
|
|
9451
|
+
await import_node_fs26.promises.unlink(pidPath).catch(() => {
|
|
9138
9452
|
});
|
|
9139
9453
|
throw new Error(
|
|
9140
9454
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -9169,7 +9483,7 @@ async function startDaemon(opts = {}) {
|
|
|
9169
9483
|
});
|
|
9170
9484
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
9171
9485
|
});
|
|
9172
|
-
await
|
|
9486
|
+
await import_node_fs26.promises.unlink(pidPath).catch(() => {
|
|
9173
9487
|
});
|
|
9174
9488
|
throw new Error(
|
|
9175
9489
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -9216,9 +9530,9 @@ async function startDaemon(opts = {}) {
|
|
|
9216
9530
|
let registryWatcher = null;
|
|
9217
9531
|
let reloadTimer = null;
|
|
9218
9532
|
if (!singleProject) try {
|
|
9219
|
-
const regDir =
|
|
9220
|
-
const regBase =
|
|
9221
|
-
registryWatcher = (0,
|
|
9533
|
+
const regDir = import_node_path45.default.dirname(regPath);
|
|
9534
|
+
const regBase = import_node_path45.default.basename(regPath);
|
|
9535
|
+
registryWatcher = (0, import_node_fs26.watch)(regDir, (_eventType, filename) => {
|
|
9222
9536
|
if (filename !== null && filename !== regBase) return;
|
|
9223
9537
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
9224
9538
|
reloadTimer = setTimeout(() => {
|
|
@@ -9267,7 +9581,7 @@ async function startDaemon(opts = {}) {
|
|
|
9267
9581
|
if (daemonRecord) {
|
|
9268
9582
|
await clearDaemonRecord(daemonRecord, home);
|
|
9269
9583
|
}
|
|
9270
|
-
await
|
|
9584
|
+
await import_node_fs26.promises.unlink(pidPath).catch(() => {
|
|
9271
9585
|
});
|
|
9272
9586
|
};
|
|
9273
9587
|
return {
|