@neat.is/core 0.4.26-dev.20260703 → 0.4.26
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 +2 -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/server.cjs
CHANGED
|
@@ -56,9 +56,9 @@ function mountBearerAuth(app, opts) {
|
|
|
56
56
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
57
57
|
const publicRead = opts.publicRead === true;
|
|
58
58
|
app.addHook("preHandler", (req, reply, done) => {
|
|
59
|
-
const
|
|
59
|
+
const path46 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
60
60
|
for (const suffix of suffixes) {
|
|
61
|
-
if (
|
|
61
|
+
if (path46 === suffix || path46.endsWith(suffix)) {
|
|
62
62
|
done();
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
@@ -188,8 +188,8 @@ function reshapeGrpcRequest(req) {
|
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
190
|
function resolveProtoRoot() {
|
|
191
|
-
const here =
|
|
192
|
-
return
|
|
191
|
+
const here = import_node_path42.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
192
|
+
return import_node_path42.default.resolve(here, "..", "proto");
|
|
193
193
|
}
|
|
194
194
|
function loadTraceService() {
|
|
195
195
|
const protoRoot = resolveProtoRoot();
|
|
@@ -257,13 +257,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
257
257
|
})
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
|
-
var import_node_url,
|
|
260
|
+
var import_node_url, import_node_path42, import_node_crypto2, grpc, protoLoader;
|
|
261
261
|
var init_otel_grpc = __esm({
|
|
262
262
|
"src/otel-grpc.ts"() {
|
|
263
263
|
"use strict";
|
|
264
264
|
init_cjs_shims();
|
|
265
265
|
import_node_url = require("url");
|
|
266
|
-
|
|
266
|
+
import_node_path42 = __toESM(require("path"), 1);
|
|
267
267
|
import_node_crypto2 = require("crypto");
|
|
268
268
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
269
269
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -343,6 +343,29 @@ function messagingDestinationOf(attrs) {
|
|
|
343
343
|
}
|
|
344
344
|
return void 0;
|
|
345
345
|
}
|
|
346
|
+
function hasWebsocketUpgradeHeader(attrs) {
|
|
347
|
+
const v = attrs["http.request.header.upgrade"];
|
|
348
|
+
const matches = (s) => typeof s === "string" && s.trim().toLowerCase() === "websocket";
|
|
349
|
+
if (Array.isArray(v)) return v.some(matches);
|
|
350
|
+
return matches(v);
|
|
351
|
+
}
|
|
352
|
+
function websocketChannelPathOf(attrs) {
|
|
353
|
+
const route = attrs["http.route"];
|
|
354
|
+
if (typeof route === "string" && route.length > 0) return route;
|
|
355
|
+
for (const key of ["url.path", "http.target"]) {
|
|
356
|
+
const v = attrs[key];
|
|
357
|
+
if (typeof v === "string" && v.length > 0) {
|
|
358
|
+
const q = v.indexOf("?");
|
|
359
|
+
const path46 = q === -1 ? v : v.slice(0, q);
|
|
360
|
+
if (path46.length > 0) return path46;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return void 0;
|
|
364
|
+
}
|
|
365
|
+
function websocketChannelOf(attrs) {
|
|
366
|
+
if (!hasWebsocketUpgradeHeader(attrs)) return void 0;
|
|
367
|
+
return websocketChannelPathOf(attrs);
|
|
368
|
+
}
|
|
346
369
|
function parseOtlpRequest(body) {
|
|
347
370
|
const out = [];
|
|
348
371
|
for (const rs of body.resourceSpans ?? []) {
|
|
@@ -373,6 +396,10 @@ function parseOtlpRequest(body) {
|
|
|
373
396
|
messagingDestination: messagingDestinationOf(attrs),
|
|
374
397
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
375
398
|
graphqlOperationType: typeof attrs["graphql.operation.type"] === "string" && attrs["graphql.operation.type"].length > 0 ? attrs["graphql.operation.type"] : void 0,
|
|
399
|
+
rpcSystem: typeof attrs["rpc.system"] === "string" && attrs["rpc.system"].length > 0 ? attrs["rpc.system"] : void 0,
|
|
400
|
+
rpcService: typeof attrs["rpc.service"] === "string" && attrs["rpc.service"].length > 0 ? attrs["rpc.service"] : void 0,
|
|
401
|
+
rpcMethod: typeof attrs["rpc.method"] === "string" && attrs["rpc.method"].length > 0 ? attrs["rpc.method"] : void 0,
|
|
402
|
+
websocketChannel: websocketChannelOf(attrs),
|
|
376
403
|
statusCode: span.status?.code,
|
|
377
404
|
errorMessage: span.status?.message,
|
|
378
405
|
exception: extractExceptionFromEvents(span.events)
|
|
@@ -384,10 +411,10 @@ function parseOtlpRequest(body) {
|
|
|
384
411
|
return out;
|
|
385
412
|
}
|
|
386
413
|
function loadProtoRoot() {
|
|
387
|
-
const here =
|
|
388
|
-
const protoRoot =
|
|
414
|
+
const here = import_node_path43.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
415
|
+
const protoRoot = import_node_path43.default.resolve(here, "..", "proto");
|
|
389
416
|
const root = new import_protobufjs.default.Root();
|
|
390
|
-
root.resolvePath = (_origin, target) =>
|
|
417
|
+
root.resolvePath = (_origin, target) => import_node_path43.default.resolve(protoRoot, target);
|
|
391
418
|
root.loadSync(
|
|
392
419
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
393
420
|
{ keepCase: true }
|
|
@@ -599,12 +626,12 @@ async function buildOtelReceiver(opts) {
|
|
|
599
626
|
};
|
|
600
627
|
return decorated;
|
|
601
628
|
}
|
|
602
|
-
var
|
|
629
|
+
var import_node_path43, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
603
630
|
var init_otel = __esm({
|
|
604
631
|
"src/otel.ts"() {
|
|
605
632
|
"use strict";
|
|
606
633
|
init_cjs_shims();
|
|
607
|
-
|
|
634
|
+
import_node_path43 = __toESM(require("path"), 1);
|
|
608
635
|
import_node_url2 = require("url");
|
|
609
636
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
610
637
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -620,7 +647,7 @@ var init_otel = __esm({
|
|
|
620
647
|
|
|
621
648
|
// src/server.ts
|
|
622
649
|
init_cjs_shims();
|
|
623
|
-
var
|
|
650
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
624
651
|
|
|
625
652
|
// src/graph.ts
|
|
626
653
|
init_cjs_shims();
|
|
@@ -644,7 +671,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
644
671
|
init_cjs_shims();
|
|
645
672
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
646
673
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
647
|
-
var
|
|
674
|
+
var import_types29 = require("@neat.is/types");
|
|
648
675
|
|
|
649
676
|
// src/extend/index.ts
|
|
650
677
|
init_cjs_shims();
|
|
@@ -1387,19 +1414,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1387
1414
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1388
1415
|
let best = { path: [start], edges: [] };
|
|
1389
1416
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1390
|
-
function step(node,
|
|
1391
|
-
if (
|
|
1392
|
-
best = { path: [...
|
|
1417
|
+
function step(node, path46, edges) {
|
|
1418
|
+
if (path46.length > best.path.length) {
|
|
1419
|
+
best = { path: [...path46], edges: [...edges] };
|
|
1393
1420
|
}
|
|
1394
|
-
if (
|
|
1421
|
+
if (path46.length - 1 >= maxDepth) return;
|
|
1395
1422
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1396
1423
|
for (const [srcId, edge] of incoming) {
|
|
1397
1424
|
if (visited.has(srcId)) continue;
|
|
1398
1425
|
visited.add(srcId);
|
|
1399
|
-
|
|
1426
|
+
path46.push(srcId);
|
|
1400
1427
|
edges.push(edge);
|
|
1401
|
-
step(srcId,
|
|
1402
|
-
|
|
1428
|
+
step(srcId, path46, edges);
|
|
1429
|
+
path46.pop();
|
|
1403
1430
|
edges.pop();
|
|
1404
1431
|
visited.delete(srcId);
|
|
1405
1432
|
}
|
|
@@ -1593,26 +1620,26 @@ function dominantFailingCall(graph, serviceId4, visited) {
|
|
|
1593
1620
|
return best;
|
|
1594
1621
|
}
|
|
1595
1622
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1596
|
-
const
|
|
1623
|
+
const path46 = [originServiceId];
|
|
1597
1624
|
const edges = [];
|
|
1598
1625
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1599
1626
|
let current = originServiceId;
|
|
1600
1627
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1601
1628
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1602
1629
|
if (!hop) break;
|
|
1603
|
-
|
|
1630
|
+
path46.push(hop.nextService);
|
|
1604
1631
|
edges.push(hop.edge);
|
|
1605
1632
|
visited.add(hop.nextService);
|
|
1606
1633
|
current = hop.nextService;
|
|
1607
1634
|
}
|
|
1608
1635
|
if (edges.length === 0) return null;
|
|
1609
|
-
return { path:
|
|
1636
|
+
return { path: path46, edges, culprit: current };
|
|
1610
1637
|
}
|
|
1611
1638
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1612
1639
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1613
1640
|
if (!chain) return null;
|
|
1614
1641
|
const culprit = chain.culprit;
|
|
1615
|
-
const
|
|
1642
|
+
const path46 = [...chain.path];
|
|
1616
1643
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1617
1644
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1618
1645
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1620,14 +1647,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1620
1647
|
if (loc) {
|
|
1621
1648
|
let rootCauseNode = culprit;
|
|
1622
1649
|
if (loc.fileNode) {
|
|
1623
|
-
|
|
1650
|
+
path46.push(loc.fileNode);
|
|
1624
1651
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1625
1652
|
rootCauseNode = loc.fileNode;
|
|
1626
1653
|
}
|
|
1627
1654
|
return import_types.RootCauseResultSchema.parse({
|
|
1628
1655
|
rootCauseNode,
|
|
1629
1656
|
rootCauseReason: loc.rootCauseReason,
|
|
1630
|
-
traversalPath:
|
|
1657
|
+
traversalPath: path46,
|
|
1631
1658
|
edgeProvenances,
|
|
1632
1659
|
confidence,
|
|
1633
1660
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1639,7 +1666,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1639
1666
|
return import_types.RootCauseResultSchema.parse({
|
|
1640
1667
|
rootCauseNode: culprit,
|
|
1641
1668
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1642
|
-
traversalPath:
|
|
1669
|
+
traversalPath: path46,
|
|
1643
1670
|
edgeProvenances,
|
|
1644
1671
|
confidence,
|
|
1645
1672
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -1819,6 +1846,11 @@ function nodeIsFrontier(graph, nodeId) {
|
|
|
1819
1846
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
1820
1847
|
return attrs.type === import_types2.NodeType.FrontierNode;
|
|
1821
1848
|
}
|
|
1849
|
+
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
1850
|
+
if (!graph.hasNode(nodeId)) return false;
|
|
1851
|
+
const attrs = graph.getNodeAttributes(nodeId);
|
|
1852
|
+
return attrs.type === import_types2.NodeType.WebSocketChannelNode;
|
|
1853
|
+
}
|
|
1822
1854
|
function clampConfidence(n) {
|
|
1823
1855
|
if (!Number.isFinite(n)) return 0;
|
|
1824
1856
|
return Math.max(0, Math.min(1, n));
|
|
@@ -1859,7 +1891,7 @@ function detectMissingDivergences(graph, bucket) {
|
|
|
1859
1891
|
});
|
|
1860
1892
|
}
|
|
1861
1893
|
}
|
|
1862
|
-
if (bucket.observed && !bucket.extracted) {
|
|
1894
|
+
if (bucket.observed && !bucket.extracted && !nodeIsWebsocketChannel(graph, bucket.target)) {
|
|
1863
1895
|
out.push({
|
|
1864
1896
|
type: "missing-extracted",
|
|
1865
1897
|
source: bucket.source,
|
|
@@ -2877,6 +2909,40 @@ function ensureGraphqlOperationNode(graph, serviceName, operationType, operation
|
|
|
2877
2909
|
graph.addNode(id, node);
|
|
2878
2910
|
return id;
|
|
2879
2911
|
}
|
|
2912
|
+
function spanServesGrpcMethod(kind) {
|
|
2913
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2914
|
+
}
|
|
2915
|
+
function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
|
|
2916
|
+
const id = (0, import_types4.grpcMethodId)(rpcService, rpcMethod);
|
|
2917
|
+
if (graph.hasNode(id)) return id;
|
|
2918
|
+
const node = {
|
|
2919
|
+
id,
|
|
2920
|
+
type: import_types4.NodeType.GrpcMethodNode,
|
|
2921
|
+
name: `${rpcService}/${rpcMethod}`,
|
|
2922
|
+
rpcService,
|
|
2923
|
+
rpcMethod,
|
|
2924
|
+
discoveredVia: "otel"
|
|
2925
|
+
};
|
|
2926
|
+
graph.addNode(id, node);
|
|
2927
|
+
return id;
|
|
2928
|
+
}
|
|
2929
|
+
function spanServesWebsocketChannel(kind) {
|
|
2930
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2931
|
+
}
|
|
2932
|
+
function ensureWebsocketChannelNode(graph, serviceName, channel) {
|
|
2933
|
+
const id = (0, import_types4.websocketChannelId)(serviceName, channel);
|
|
2934
|
+
if (graph.hasNode(id)) return id;
|
|
2935
|
+
const node = {
|
|
2936
|
+
id,
|
|
2937
|
+
type: import_types4.NodeType.WebSocketChannelNode,
|
|
2938
|
+
name: channel,
|
|
2939
|
+
service: serviceName,
|
|
2940
|
+
channel,
|
|
2941
|
+
discoveredVia: "otel"
|
|
2942
|
+
};
|
|
2943
|
+
graph.addNode(id, node);
|
|
2944
|
+
return id;
|
|
2945
|
+
}
|
|
2880
2946
|
function messagingDestinationKind(system) {
|
|
2881
2947
|
return `${system}-topic`;
|
|
2882
2948
|
}
|
|
@@ -3287,6 +3353,34 @@ async function handleSpan(ctx, span) {
|
|
|
3287
3353
|
callSiteEvidence
|
|
3288
3354
|
);
|
|
3289
3355
|
if (result) affectedNode = targetId;
|
|
3356
|
+
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
3357
|
+
const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
|
|
3358
|
+
const result = upsertObservedEdge(
|
|
3359
|
+
ctx.graph,
|
|
3360
|
+
import_types4.EdgeType.CONTAINS,
|
|
3361
|
+
observedSource(),
|
|
3362
|
+
targetId,
|
|
3363
|
+
ts,
|
|
3364
|
+
isError,
|
|
3365
|
+
callSiteEvidence
|
|
3366
|
+
);
|
|
3367
|
+
if (result) affectedNode = targetId;
|
|
3368
|
+
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
3369
|
+
const targetId = ensureWebsocketChannelNode(
|
|
3370
|
+
ctx.graph,
|
|
3371
|
+
span.service,
|
|
3372
|
+
span.websocketChannel
|
|
3373
|
+
);
|
|
3374
|
+
const result = upsertObservedEdge(
|
|
3375
|
+
ctx.graph,
|
|
3376
|
+
import_types4.EdgeType.CONNECTS_TO,
|
|
3377
|
+
observedSource(),
|
|
3378
|
+
targetId,
|
|
3379
|
+
ts,
|
|
3380
|
+
isError,
|
|
3381
|
+
callSiteEvidence
|
|
3382
|
+
);
|
|
3383
|
+
if (result) affectedNode = targetId;
|
|
3290
3384
|
} else {
|
|
3291
3385
|
const host = pickAddress(span);
|
|
3292
3386
|
let resolvedViaAddress = false;
|
|
@@ -5857,17 +5951,138 @@ async function addRoutes(graph, services) {
|
|
|
5857
5951
|
return { nodesAdded, edgesAdded };
|
|
5858
5952
|
}
|
|
5859
5953
|
|
|
5954
|
+
// src/extract/proto.ts
|
|
5955
|
+
init_cjs_shims();
|
|
5956
|
+
var import_node_fs17 = require("fs");
|
|
5957
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
5958
|
+
var import_types13 = require("@neat.is/types");
|
|
5959
|
+
var PROTO_EXTENSION = ".proto";
|
|
5960
|
+
function packageOf(content) {
|
|
5961
|
+
const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
|
|
5962
|
+
return m ? m[1] : null;
|
|
5963
|
+
}
|
|
5964
|
+
function lineAt(content, offset) {
|
|
5965
|
+
return content.slice(0, offset).split("\n").length;
|
|
5966
|
+
}
|
|
5967
|
+
function grpcMethodsFromProto(content, fqPackage) {
|
|
5968
|
+
const out = [];
|
|
5969
|
+
const serviceRe = /\bservice\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/g;
|
|
5970
|
+
let sm;
|
|
5971
|
+
while ((sm = serviceRe.exec(content)) !== null) {
|
|
5972
|
+
const serviceName = sm[1];
|
|
5973
|
+
const rpcService = fqPackage ? `${fqPackage}.${serviceName}` : serviceName;
|
|
5974
|
+
const bodyStart = serviceRe.lastIndex;
|
|
5975
|
+
let depth = 1;
|
|
5976
|
+
let i = bodyStart;
|
|
5977
|
+
for (; i < content.length && depth > 0; i++) {
|
|
5978
|
+
const ch = content[i];
|
|
5979
|
+
if (ch === "{") depth++;
|
|
5980
|
+
else if (ch === "}") depth--;
|
|
5981
|
+
}
|
|
5982
|
+
const body = content.slice(bodyStart, i - 1);
|
|
5983
|
+
const rpcRe = /\brpc\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
5984
|
+
let rm;
|
|
5985
|
+
while ((rm = rpcRe.exec(body)) !== null) {
|
|
5986
|
+
const rpcMethod = rm[1];
|
|
5987
|
+
const line = lineAt(content, bodyStart + rm.index);
|
|
5988
|
+
out.push({ rpcService, rpcMethod, line });
|
|
5989
|
+
}
|
|
5990
|
+
serviceRe.lastIndex = i;
|
|
5991
|
+
}
|
|
5992
|
+
return out;
|
|
5993
|
+
}
|
|
5994
|
+
async function walkProtoFiles(dir) {
|
|
5995
|
+
const out = [];
|
|
5996
|
+
async function walk3(current) {
|
|
5997
|
+
const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
5998
|
+
for (const entry of entries) {
|
|
5999
|
+
const full = import_node_path25.default.join(current, entry.name);
|
|
6000
|
+
if (entry.isDirectory()) {
|
|
6001
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6002
|
+
if (await isPythonVenvDir(full)) continue;
|
|
6003
|
+
await walk3(full);
|
|
6004
|
+
} else if (entry.isFile() && import_node_path25.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
6005
|
+
out.push(full);
|
|
6006
|
+
}
|
|
6007
|
+
}
|
|
6008
|
+
}
|
|
6009
|
+
await walk3(dir);
|
|
6010
|
+
return out;
|
|
6011
|
+
}
|
|
6012
|
+
async function addGrpcMethods(graph, services) {
|
|
6013
|
+
let nodesAdded = 0;
|
|
6014
|
+
let edgesAdded = 0;
|
|
6015
|
+
for (const service of services) {
|
|
6016
|
+
const protoPaths = await walkProtoFiles(service.dir);
|
|
6017
|
+
for (const protoPath of protoPaths) {
|
|
6018
|
+
if (isTestPath(protoPath)) continue;
|
|
6019
|
+
const relFile = toPosix2(import_node_path25.default.relative(service.dir, protoPath));
|
|
6020
|
+
let content;
|
|
6021
|
+
try {
|
|
6022
|
+
content = await import_node_fs17.promises.readFile(protoPath, "utf8");
|
|
6023
|
+
} catch (err) {
|
|
6024
|
+
recordExtractionError("proto extraction", protoPath, err);
|
|
6025
|
+
continue;
|
|
6026
|
+
}
|
|
6027
|
+
let methods;
|
|
6028
|
+
try {
|
|
6029
|
+
methods = grpcMethodsFromProto(content, packageOf(content));
|
|
6030
|
+
} catch (err) {
|
|
6031
|
+
recordExtractionError("proto extraction", protoPath, err);
|
|
6032
|
+
continue;
|
|
6033
|
+
}
|
|
6034
|
+
if (methods.length === 0) continue;
|
|
6035
|
+
for (const method of methods) {
|
|
6036
|
+
const mid = (0, import_types13.grpcMethodId)(method.rpcService, method.rpcMethod);
|
|
6037
|
+
if (!graph.hasNode(mid)) {
|
|
6038
|
+
const node = {
|
|
6039
|
+
id: mid,
|
|
6040
|
+
type: import_types13.NodeType.GrpcMethodNode,
|
|
6041
|
+
name: `${method.rpcService}/${method.rpcMethod}`,
|
|
6042
|
+
rpcService: method.rpcService,
|
|
6043
|
+
rpcMethod: method.rpcMethod,
|
|
6044
|
+
path: relFile,
|
|
6045
|
+
line: method.line,
|
|
6046
|
+
discoveredVia: "static"
|
|
6047
|
+
};
|
|
6048
|
+
graph.addNode(mid, node);
|
|
6049
|
+
nodesAdded++;
|
|
6050
|
+
}
|
|
6051
|
+
const containsId = (0, import_types13.extractedEdgeId)(service.node.id, mid, import_types13.EdgeType.CONTAINS);
|
|
6052
|
+
if (!graph.hasEdge(containsId)) {
|
|
6053
|
+
const edge = {
|
|
6054
|
+
id: containsId,
|
|
6055
|
+
source: service.node.id,
|
|
6056
|
+
target: mid,
|
|
6057
|
+
type: import_types13.EdgeType.CONTAINS,
|
|
6058
|
+
provenance: import_types13.Provenance.EXTRACTED,
|
|
6059
|
+
confidence: (0, import_types13.confidenceForExtracted)("structural"),
|
|
6060
|
+
evidence: {
|
|
6061
|
+
file: relFile,
|
|
6062
|
+
line: method.line,
|
|
6063
|
+
snippet: snippet(content, method.line)
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
graph.addEdgeWithKey(containsId, service.node.id, mid, edge);
|
|
6067
|
+
edgesAdded++;
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
return { nodesAdded, edgesAdded };
|
|
6073
|
+
}
|
|
6074
|
+
|
|
5860
6075
|
// src/extract/calls/index.ts
|
|
5861
6076
|
init_cjs_shims();
|
|
5862
|
-
var
|
|
6077
|
+
var import_types21 = require("@neat.is/types");
|
|
5863
6078
|
|
|
5864
6079
|
// src/extract/calls/http.ts
|
|
5865
6080
|
init_cjs_shims();
|
|
5866
|
-
var
|
|
6081
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
5867
6082
|
var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
5868
6083
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5869
6084
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
5870
|
-
var
|
|
6085
|
+
var import_types14 = require("@neat.is/types");
|
|
5871
6086
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
5872
6087
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
5873
6088
|
function isInsideJsxExternalLink(node) {
|
|
@@ -5937,7 +6152,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5937
6152
|
const seen = /* @__PURE__ */ new Set();
|
|
5938
6153
|
for (const file of files) {
|
|
5939
6154
|
if (isTestPath(file.path)) continue;
|
|
5940
|
-
const parser =
|
|
6155
|
+
const parser = import_node_path26.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
5941
6156
|
let sites;
|
|
5942
6157
|
try {
|
|
5943
6158
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -5946,14 +6161,14 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5946
6161
|
continue;
|
|
5947
6162
|
}
|
|
5948
6163
|
if (sites.length === 0) continue;
|
|
5949
|
-
const relFile = toPosix2(
|
|
6164
|
+
const relFile = toPosix2(import_node_path26.default.relative(service.dir, file.path));
|
|
5950
6165
|
for (const site of sites) {
|
|
5951
6166
|
const targetId = hostToNodeId.get(site.host);
|
|
5952
6167
|
if (!targetId || targetId === service.node.id) continue;
|
|
5953
6168
|
const dedupKey = `${relFile}|${targetId}`;
|
|
5954
6169
|
if (seen.has(dedupKey)) continue;
|
|
5955
6170
|
seen.add(dedupKey);
|
|
5956
|
-
const confidence = (0,
|
|
6171
|
+
const confidence = (0, import_types14.confidenceForExtracted)("url-literal-service-target");
|
|
5957
6172
|
const ev = {
|
|
5958
6173
|
file: relFile,
|
|
5959
6174
|
line: site.line,
|
|
@@ -5967,25 +6182,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5967
6182
|
);
|
|
5968
6183
|
nodesAdded += n;
|
|
5969
6184
|
edgesAdded += e;
|
|
5970
|
-
if (!(0,
|
|
6185
|
+
if (!(0, import_types14.passesExtractedFloor)(confidence)) {
|
|
5971
6186
|
noteExtractedDropped({
|
|
5972
6187
|
source: fileNodeId,
|
|
5973
6188
|
target: targetId,
|
|
5974
|
-
type:
|
|
6189
|
+
type: import_types14.EdgeType.CALLS,
|
|
5975
6190
|
confidence,
|
|
5976
6191
|
confidenceKind: "url-literal-service-target",
|
|
5977
6192
|
evidence: ev
|
|
5978
6193
|
});
|
|
5979
6194
|
continue;
|
|
5980
6195
|
}
|
|
5981
|
-
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, targetId,
|
|
6196
|
+
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, targetId, import_types14.EdgeType.CALLS);
|
|
5982
6197
|
if (!graph.hasEdge(edgeId)) {
|
|
5983
6198
|
const edge = {
|
|
5984
6199
|
id: edgeId,
|
|
5985
6200
|
source: fileNodeId,
|
|
5986
6201
|
target: targetId,
|
|
5987
|
-
type:
|
|
5988
|
-
provenance:
|
|
6202
|
+
type: import_types14.EdgeType.CALLS,
|
|
6203
|
+
provenance: import_types14.Provenance.EXTRACTED,
|
|
5989
6204
|
confidence,
|
|
5990
6205
|
evidence: ev
|
|
5991
6206
|
};
|
|
@@ -6000,10 +6215,10 @@ async function addHttpCallEdges(graph, services) {
|
|
|
6000
6215
|
|
|
6001
6216
|
// src/extract/calls/route-match.ts
|
|
6002
6217
|
init_cjs_shims();
|
|
6003
|
-
var
|
|
6218
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
6004
6219
|
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
6005
6220
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
6006
|
-
var
|
|
6221
|
+
var import_types15 = require("@neat.is/types");
|
|
6007
6222
|
var PARSE_CHUNK4 = 16384;
|
|
6008
6223
|
function parseSource4(parser, source) {
|
|
6009
6224
|
return parser.parse(
|
|
@@ -6168,9 +6383,9 @@ function buildRouteIndex(graph) {
|
|
|
6168
6383
|
const index = /* @__PURE__ */ new Map();
|
|
6169
6384
|
graph.forEachNode((_id, attrs) => {
|
|
6170
6385
|
const node = attrs;
|
|
6171
|
-
if (node.type !==
|
|
6386
|
+
if (node.type !== import_types15.NodeType.RouteNode) return;
|
|
6172
6387
|
const route = attrs;
|
|
6173
|
-
const owner = (0,
|
|
6388
|
+
const owner = (0, import_types15.serviceId)(route.service);
|
|
6174
6389
|
const entry = {
|
|
6175
6390
|
method: route.method.toUpperCase(),
|
|
6176
6391
|
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
@@ -6199,7 +6414,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
6199
6414
|
const seen = /* @__PURE__ */ new Set();
|
|
6200
6415
|
for (const file of files) {
|
|
6201
6416
|
if (isTestPath(file.path)) continue;
|
|
6202
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
6417
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path27.default.extname(file.path))) continue;
|
|
6203
6418
|
let sites;
|
|
6204
6419
|
try {
|
|
6205
6420
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -6208,7 +6423,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
6208
6423
|
continue;
|
|
6209
6424
|
}
|
|
6210
6425
|
if (sites.length === 0) continue;
|
|
6211
|
-
const relFile = toPosix2(
|
|
6426
|
+
const relFile = toPosix2(import_node_path27.default.relative(service.dir, file.path));
|
|
6212
6427
|
for (const site of sites) {
|
|
6213
6428
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
6214
6429
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -6228,7 +6443,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
6228
6443
|
);
|
|
6229
6444
|
nodesAdded += n;
|
|
6230
6445
|
edgesAdded += e;
|
|
6231
|
-
const confidence = (0,
|
|
6446
|
+
const confidence = (0, import_types15.confidenceForExtracted)("verified-call-site");
|
|
6232
6447
|
const ev = {
|
|
6233
6448
|
file: relFile,
|
|
6234
6449
|
line: site.line,
|
|
@@ -6236,25 +6451,25 @@ async function addRouteCallEdges(graph, services) {
|
|
|
6236
6451
|
method: site.method ?? match.method,
|
|
6237
6452
|
pathTemplate: site.pathTemplate
|
|
6238
6453
|
};
|
|
6239
|
-
if (!(0,
|
|
6454
|
+
if (!(0, import_types15.passesExtractedFloor)(confidence)) {
|
|
6240
6455
|
noteExtractedDropped({
|
|
6241
6456
|
source: fileNodeId,
|
|
6242
6457
|
target: match.routeNodeId,
|
|
6243
|
-
type:
|
|
6458
|
+
type: import_types15.EdgeType.CALLS,
|
|
6244
6459
|
confidence,
|
|
6245
6460
|
confidenceKind: "verified-call-site",
|
|
6246
6461
|
evidence: ev
|
|
6247
6462
|
});
|
|
6248
6463
|
continue;
|
|
6249
6464
|
}
|
|
6250
|
-
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, match.routeNodeId,
|
|
6465
|
+
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types15.EdgeType.CALLS);
|
|
6251
6466
|
if (!graph.hasEdge(edgeId)) {
|
|
6252
6467
|
const edge = {
|
|
6253
6468
|
id: edgeId,
|
|
6254
6469
|
source: fileNodeId,
|
|
6255
6470
|
target: match.routeNodeId,
|
|
6256
|
-
type:
|
|
6257
|
-
provenance:
|
|
6471
|
+
type: import_types15.EdgeType.CALLS,
|
|
6472
|
+
provenance: import_types15.Provenance.EXTRACTED,
|
|
6258
6473
|
confidence,
|
|
6259
6474
|
evidence: ev
|
|
6260
6475
|
};
|
|
@@ -6269,8 +6484,8 @@ async function addRouteCallEdges(graph, services) {
|
|
|
6269
6484
|
|
|
6270
6485
|
// src/extract/calls/kafka.ts
|
|
6271
6486
|
init_cjs_shims();
|
|
6272
|
-
var
|
|
6273
|
-
var
|
|
6487
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
6488
|
+
var import_types16 = require("@neat.is/types");
|
|
6274
6489
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
6275
6490
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
6276
6491
|
function findAll(re, text) {
|
|
@@ -6291,7 +6506,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
6291
6506
|
seen.add(key);
|
|
6292
6507
|
const line = lineOf(file.content, topic);
|
|
6293
6508
|
out.push({
|
|
6294
|
-
infraId: (0,
|
|
6509
|
+
infraId: (0, import_types16.infraId)("kafka-topic", topic),
|
|
6295
6510
|
name: topic,
|
|
6296
6511
|
kind: "kafka-topic",
|
|
6297
6512
|
edgeType,
|
|
@@ -6300,7 +6515,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
6300
6515
|
// tier (ADR-066).
|
|
6301
6516
|
confidenceKind: "verified-call-site",
|
|
6302
6517
|
evidence: {
|
|
6303
|
-
file:
|
|
6518
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
6304
6519
|
line,
|
|
6305
6520
|
snippet: snippet(file.content, line)
|
|
6306
6521
|
}
|
|
@@ -6313,8 +6528,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
6313
6528
|
|
|
6314
6529
|
// src/extract/calls/redis.ts
|
|
6315
6530
|
init_cjs_shims();
|
|
6316
|
-
var
|
|
6317
|
-
var
|
|
6531
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
6532
|
+
var import_types17 = require("@neat.is/types");
|
|
6318
6533
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
6319
6534
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
6320
6535
|
const out = [];
|
|
@@ -6327,7 +6542,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
6327
6542
|
seen.add(host);
|
|
6328
6543
|
const line = lineOf(file.content, host);
|
|
6329
6544
|
out.push({
|
|
6330
|
-
infraId: (0,
|
|
6545
|
+
infraId: (0, import_types17.infraId)("redis", host),
|
|
6331
6546
|
name: host,
|
|
6332
6547
|
kind: "redis",
|
|
6333
6548
|
edgeType: "CALLS",
|
|
@@ -6336,7 +6551,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
6336
6551
|
// support tier (ADR-066).
|
|
6337
6552
|
confidenceKind: "url-with-structural-support",
|
|
6338
6553
|
evidence: {
|
|
6339
|
-
file:
|
|
6554
|
+
file: import_node_path29.default.relative(serviceDir, file.path),
|
|
6340
6555
|
line,
|
|
6341
6556
|
snippet: snippet(file.content, line)
|
|
6342
6557
|
}
|
|
@@ -6347,8 +6562,8 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
6347
6562
|
|
|
6348
6563
|
// src/extract/calls/aws.ts
|
|
6349
6564
|
init_cjs_shims();
|
|
6350
|
-
var
|
|
6351
|
-
var
|
|
6565
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
6566
|
+
var import_types18 = require("@neat.is/types");
|
|
6352
6567
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
6353
6568
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
6354
6569
|
function hasMarker(text, markers) {
|
|
@@ -6372,7 +6587,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
6372
6587
|
seen.add(key);
|
|
6373
6588
|
const line = lineOf(file.content, name);
|
|
6374
6589
|
out.push({
|
|
6375
|
-
infraId: (0,
|
|
6590
|
+
infraId: (0, import_types18.infraId)(kind, name),
|
|
6376
6591
|
name,
|
|
6377
6592
|
kind,
|
|
6378
6593
|
edgeType: "CALLS",
|
|
@@ -6381,7 +6596,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
6381
6596
|
// (ADR-066).
|
|
6382
6597
|
confidenceKind: "verified-call-site",
|
|
6383
6598
|
evidence: {
|
|
6384
|
-
file:
|
|
6599
|
+
file: import_node_path30.default.relative(serviceDir, file.path),
|
|
6385
6600
|
line,
|
|
6386
6601
|
snippet: snippet(file.content, line)
|
|
6387
6602
|
}
|
|
@@ -6406,8 +6621,8 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
6406
6621
|
|
|
6407
6622
|
// src/extract/calls/grpc.ts
|
|
6408
6623
|
init_cjs_shims();
|
|
6409
|
-
var
|
|
6410
|
-
var
|
|
6624
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6625
|
+
var import_types19 = require("@neat.is/types");
|
|
6411
6626
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
6412
6627
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
6413
6628
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -6456,7 +6671,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6456
6671
|
const { kind } = classified;
|
|
6457
6672
|
const line = lineOf(file.content, m[0]);
|
|
6458
6673
|
out.push({
|
|
6459
|
-
infraId: (0,
|
|
6674
|
+
infraId: (0, import_types19.infraId)(kind, name),
|
|
6460
6675
|
name,
|
|
6461
6676
|
kind,
|
|
6462
6677
|
edgeType: "CALLS",
|
|
@@ -6465,7 +6680,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6465
6680
|
// tier (ADR-066).
|
|
6466
6681
|
confidenceKind: "verified-call-site",
|
|
6467
6682
|
evidence: {
|
|
6468
|
-
file:
|
|
6683
|
+
file: import_node_path31.default.relative(serviceDir, file.path),
|
|
6469
6684
|
line,
|
|
6470
6685
|
snippet: snippet(file.content, line)
|
|
6471
6686
|
}
|
|
@@ -6476,8 +6691,8 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6476
6691
|
|
|
6477
6692
|
// src/extract/calls/supabase.ts
|
|
6478
6693
|
init_cjs_shims();
|
|
6479
|
-
var
|
|
6480
|
-
var
|
|
6694
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6695
|
+
var import_types20 = require("@neat.is/types");
|
|
6481
6696
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
6482
6697
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
6483
6698
|
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
@@ -6515,7 +6730,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6515
6730
|
seen.add(name);
|
|
6516
6731
|
const line = lineOf(file.content, m[0]);
|
|
6517
6732
|
out.push({
|
|
6518
|
-
infraId: (0,
|
|
6733
|
+
infraId: (0, import_types20.infraId)("supabase", name),
|
|
6519
6734
|
name,
|
|
6520
6735
|
kind: "supabase",
|
|
6521
6736
|
edgeType: "CALLS",
|
|
@@ -6525,7 +6740,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6525
6740
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
6526
6741
|
confidenceKind: "verified-call-site",
|
|
6527
6742
|
evidence: {
|
|
6528
|
-
file:
|
|
6743
|
+
file: import_node_path32.default.relative(serviceDir, file.path),
|
|
6529
6744
|
line,
|
|
6530
6745
|
snippet: snippet(file.content, line)
|
|
6531
6746
|
}
|
|
@@ -6538,11 +6753,11 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6538
6753
|
function edgeTypeFromEndpoint(ep) {
|
|
6539
6754
|
switch (ep.edgeType) {
|
|
6540
6755
|
case "PUBLISHES_TO":
|
|
6541
|
-
return
|
|
6756
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6542
6757
|
case "CONSUMES_FROM":
|
|
6543
|
-
return
|
|
6758
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6544
6759
|
default:
|
|
6545
|
-
return
|
|
6760
|
+
return import_types21.EdgeType.CALLS;
|
|
6546
6761
|
}
|
|
6547
6762
|
}
|
|
6548
6763
|
function isAwsKind(kind) {
|
|
@@ -6570,7 +6785,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6570
6785
|
if (!graph.hasNode(ep.infraId)) {
|
|
6571
6786
|
const node = {
|
|
6572
6787
|
id: ep.infraId,
|
|
6573
|
-
type:
|
|
6788
|
+
type: import_types21.NodeType.InfraNode,
|
|
6574
6789
|
name: ep.name,
|
|
6575
6790
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6576
6791
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6582,7 +6797,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6582
6797
|
nodesAdded++;
|
|
6583
6798
|
}
|
|
6584
6799
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6585
|
-
const confidence = (0,
|
|
6800
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6586
6801
|
const relFile = toPosix2(ep.evidence.file);
|
|
6587
6802
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6588
6803
|
graph,
|
|
@@ -6592,7 +6807,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6592
6807
|
);
|
|
6593
6808
|
nodesAdded += n;
|
|
6594
6809
|
edgesAdded += e;
|
|
6595
|
-
if (!(0,
|
|
6810
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6596
6811
|
noteExtractedDropped({
|
|
6597
6812
|
source: fileNodeId,
|
|
6598
6813
|
target: ep.infraId,
|
|
@@ -6612,7 +6827,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6612
6827
|
source: fileNodeId,
|
|
6613
6828
|
target: ep.infraId,
|
|
6614
6829
|
type: edgeType,
|
|
6615
|
-
provenance:
|
|
6830
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6616
6831
|
confidence,
|
|
6617
6832
|
evidence: ep.evidence
|
|
6618
6833
|
};
|
|
@@ -6638,16 +6853,16 @@ init_cjs_shims();
|
|
|
6638
6853
|
|
|
6639
6854
|
// src/extract/infra/docker-compose.ts
|
|
6640
6855
|
init_cjs_shims();
|
|
6641
|
-
var
|
|
6642
|
-
var
|
|
6856
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6857
|
+
var import_types23 = require("@neat.is/types");
|
|
6643
6858
|
|
|
6644
6859
|
// src/extract/infra/shared.ts
|
|
6645
6860
|
init_cjs_shims();
|
|
6646
|
-
var
|
|
6861
|
+
var import_types22 = require("@neat.is/types");
|
|
6647
6862
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6648
6863
|
return {
|
|
6649
|
-
id: (0,
|
|
6650
|
-
type:
|
|
6864
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6865
|
+
type: import_types22.NodeType.InfraNode,
|
|
6651
6866
|
name,
|
|
6652
6867
|
provider,
|
|
6653
6868
|
kind,
|
|
@@ -6676,7 +6891,7 @@ function dependsOnList(value) {
|
|
|
6676
6891
|
}
|
|
6677
6892
|
function serviceNameToServiceNode(name, services) {
|
|
6678
6893
|
for (const s of services) {
|
|
6679
|
-
if (s.node.name === name ||
|
|
6894
|
+
if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
|
|
6680
6895
|
}
|
|
6681
6896
|
return null;
|
|
6682
6897
|
}
|
|
@@ -6685,7 +6900,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6685
6900
|
let edgesAdded = 0;
|
|
6686
6901
|
let composePath = null;
|
|
6687
6902
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6688
|
-
const abs =
|
|
6903
|
+
const abs = import_node_path33.default.join(scanPath, name);
|
|
6689
6904
|
if (await exists2(abs)) {
|
|
6690
6905
|
composePath = abs;
|
|
6691
6906
|
break;
|
|
@@ -6698,13 +6913,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6698
6913
|
} catch (err) {
|
|
6699
6914
|
recordExtractionError(
|
|
6700
6915
|
"infra docker-compose",
|
|
6701
|
-
|
|
6916
|
+
import_node_path33.default.relative(scanPath, composePath),
|
|
6702
6917
|
err
|
|
6703
6918
|
);
|
|
6704
6919
|
return { nodesAdded, edgesAdded };
|
|
6705
6920
|
}
|
|
6706
6921
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6707
|
-
const evidenceFile =
|
|
6922
|
+
const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
|
|
6708
6923
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6709
6924
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6710
6925
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6726,15 +6941,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6726
6941
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6727
6942
|
const targetId = composeNameToNodeId.get(dep);
|
|
6728
6943
|
if (!targetId) continue;
|
|
6729
|
-
const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId,
|
|
6944
|
+
const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6730
6945
|
if (graph.hasEdge(edgeId)) continue;
|
|
6731
6946
|
const edge = {
|
|
6732
6947
|
id: edgeId,
|
|
6733
6948
|
source: sourceId,
|
|
6734
6949
|
target: targetId,
|
|
6735
|
-
type:
|
|
6736
|
-
provenance:
|
|
6737
|
-
confidence: (0,
|
|
6950
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
6951
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6952
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6738
6953
|
evidence: { file: evidenceFile }
|
|
6739
6954
|
};
|
|
6740
6955
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6746,9 +6961,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6746
6961
|
|
|
6747
6962
|
// src/extract/infra/dockerfile.ts
|
|
6748
6963
|
init_cjs_shims();
|
|
6749
|
-
var
|
|
6750
|
-
var
|
|
6751
|
-
var
|
|
6964
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
6965
|
+
var import_node_fs18 = require("fs");
|
|
6966
|
+
var import_types24 = require("@neat.is/types");
|
|
6752
6967
|
function readDockerfile(content) {
|
|
6753
6968
|
let image = null;
|
|
6754
6969
|
const ports = [];
|
|
@@ -6777,15 +6992,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6777
6992
|
let nodesAdded = 0;
|
|
6778
6993
|
let edgesAdded = 0;
|
|
6779
6994
|
for (const service of services) {
|
|
6780
|
-
const dockerfilePath =
|
|
6995
|
+
const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
|
|
6781
6996
|
if (!await exists2(dockerfilePath)) continue;
|
|
6782
6997
|
let content;
|
|
6783
6998
|
try {
|
|
6784
|
-
content = await
|
|
6999
|
+
content = await import_node_fs18.promises.readFile(dockerfilePath, "utf8");
|
|
6785
7000
|
} catch (err) {
|
|
6786
7001
|
recordExtractionError(
|
|
6787
7002
|
"infra dockerfile",
|
|
6788
|
-
|
|
7003
|
+
import_node_path34.default.relative(scanPath, dockerfilePath),
|
|
6789
7004
|
err
|
|
6790
7005
|
);
|
|
6791
7006
|
continue;
|
|
@@ -6797,8 +7012,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6797
7012
|
graph.addNode(node.id, node);
|
|
6798
7013
|
nodesAdded++;
|
|
6799
7014
|
}
|
|
6800
|
-
const relDockerfile = toPosix2(
|
|
6801
|
-
const evidenceFile = toPosix2(
|
|
7015
|
+
const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
|
|
7016
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
|
|
6802
7017
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6803
7018
|
graph,
|
|
6804
7019
|
service.pkg.name,
|
|
@@ -6807,15 +7022,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6807
7022
|
);
|
|
6808
7023
|
nodesAdded += fn;
|
|
6809
7024
|
edgesAdded += fe;
|
|
6810
|
-
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id,
|
|
7025
|
+
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6811
7026
|
if (!graph.hasEdge(edgeId)) {
|
|
6812
7027
|
const edge = {
|
|
6813
7028
|
id: edgeId,
|
|
6814
7029
|
source: fileNodeId,
|
|
6815
7030
|
target: node.id,
|
|
6816
|
-
type:
|
|
6817
|
-
provenance:
|
|
6818
|
-
confidence: (0,
|
|
7031
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7032
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7033
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6819
7034
|
evidence: {
|
|
6820
7035
|
file: evidenceFile,
|
|
6821
7036
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6830,15 +7045,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6830
7045
|
graph.addNode(portNode.id, portNode);
|
|
6831
7046
|
nodesAdded++;
|
|
6832
7047
|
}
|
|
6833
|
-
const portEdgeId = (0, import_types5.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7048
|
+
const portEdgeId = (0, import_types5.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6834
7049
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6835
7050
|
const portEdge = {
|
|
6836
7051
|
id: portEdgeId,
|
|
6837
7052
|
source: fileNodeId,
|
|
6838
7053
|
target: portNode.id,
|
|
6839
|
-
type:
|
|
6840
|
-
provenance:
|
|
6841
|
-
confidence: (0,
|
|
7054
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7055
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7056
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6842
7057
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6843
7058
|
};
|
|
6844
7059
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6850,23 +7065,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6850
7065
|
|
|
6851
7066
|
// src/extract/infra/terraform.ts
|
|
6852
7067
|
init_cjs_shims();
|
|
6853
|
-
var
|
|
6854
|
-
var
|
|
6855
|
-
var
|
|
7068
|
+
var import_node_fs19 = require("fs");
|
|
7069
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7070
|
+
var import_types25 = require("@neat.is/types");
|
|
6856
7071
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6857
7072
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6858
7073
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
6859
7074
|
if (depth > max) return [];
|
|
6860
7075
|
const out = [];
|
|
6861
|
-
const entries = await
|
|
7076
|
+
const entries = await import_node_fs19.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6862
7077
|
for (const entry of entries) {
|
|
6863
7078
|
if (entry.isDirectory()) {
|
|
6864
7079
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
6865
|
-
const child =
|
|
7080
|
+
const child = import_node_path35.default.join(start, entry.name);
|
|
6866
7081
|
if (await isPythonVenvDir(child)) continue;
|
|
6867
7082
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6868
7083
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
6869
|
-
out.push(
|
|
7084
|
+
out.push(import_node_path35.default.join(start, entry.name));
|
|
6870
7085
|
}
|
|
6871
7086
|
}
|
|
6872
7087
|
return out;
|
|
@@ -6885,7 +7100,7 @@ function blockBody(content, from) {
|
|
|
6885
7100
|
}
|
|
6886
7101
|
return null;
|
|
6887
7102
|
}
|
|
6888
|
-
function
|
|
7103
|
+
function lineAt2(content, index) {
|
|
6889
7104
|
let line = 1;
|
|
6890
7105
|
for (let i = 0; i < index && i < content.length; i++) {
|
|
6891
7106
|
if (content[i] === "\n") line++;
|
|
@@ -6897,8 +7112,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6897
7112
|
let edgesAdded = 0;
|
|
6898
7113
|
const files = await walkTfFiles(scanPath);
|
|
6899
7114
|
for (const file of files) {
|
|
6900
|
-
const content = await
|
|
6901
|
-
const evidenceFile = toPosix2(
|
|
7115
|
+
const content = await import_node_fs19.promises.readFile(file, "utf8");
|
|
7116
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
|
|
6902
7117
|
const resources = [];
|
|
6903
7118
|
const byKey = /* @__PURE__ */ new Map();
|
|
6904
7119
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6933,16 +7148,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6933
7148
|
if (!target) continue;
|
|
6934
7149
|
if (seen.has(target.nodeId)) continue;
|
|
6935
7150
|
seen.add(target.nodeId);
|
|
6936
|
-
const edgeId = (0, import_types5.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7151
|
+
const edgeId = (0, import_types5.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6937
7152
|
if (graph.hasEdge(edgeId)) continue;
|
|
6938
|
-
const line =
|
|
7153
|
+
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6939
7154
|
const edge = {
|
|
6940
7155
|
id: edgeId,
|
|
6941
7156
|
source: resource.nodeId,
|
|
6942
7157
|
target: target.nodeId,
|
|
6943
|
-
type:
|
|
6944
|
-
provenance:
|
|
6945
|
-
confidence: (0,
|
|
7158
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7159
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7160
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6946
7161
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6947
7162
|
};
|
|
6948
7163
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6955,8 +7170,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6955
7170
|
|
|
6956
7171
|
// src/extract/infra/k8s.ts
|
|
6957
7172
|
init_cjs_shims();
|
|
6958
|
-
var
|
|
6959
|
-
var
|
|
7173
|
+
var import_node_fs20 = require("fs");
|
|
7174
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6960
7175
|
var import_yaml3 = require("yaml");
|
|
6961
7176
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6962
7177
|
Service: "k8s-service",
|
|
@@ -6970,15 +7185,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
6970
7185
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
6971
7186
|
if (depth > max) return [];
|
|
6972
7187
|
const out = [];
|
|
6973
|
-
const entries = await
|
|
7188
|
+
const entries = await import_node_fs20.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6974
7189
|
for (const entry of entries) {
|
|
6975
7190
|
if (entry.isDirectory()) {
|
|
6976
7191
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6977
|
-
const child =
|
|
7192
|
+
const child = import_node_path36.default.join(start, entry.name);
|
|
6978
7193
|
if (await isPythonVenvDir(child)) continue;
|
|
6979
7194
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6980
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6981
|
-
out.push(
|
|
7195
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry.name))) {
|
|
7196
|
+
out.push(import_node_path36.default.join(start, entry.name));
|
|
6982
7197
|
}
|
|
6983
7198
|
}
|
|
6984
7199
|
return out;
|
|
@@ -6987,7 +7202,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6987
7202
|
let nodesAdded = 0;
|
|
6988
7203
|
const files = await walkYamlFiles2(scanPath);
|
|
6989
7204
|
for (const file of files) {
|
|
6990
|
-
const content = await
|
|
7205
|
+
const content = await import_node_fs20.promises.readFile(file, "utf8");
|
|
6991
7206
|
let docs;
|
|
6992
7207
|
try {
|
|
6993
7208
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -7022,17 +7237,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7022
7237
|
}
|
|
7023
7238
|
|
|
7024
7239
|
// src/extract/index.ts
|
|
7025
|
-
var
|
|
7240
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7026
7241
|
|
|
7027
7242
|
// src/extract/retire.ts
|
|
7028
7243
|
init_cjs_shims();
|
|
7029
|
-
var
|
|
7030
|
-
var
|
|
7031
|
-
var
|
|
7244
|
+
var import_node_fs21 = require("fs");
|
|
7245
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7246
|
+
var import_types26 = require("@neat.is/types");
|
|
7032
7247
|
function dropOrphanedFileNodes(graph) {
|
|
7033
7248
|
const orphans = [];
|
|
7034
7249
|
graph.forEachNode((id, attrs) => {
|
|
7035
|
-
if (attrs.type !==
|
|
7250
|
+
if (attrs.type !== import_types26.NodeType.FileNode) return;
|
|
7036
7251
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7037
7252
|
orphans.push(id);
|
|
7038
7253
|
}
|
|
@@ -7045,14 +7260,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7045
7260
|
const bases = [scanPath, ...serviceDirs];
|
|
7046
7261
|
graph.forEachEdge((id, attrs) => {
|
|
7047
7262
|
const edge = attrs;
|
|
7048
|
-
if (edge.provenance !==
|
|
7263
|
+
if (edge.provenance !== import_types26.Provenance.EXTRACTED) return;
|
|
7049
7264
|
const evidenceFile = edge.evidence?.file;
|
|
7050
7265
|
if (!evidenceFile) return;
|
|
7051
|
-
if (
|
|
7052
|
-
if (!(0,
|
|
7266
|
+
if (import_node_path37.default.isAbsolute(evidenceFile)) {
|
|
7267
|
+
if (!(0, import_node_fs21.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7053
7268
|
return;
|
|
7054
7269
|
}
|
|
7055
|
-
const found = bases.some((base) => (0,
|
|
7270
|
+
const found = bases.some((base) => (0, import_node_fs21.existsSync)(import_node_path37.default.join(base, evidenceFile)));
|
|
7056
7271
|
if (!found) toDrop.push(id);
|
|
7057
7272
|
});
|
|
7058
7273
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7072,6 +7287,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7072
7287
|
const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
|
|
7073
7288
|
const phase3 = await addConfigNodes(graph, services, scanPath);
|
|
7074
7289
|
const routePhase = await addRoutes(graph, services);
|
|
7290
|
+
const grpcPhase = await addGrpcMethods(graph, services);
|
|
7075
7291
|
const phase4 = await addCallEdges(graph, services);
|
|
7076
7292
|
const phase5 = await addInfra(graph, scanPath, services);
|
|
7077
7293
|
const ghostsRetired = retireExtractedEdgesByMissingFile(
|
|
@@ -7093,7 +7309,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7093
7309
|
}
|
|
7094
7310
|
const droppedEntries = drainDroppedExtracted();
|
|
7095
7311
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7096
|
-
const rejectedPath =
|
|
7312
|
+
const rejectedPath = import_node_path38.default.join(import_node_path38.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7097
7313
|
try {
|
|
7098
7314
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7099
7315
|
} catch (err) {
|
|
@@ -7103,8 +7319,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7103
7319
|
}
|
|
7104
7320
|
}
|
|
7105
7321
|
const result = {
|
|
7106
|
-
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
7107
|
-
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
7322
|
+
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + grpcPhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
7323
|
+
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + grpcPhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
7108
7324
|
frontiersPromoted,
|
|
7109
7325
|
extractionErrors: errorEntries.length,
|
|
7110
7326
|
errorEntries,
|
|
@@ -7127,7 +7343,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7127
7343
|
|
|
7128
7344
|
// src/diff.ts
|
|
7129
7345
|
init_cjs_shims();
|
|
7130
|
-
var
|
|
7346
|
+
var import_node_fs22 = require("fs");
|
|
7131
7347
|
async function loadSnapshotForDiff(target) {
|
|
7132
7348
|
if (/^https?:\/\//i.test(target)) {
|
|
7133
7349
|
const res = await fetch(target);
|
|
@@ -7136,7 +7352,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
7136
7352
|
}
|
|
7137
7353
|
return await res.json();
|
|
7138
7354
|
}
|
|
7139
|
-
const raw = await
|
|
7355
|
+
const raw = await import_node_fs22.promises.readFile(target, "utf8");
|
|
7140
7356
|
return JSON.parse(raw);
|
|
7141
7357
|
}
|
|
7142
7358
|
function indexEntries(entries) {
|
|
@@ -7204,9 +7420,9 @@ function canonicalJson(value) {
|
|
|
7204
7420
|
|
|
7205
7421
|
// src/persist.ts
|
|
7206
7422
|
init_cjs_shims();
|
|
7207
|
-
var
|
|
7208
|
-
var
|
|
7209
|
-
var
|
|
7423
|
+
var import_node_fs23 = require("fs");
|
|
7424
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7425
|
+
var import_types27 = require("@neat.is/types");
|
|
7210
7426
|
var SCHEMA_VERSION = 4;
|
|
7211
7427
|
function migrateV1ToV2(payload) {
|
|
7212
7428
|
const nodes = payload.graph.nodes;
|
|
@@ -7228,12 +7444,12 @@ function migrateV2ToV3(payload) {
|
|
|
7228
7444
|
for (const edge of edges) {
|
|
7229
7445
|
const attrs = edge.attributes;
|
|
7230
7446
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7231
|
-
attrs.provenance =
|
|
7447
|
+
attrs.provenance = import_types27.Provenance.OBSERVED;
|
|
7232
7448
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7233
7449
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7234
7450
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7235
7451
|
if (type && source && target) {
|
|
7236
|
-
const newId = (0,
|
|
7452
|
+
const newId = (0, import_types27.observedEdgeId)(source, target, type);
|
|
7237
7453
|
attrs.id = newId;
|
|
7238
7454
|
if (edge.key) edge.key = newId;
|
|
7239
7455
|
}
|
|
@@ -7242,7 +7458,7 @@ function migrateV2ToV3(payload) {
|
|
|
7242
7458
|
return { ...payload, schemaVersion: 3 };
|
|
7243
7459
|
}
|
|
7244
7460
|
async function ensureDir(filePath) {
|
|
7245
|
-
await
|
|
7461
|
+
await import_node_fs23.promises.mkdir(import_node_path39.default.dirname(filePath), { recursive: true });
|
|
7246
7462
|
}
|
|
7247
7463
|
async function saveGraphToDisk(graph, outPath) {
|
|
7248
7464
|
await ensureDir(outPath);
|
|
@@ -7252,13 +7468,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
7252
7468
|
graph: graph.export()
|
|
7253
7469
|
};
|
|
7254
7470
|
const tmp = `${outPath}.tmp`;
|
|
7255
|
-
await
|
|
7256
|
-
await
|
|
7471
|
+
await import_node_fs23.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
7472
|
+
await import_node_fs23.promises.rename(tmp, outPath);
|
|
7257
7473
|
}
|
|
7258
7474
|
async function loadGraphFromDisk(graph, outPath) {
|
|
7259
7475
|
let raw;
|
|
7260
7476
|
try {
|
|
7261
|
-
raw = await
|
|
7477
|
+
raw = await import_node_fs23.promises.readFile(outPath, "utf8");
|
|
7262
7478
|
} catch (err) {
|
|
7263
7479
|
if (err.code === "ENOENT") return;
|
|
7264
7480
|
throw err;
|
|
@@ -7323,23 +7539,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7323
7539
|
|
|
7324
7540
|
// src/projects.ts
|
|
7325
7541
|
init_cjs_shims();
|
|
7326
|
-
var
|
|
7542
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7327
7543
|
function pathsForProject(project, baseDir) {
|
|
7328
7544
|
if (project === DEFAULT_PROJECT) {
|
|
7329
7545
|
return {
|
|
7330
|
-
snapshotPath:
|
|
7331
|
-
errorsPath:
|
|
7332
|
-
staleEventsPath:
|
|
7333
|
-
embeddingsCachePath:
|
|
7334
|
-
policyViolationsPath:
|
|
7546
|
+
snapshotPath: import_node_path40.default.join(baseDir, "graph.json"),
|
|
7547
|
+
errorsPath: import_node_path40.default.join(baseDir, "errors.ndjson"),
|
|
7548
|
+
staleEventsPath: import_node_path40.default.join(baseDir, "stale-events.ndjson"),
|
|
7549
|
+
embeddingsCachePath: import_node_path40.default.join(baseDir, "embeddings.json"),
|
|
7550
|
+
policyViolationsPath: import_node_path40.default.join(baseDir, "policy-violations.ndjson")
|
|
7335
7551
|
};
|
|
7336
7552
|
}
|
|
7337
7553
|
return {
|
|
7338
|
-
snapshotPath:
|
|
7339
|
-
errorsPath:
|
|
7340
|
-
staleEventsPath:
|
|
7341
|
-
embeddingsCachePath:
|
|
7342
|
-
policyViolationsPath:
|
|
7554
|
+
snapshotPath: import_node_path40.default.join(baseDir, `${project}.json`),
|
|
7555
|
+
errorsPath: import_node_path40.default.join(baseDir, `errors.${project}.ndjson`),
|
|
7556
|
+
staleEventsPath: import_node_path40.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
7557
|
+
embeddingsCachePath: import_node_path40.default.join(baseDir, `embeddings.${project}.json`),
|
|
7558
|
+
policyViolationsPath: import_node_path40.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
7343
7559
|
};
|
|
7344
7560
|
}
|
|
7345
7561
|
var Projects = class {
|
|
@@ -7379,23 +7595,23 @@ function parseExtraProjects(raw) {
|
|
|
7379
7595
|
|
|
7380
7596
|
// src/registry.ts
|
|
7381
7597
|
init_cjs_shims();
|
|
7382
|
-
var
|
|
7598
|
+
var import_node_fs24 = require("fs");
|
|
7383
7599
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
7384
|
-
var
|
|
7385
|
-
var
|
|
7600
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7601
|
+
var import_types28 = require("@neat.is/types");
|
|
7386
7602
|
function neatHome() {
|
|
7387
7603
|
const override = process.env.NEAT_HOME;
|
|
7388
|
-
if (override && override.length > 0) return
|
|
7389
|
-
return
|
|
7604
|
+
if (override && override.length > 0) return import_node_path41.default.resolve(override);
|
|
7605
|
+
return import_node_path41.default.join(import_node_os3.default.homedir(), ".neat");
|
|
7390
7606
|
}
|
|
7391
7607
|
function registryPath() {
|
|
7392
|
-
return
|
|
7608
|
+
return import_node_path41.default.join(neatHome(), "projects.json");
|
|
7393
7609
|
}
|
|
7394
7610
|
async function readRegistry() {
|
|
7395
7611
|
const file = registryPath();
|
|
7396
7612
|
let raw;
|
|
7397
7613
|
try {
|
|
7398
|
-
raw = await
|
|
7614
|
+
raw = await import_node_fs24.promises.readFile(file, "utf8");
|
|
7399
7615
|
} catch (err) {
|
|
7400
7616
|
if (err.code === "ENOENT") {
|
|
7401
7617
|
return { version: 1, projects: [] };
|
|
@@ -7403,7 +7619,7 @@ async function readRegistry() {
|
|
|
7403
7619
|
throw err;
|
|
7404
7620
|
}
|
|
7405
7621
|
const parsed = JSON.parse(raw);
|
|
7406
|
-
return
|
|
7622
|
+
return import_types28.RegistryFileSchema.parse(parsed);
|
|
7407
7623
|
}
|
|
7408
7624
|
async function getProject(name) {
|
|
7409
7625
|
const reg = await readRegistry();
|
|
@@ -7624,11 +7840,11 @@ function registerRoutes(scope, ctx) {
|
|
|
7624
7840
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7625
7841
|
const parsed = [];
|
|
7626
7842
|
for (const c of candidates) {
|
|
7627
|
-
const r =
|
|
7843
|
+
const r = import_types29.DivergenceTypeSchema.safeParse(c);
|
|
7628
7844
|
if (!r.success) {
|
|
7629
7845
|
return reply.code(400).send({
|
|
7630
7846
|
error: `unknown divergence type "${c}"`,
|
|
7631
|
-
allowed:
|
|
7847
|
+
allowed: import_types29.DivergenceTypeSchema.options
|
|
7632
7848
|
});
|
|
7633
7849
|
}
|
|
7634
7850
|
parsed.push(r.data);
|
|
@@ -7847,7 +8063,7 @@ function registerRoutes(scope, ctx) {
|
|
|
7847
8063
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
7848
8064
|
let violations = await log.readAll();
|
|
7849
8065
|
if (req.query.severity) {
|
|
7850
|
-
const sev =
|
|
8066
|
+
const sev = import_types29.PolicySeveritySchema.safeParse(req.query.severity);
|
|
7851
8067
|
if (!sev.success) {
|
|
7852
8068
|
return reply.code(400).send({
|
|
7853
8069
|
error: "invalid severity",
|
|
@@ -7886,7 +8102,7 @@ function registerRoutes(scope, ctx) {
|
|
|
7886
8102
|
scope.post("/policies/check", async (req, reply) => {
|
|
7887
8103
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
7888
8104
|
if (!proj) return;
|
|
7889
|
-
const parsed =
|
|
8105
|
+
const parsed = import_types29.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
7890
8106
|
if (!parsed.success) {
|
|
7891
8107
|
return reply.code(400).send({
|
|
7892
8108
|
error: "invalid /policies/check body",
|
|
@@ -8148,8 +8364,8 @@ init_otel_grpc();
|
|
|
8148
8364
|
|
|
8149
8365
|
// src/search.ts
|
|
8150
8366
|
init_cjs_shims();
|
|
8151
|
-
var
|
|
8152
|
-
var
|
|
8367
|
+
var import_node_fs25 = require("fs");
|
|
8368
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8153
8369
|
var import_node_crypto3 = require("crypto");
|
|
8154
8370
|
var DEFAULT_LIMIT = 10;
|
|
8155
8371
|
var NOMIC_DIM = 768;
|
|
@@ -8196,6 +8412,18 @@ function embedText(node) {
|
|
|
8196
8412
|
if (opType) parts.push(`operationType=${opType}`);
|
|
8197
8413
|
break;
|
|
8198
8414
|
}
|
|
8415
|
+
case "GrpcMethodNode": {
|
|
8416
|
+
const rpcService = node.rpcService;
|
|
8417
|
+
const rpcMethod = node.rpcMethod;
|
|
8418
|
+
if (rpcService) parts.push(`rpcService=${rpcService}`);
|
|
8419
|
+
if (rpcMethod) parts.push(`rpcMethod=${rpcMethod}`);
|
|
8420
|
+
break;
|
|
8421
|
+
}
|
|
8422
|
+
case "WebSocketChannelNode": {
|
|
8423
|
+
const channel = node.channel;
|
|
8424
|
+
if (channel) parts.push(`channel=${channel}`);
|
|
8425
|
+
break;
|
|
8426
|
+
}
|
|
8199
8427
|
default:
|
|
8200
8428
|
break;
|
|
8201
8429
|
}
|
|
@@ -8291,7 +8519,7 @@ async function pickEmbedder() {
|
|
|
8291
8519
|
}
|
|
8292
8520
|
async function readCache(cachePath) {
|
|
8293
8521
|
try {
|
|
8294
|
-
const raw = await
|
|
8522
|
+
const raw = await import_node_fs25.promises.readFile(cachePath, "utf8");
|
|
8295
8523
|
const parsed = JSON.parse(raw);
|
|
8296
8524
|
if (parsed.version !== 1) return null;
|
|
8297
8525
|
return parsed;
|
|
@@ -8300,8 +8528,8 @@ async function readCache(cachePath) {
|
|
|
8300
8528
|
}
|
|
8301
8529
|
}
|
|
8302
8530
|
async function writeCache(cachePath, cache) {
|
|
8303
|
-
await
|
|
8304
|
-
await
|
|
8531
|
+
await import_node_fs25.promises.mkdir(import_node_path44.default.dirname(cachePath), { recursive: true });
|
|
8532
|
+
await import_node_fs25.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
8305
8533
|
}
|
|
8306
8534
|
var VectorIndex = class {
|
|
8307
8535
|
constructor(embedder, cachePath) {
|
|
@@ -8481,14 +8709,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
8481
8709
|
async function main() {
|
|
8482
8710
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
8483
8711
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
8484
|
-
const baseDir = baseDirEnv ?
|
|
8485
|
-
const defaultScanPath =
|
|
8712
|
+
const baseDir = baseDirEnv ? import_node_path45.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path45.default.resolve(import_node_path45.default.dirname(legacyOutPath)) : import_node_path45.default.resolve("./neat-out");
|
|
8713
|
+
const defaultScanPath = import_node_path45.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
8486
8714
|
const registry = new Projects();
|
|
8487
8715
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
8488
8716
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
8489
8717
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
8490
8718
|
const projectScan = process.env[envKey];
|
|
8491
|
-
await bootProject(registry, name, projectScan ?
|
|
8719
|
+
await bootProject(registry, name, projectScan ? import_node_path45.default.resolve(projectScan) : void 0, baseDir);
|
|
8492
8720
|
}
|
|
8493
8721
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
8494
8722
|
const port = Number(process.env.PORT ?? 8080);
|