@neat.is/core 0.9.1-dev.20260819 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JUVJBH23.js → chunk-UN7VFA4H.js} +1045 -376
- package/dist/chunk-UN7VFA4H.js.map +1 -0
- package/dist/{chunk-ICO37D3H.js → chunk-YKQB622D.js} +2 -2
- package/dist/cli.cjs +1581 -910
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1244 -573
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +1260 -589
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +1147 -476
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-JUVJBH23.js.map +0 -1
- /package/dist/{chunk-ICO37D3H.js.map → chunk-YKQB622D.js.map} +0 -0
package/dist/neatd.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path78 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path78) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path78)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path52.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url,
|
|
266
|
+
var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path52 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
415
415
|
const v = attrs[key];
|
|
416
416
|
if (typeof v === "string" && v.length > 0) {
|
|
417
417
|
const q = v.indexOf("?");
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
418
|
+
const path78 = q === -1 ? v : v.slice(0, q);
|
|
419
|
+
if (path78.length > 0) return path78;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
return void 0;
|
|
@@ -478,10 +478,10 @@ function parseOtlpRequest(body) {
|
|
|
478
478
|
return out;
|
|
479
479
|
}
|
|
480
480
|
function loadProtoRoot() {
|
|
481
|
-
const here =
|
|
482
|
-
const protoRoot =
|
|
481
|
+
const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
482
|
+
const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
|
|
483
483
|
const root = new import_protobufjs.default.Root();
|
|
484
|
-
root.resolvePath = (_origin, target) =>
|
|
484
|
+
root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
|
|
485
485
|
root.loadSync(
|
|
486
486
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
487
487
|
{ keepCase: true }
|
|
@@ -728,12 +728,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
728
728
|
}
|
|
729
729
|
}
|
|
730
730
|
}
|
|
731
|
-
var
|
|
731
|
+
var import_node_path53, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
732
732
|
var init_otel = __esm({
|
|
733
733
|
"src/otel.ts"() {
|
|
734
734
|
"use strict";
|
|
735
735
|
init_cjs_shims();
|
|
736
|
-
|
|
736
|
+
import_node_path53 = __toESM(require("path"), 1);
|
|
737
737
|
import_node_url2 = require("url");
|
|
738
738
|
import_fastify = __toESM(require("fastify"), 1);
|
|
739
739
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -756,14 +756,14 @@ __export(neatd_exports, {
|
|
|
756
756
|
});
|
|
757
757
|
module.exports = __toCommonJS(neatd_exports);
|
|
758
758
|
init_cjs_shims();
|
|
759
|
-
var
|
|
760
|
-
var
|
|
759
|
+
var import_node_fs42 = require("fs");
|
|
760
|
+
var import_node_path77 = __toESM(require("path"), 1);
|
|
761
761
|
var import_node_module2 = require("module");
|
|
762
762
|
|
|
763
763
|
// src/daemon.ts
|
|
764
764
|
init_cjs_shims();
|
|
765
|
-
var
|
|
766
|
-
var
|
|
765
|
+
var import_node_fs40 = require("fs");
|
|
766
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
767
767
|
var import_node_module = require("module");
|
|
768
768
|
|
|
769
769
|
// src/graph.ts
|
|
@@ -1296,19 +1296,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1296
1296
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1297
1297
|
let best = { path: [start], edges: [] };
|
|
1298
1298
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1299
|
-
function step(node,
|
|
1300
|
-
if (
|
|
1301
|
-
best = { path: [...
|
|
1299
|
+
function step(node, path78, edges) {
|
|
1300
|
+
if (path78.length > best.path.length) {
|
|
1301
|
+
best = { path: [...path78], edges: [...edges] };
|
|
1302
1302
|
}
|
|
1303
|
-
if (
|
|
1303
|
+
if (path78.length - 1 >= maxDepth) return;
|
|
1304
1304
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1305
1305
|
for (const [srcId, edge] of incoming) {
|
|
1306
1306
|
if (visited.has(srcId)) continue;
|
|
1307
1307
|
visited.add(srcId);
|
|
1308
|
-
|
|
1308
|
+
path78.push(srcId);
|
|
1309
1309
|
edges.push(edge);
|
|
1310
|
-
step(srcId,
|
|
1311
|
-
|
|
1310
|
+
step(srcId, path78, edges);
|
|
1311
|
+
path78.pop();
|
|
1312
1312
|
edges.pop();
|
|
1313
1313
|
visited.delete(srcId);
|
|
1314
1314
|
}
|
|
@@ -1316,11 +1316,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1316
1316
|
step(start, [start], []);
|
|
1317
1317
|
return best;
|
|
1318
1318
|
}
|
|
1319
|
-
function databaseRootCauseShape(graph, origin,
|
|
1319
|
+
function databaseRootCauseShape(graph, origin, walk10) {
|
|
1320
1320
|
const targetDb = origin;
|
|
1321
1321
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1322
1322
|
if (candidatePairs.length === 0) return null;
|
|
1323
|
-
for (const id of
|
|
1323
|
+
for (const id of walk10.path) {
|
|
1324
1324
|
const owner = resolveOwningService(graph, id);
|
|
1325
1325
|
if (!owner) continue;
|
|
1326
1326
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1347,8 +1347,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
|
|
|
1347
1347
|
}
|
|
1348
1348
|
return null;
|
|
1349
1349
|
}
|
|
1350
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1351
|
-
for (const id of
|
|
1350
|
+
function serviceRootCauseShape(graph, _origin, walk10) {
|
|
1351
|
+
for (const id of walk10.path) {
|
|
1352
1352
|
const owner = resolveOwningService(graph, id);
|
|
1353
1353
|
if (!owner) continue;
|
|
1354
1354
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1384,15 +1384,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
1384
1384
|
}
|
|
1385
1385
|
return null;
|
|
1386
1386
|
}
|
|
1387
|
-
function fileRootCauseShape(graph, origin,
|
|
1387
|
+
function fileRootCauseShape(graph, origin, walk10) {
|
|
1388
1388
|
const owner = resolveOwningService(graph, origin.id);
|
|
1389
1389
|
if (!owner) return null;
|
|
1390
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1390
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1391
1391
|
}
|
|
1392
|
-
function symbolRootCauseShape(graph, origin,
|
|
1392
|
+
function symbolRootCauseShape(graph, origin, walk10) {
|
|
1393
1393
|
const owner = resolveOwningService(graph, origin.id);
|
|
1394
1394
|
if (!owner) return null;
|
|
1395
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1395
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1396
1396
|
}
|
|
1397
1397
|
var rootCauseShapes = {
|
|
1398
1398
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1405,25 +1405,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1405
1405
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1406
1406
|
const shape = rootCauseShapes[origin.type];
|
|
1407
1407
|
if (shape) {
|
|
1408
|
-
const
|
|
1409
|
-
const match = shape(graph, origin,
|
|
1408
|
+
const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1409
|
+
const match = shape(graph, origin, walk10);
|
|
1410
1410
|
if (match) {
|
|
1411
1411
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1412
|
-
return
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1412
|
+
return {
|
|
1413
|
+
source: "compat",
|
|
1414
|
+
result: import_types.RootCauseResultSchema.parse({
|
|
1415
|
+
rootCauseNode: match.rootCauseNode,
|
|
1416
|
+
rootCauseReason: reason,
|
|
1417
|
+
traversalPath: walk10.path,
|
|
1418
|
+
edgeProvenances: walk10.edges.map((e) => e.provenance),
|
|
1419
|
+
confidence: confidenceFromMix(walk10.edges),
|
|
1420
|
+
fixRecommendation: match.fixRecommendation
|
|
1421
|
+
})
|
|
1422
|
+
};
|
|
1420
1423
|
}
|
|
1421
1424
|
}
|
|
1422
1425
|
if (origin.type === import_types.NodeType.ServiceNode) {
|
|
1423
1426
|
const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
|
|
1424
|
-
if (crossService) return crossService;
|
|
1427
|
+
if (crossService) return { result: crossService, source: "cross-service" };
|
|
1425
1428
|
}
|
|
1426
|
-
|
|
1429
|
+
const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
|
|
1430
|
+
return incident ? { result: incident, source: "incident" } : null;
|
|
1427
1431
|
}
|
|
1428
1432
|
var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
|
|
1429
1433
|
function incidentMatchesNode(ev, nodeId) {
|
|
@@ -1519,26 +1523,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
|
|
|
1519
1523
|
return best;
|
|
1520
1524
|
}
|
|
1521
1525
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1522
|
-
const
|
|
1526
|
+
const path78 = [originServiceId];
|
|
1523
1527
|
const edges = [];
|
|
1524
1528
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1525
1529
|
let current = originServiceId;
|
|
1526
1530
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1527
1531
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1528
1532
|
if (!hop) break;
|
|
1529
|
-
|
|
1533
|
+
path78.push(hop.nextService);
|
|
1530
1534
|
edges.push(hop.edge);
|
|
1531
1535
|
visited.add(hop.nextService);
|
|
1532
1536
|
current = hop.nextService;
|
|
1533
1537
|
}
|
|
1534
1538
|
if (edges.length === 0) return null;
|
|
1535
|
-
return { path:
|
|
1539
|
+
return { path: path78, edges, culprit: current };
|
|
1540
|
+
}
|
|
1541
|
+
function isStaleCallEdge(e) {
|
|
1542
|
+
return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
|
|
1543
|
+
}
|
|
1544
|
+
function staleCallDominates(e, id, curEdge, curId) {
|
|
1545
|
+
const ev = e.signal?.spanCount ?? e.callCount ?? 0;
|
|
1546
|
+
const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
|
|
1547
|
+
if (ev !== cv) return ev > cv;
|
|
1548
|
+
return id < curId;
|
|
1549
|
+
}
|
|
1550
|
+
function dominantStaleCall(graph, serviceId15, visited) {
|
|
1551
|
+
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1552
|
+
for (const src of callSourcesForService(graph, serviceId15)) {
|
|
1553
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
1554
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
1555
|
+
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
1556
|
+
if (isFrontierNode(graph, e.target)) continue;
|
|
1557
|
+
const owner = resolveOwningService(graph, e.target);
|
|
1558
|
+
if (!owner || visited.has(owner.id)) continue;
|
|
1559
|
+
const cur = bestByCallee.get(owner.id);
|
|
1560
|
+
if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
|
|
1561
|
+
bestByCallee.set(owner.id, e);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
let best = null;
|
|
1566
|
+
for (const [id, edge] of bestByCallee) {
|
|
1567
|
+
if (!isStaleCallEdge(edge)) continue;
|
|
1568
|
+
if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
|
|
1569
|
+
best = { nextService: id, edge };
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return best;
|
|
1573
|
+
}
|
|
1574
|
+
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
1575
|
+
const path78 = [originServiceId];
|
|
1576
|
+
const edges = [];
|
|
1577
|
+
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1578
|
+
let current = originServiceId;
|
|
1579
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1580
|
+
const hop = dominantStaleCall(graph, current, visited);
|
|
1581
|
+
if (!hop) break;
|
|
1582
|
+
path78.push(hop.nextService);
|
|
1583
|
+
edges.push(hop.edge);
|
|
1584
|
+
visited.add(hop.nextService);
|
|
1585
|
+
current = hop.nextService;
|
|
1586
|
+
}
|
|
1587
|
+
if (edges.length === 0) return null;
|
|
1588
|
+
return { path: path78, edges, culprit: current };
|
|
1536
1589
|
}
|
|
1537
1590
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1538
1591
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1539
1592
|
if (!chain) return null;
|
|
1540
1593
|
const culprit = chain.culprit;
|
|
1541
|
-
const
|
|
1594
|
+
const path78 = [...chain.path];
|
|
1542
1595
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1543
1596
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1544
1597
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1546,14 +1599,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1546
1599
|
if (loc) {
|
|
1547
1600
|
let rootCauseNode = culprit;
|
|
1548
1601
|
if (loc.fileNode) {
|
|
1549
|
-
|
|
1602
|
+
path78.push(loc.fileNode);
|
|
1550
1603
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1551
1604
|
rootCauseNode = loc.fileNode;
|
|
1552
1605
|
}
|
|
1553
1606
|
return import_types.RootCauseResultSchema.parse({
|
|
1554
1607
|
rootCauseNode,
|
|
1555
1608
|
rootCauseReason: loc.rootCauseReason,
|
|
1556
|
-
traversalPath:
|
|
1609
|
+
traversalPath: path78,
|
|
1557
1610
|
edgeProvenances,
|
|
1558
1611
|
confidence,
|
|
1559
1612
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1565,7 +1618,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1565
1618
|
return import_types.RootCauseResultSchema.parse({
|
|
1566
1619
|
rootCauseNode: culprit,
|
|
1567
1620
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1568
|
-
traversalPath:
|
|
1621
|
+
traversalPath: path78,
|
|
1569
1622
|
edgeProvenances,
|
|
1570
1623
|
confidence,
|
|
1571
1624
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -1952,17 +2005,20 @@ function displayNameOf(nodeId) {
|
|
|
1952
2005
|
return nodeId.replace(/^[a-z]+:/, "");
|
|
1953
2006
|
}
|
|
1954
2007
|
function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
|
|
1955
|
-
const
|
|
1956
|
-
if (!
|
|
2008
|
+
const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
|
|
2009
|
+
if (!tagged) return null;
|
|
1957
2010
|
const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
|
|
1958
|
-
if (!navigation) return
|
|
1959
|
-
return enrichWithNavigation(graph, errorNodeId,
|
|
2011
|
+
if (!navigation) return tagged.result;
|
|
2012
|
+
return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
|
|
1960
2013
|
}
|
|
1961
|
-
function enrichWithNavigation(graph, errorNodeId,
|
|
2014
|
+
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
2015
|
+
const legacy = tagged.result;
|
|
1962
2016
|
const seedNode = legacy.rootCauseNode;
|
|
1963
2017
|
const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
|
|
1964
2018
|
const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
|
|
1965
2019
|
const candidates = [];
|
|
2020
|
+
const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
|
|
2021
|
+
const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
|
|
1966
2022
|
if (seedCtx && isVictimSeed(seedCtx)) {
|
|
1967
2023
|
const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
|
|
1968
2024
|
const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
|
|
@@ -1987,6 +2043,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
1987
2043
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
1988
2044
|
...lastProv ? { provenance: lastProv } : {}
|
|
1989
2045
|
});
|
|
2046
|
+
} else if (staleChain) {
|
|
2047
|
+
const culprit = staleChain.culprit;
|
|
2048
|
+
const culpritName = displayNameOf(culprit);
|
|
2049
|
+
const seedName = displayNameOf(seedNode);
|
|
2050
|
+
const staleConfidence = confidenceFromMix(staleChain.edges, now);
|
|
2051
|
+
candidates.push({
|
|
2052
|
+
node: culprit,
|
|
2053
|
+
classification: "primary-failure",
|
|
2054
|
+
reason: `${culpritName} is the stale-derived root cause (low confidence): live telemetry for this subgraph has gone quiet, but the last-observed topology traces the failure surfacing at ${seedName} downstream through a STALE call chain to ${culpritName}. Provenance is STALE, so confidence is capped low \u2014 restore instrumentation and re-run to confirm before acting.`,
|
|
2055
|
+
context: nodeContext(graph, culprit, incidents, now),
|
|
2056
|
+
confidence: staleConfidence,
|
|
2057
|
+
provenance: import_types.Provenance.STALE
|
|
2058
|
+
});
|
|
2059
|
+
candidates.push({
|
|
2060
|
+
node: seedNode,
|
|
2061
|
+
classification: "symptom-only",
|
|
2062
|
+
reason: `The failure surfaced here, but the only causal chain the graph still holds is STALE and runs downstream \u2014 ${seedName} is the surface of a stale-traced failure, not a proven origin.`,
|
|
2063
|
+
context: seedCtx ?? EMPTY_CONTEXT,
|
|
2064
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
|
|
2065
|
+
...lastProv ? { provenance: lastProv } : {}
|
|
2066
|
+
});
|
|
1990
2067
|
} else {
|
|
1991
2068
|
candidates.push({
|
|
1992
2069
|
node: seedNode,
|
|
@@ -2000,11 +2077,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
2000
2077
|
const top = candidates[0];
|
|
2001
2078
|
let traversalPath = legacy.traversalPath;
|
|
2002
2079
|
let edgeProvenances = legacy.edgeProvenances;
|
|
2003
|
-
if (top.node
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2080
|
+
if (staleChain && top.node === staleChain.culprit) {
|
|
2081
|
+
traversalPath = staleChain.path;
|
|
2082
|
+
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
2083
|
+
} else if (top.node !== seedNode) {
|
|
2084
|
+
const path78 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
2085
|
+
if (path78) {
|
|
2086
|
+
traversalPath = path78.nodes;
|
|
2087
|
+
edgeProvenances = path78.edges.map((e) => e.provenance);
|
|
2008
2088
|
} else {
|
|
2009
2089
|
traversalPath = [errorNodeId, top.node];
|
|
2010
2090
|
edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
|
|
@@ -2026,6 +2106,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
|
|
|
2026
2106
|
return legacy.fixRecommendation;
|
|
2027
2107
|
}
|
|
2028
2108
|
const name = top.node.replace(/^service:/, "");
|
|
2109
|
+
if (top.provenance === import_types.Provenance.STALE) {
|
|
2110
|
+
return `Live telemetry for this path has gone quiet; the last-observed topology traces the failure downstream to ${name}. Restore instrumentation (or re-run with live traces) to confirm, then inspect ${name}.`;
|
|
2111
|
+
}
|
|
2029
2112
|
if (top.classification === "primary-failure") {
|
|
2030
2113
|
return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
|
|
2031
2114
|
}
|
|
@@ -2544,6 +2627,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
|
2544
2627
|
var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
|
|
2545
2628
|
var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
|
|
2546
2629
|
var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
|
|
2630
|
+
var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
|
|
2547
2631
|
var import_types6 = require("@neat.is/types");
|
|
2548
2632
|
|
|
2549
2633
|
// src/extract/shared.ts
|
|
@@ -2824,7 +2908,7 @@ function buildServiceHostIndex(services) {
|
|
|
2824
2908
|
async function walkSourceFiles(dir, excludeDirs = []) {
|
|
2825
2909
|
const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
|
|
2826
2910
|
const out = [];
|
|
2827
|
-
async function
|
|
2911
|
+
async function walk10(current) {
|
|
2828
2912
|
const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2829
2913
|
for (const entry2 of entries) {
|
|
2830
2914
|
const full = import_node_path5.default.join(current, entry2.name);
|
|
@@ -2832,7 +2916,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2832
2916
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
2833
2917
|
if (excluded.has(import_node_path5.default.resolve(full))) continue;
|
|
2834
2918
|
if (await isPythonVenvDir(full)) continue;
|
|
2835
|
-
await
|
|
2919
|
+
await walk10(full);
|
|
2836
2920
|
} else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
2837
2921
|
// would attribute our instrumentation imports to the user's service.
|
|
2838
2922
|
!isNeatAuthoredSourceFile(entry2.name)) {
|
|
@@ -2840,7 +2924,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2840
2924
|
}
|
|
2841
2925
|
}
|
|
2842
2926
|
}
|
|
2843
|
-
await
|
|
2927
|
+
await walk10(dir);
|
|
2844
2928
|
return out;
|
|
2845
2929
|
}
|
|
2846
2930
|
async function loadSourceFiles(dir, excludeDirs = []) {
|
|
@@ -3360,6 +3444,11 @@ function makePhpParser() {
|
|
|
3360
3444
|
p.setLanguage(import_tree_sitter_php.default.php_only);
|
|
3361
3445
|
return p;
|
|
3362
3446
|
}
|
|
3447
|
+
function makeRustParser() {
|
|
3448
|
+
const p = new import_tree_sitter2.default();
|
|
3449
|
+
p.setLanguage(import_tree_sitter_rust.default);
|
|
3450
|
+
return p;
|
|
3451
|
+
}
|
|
3363
3452
|
var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
3364
3453
|
"get",
|
|
3365
3454
|
"post",
|
|
@@ -3431,8 +3520,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3431
3520
|
chiWalk(tree.rootNode, "", out);
|
|
3432
3521
|
return out;
|
|
3433
3522
|
}
|
|
3434
|
-
function stripChiRegex(
|
|
3435
|
-
return
|
|
3523
|
+
function stripChiRegex(path78) {
|
|
3524
|
+
return path78.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3436
3525
|
}
|
|
3437
3526
|
function chiWalk(node, prefix, out) {
|
|
3438
3527
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -4104,9 +4193,9 @@ function rubyRocketRoute(args) {
|
|
|
4104
4193
|
if (!pair || pair.type !== "pair") continue;
|
|
4105
4194
|
const k = pair.childForFieldName("key");
|
|
4106
4195
|
if (k?.type !== "string") continue;
|
|
4107
|
-
const
|
|
4108
|
-
if (
|
|
4109
|
-
return { path:
|
|
4196
|
+
const path78 = rubyLiteral(k);
|
|
4197
|
+
if (path78 === null) continue;
|
|
4198
|
+
return { path: path78, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
4110
4199
|
}
|
|
4111
4200
|
return null;
|
|
4112
4201
|
}
|
|
@@ -4276,6 +4365,48 @@ function railsRoutesFromSource(source, parser) {
|
|
|
4276
4365
|
});
|
|
4277
4366
|
return out;
|
|
4278
4367
|
}
|
|
4368
|
+
var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4369
|
+
function sinatraRoutesFromSource(source, parser) {
|
|
4370
|
+
const tree = parseSource2(parser, source);
|
|
4371
|
+
if (!fileReferencesSinatra(tree.rootNode)) return [];
|
|
4372
|
+
const out = [];
|
|
4373
|
+
walk(tree.rootNode, (node) => {
|
|
4374
|
+
if (node.type !== "call") return;
|
|
4375
|
+
if (node.childForFieldName("receiver")) return;
|
|
4376
|
+
const method = node.childForFieldName("method")?.text;
|
|
4377
|
+
if (!method || !SINATRA_VERBS.has(method)) return;
|
|
4378
|
+
if (!node.childForFieldName("block")) return;
|
|
4379
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
4380
|
+
if (first?.type !== "string") return;
|
|
4381
|
+
const p = rubyLiteral(first);
|
|
4382
|
+
if (p === null || !p.startsWith("/")) return;
|
|
4383
|
+
out.push({
|
|
4384
|
+
method: method.toUpperCase(),
|
|
4385
|
+
pathTemplate: canonicalizeTemplate(p),
|
|
4386
|
+
line: node.startPosition.row + 1,
|
|
4387
|
+
framework: "sinatra"
|
|
4388
|
+
});
|
|
4389
|
+
});
|
|
4390
|
+
return out;
|
|
4391
|
+
}
|
|
4392
|
+
function fileReferencesSinatra(root) {
|
|
4393
|
+
let found = false;
|
|
4394
|
+
walk(root, (node) => {
|
|
4395
|
+
if (found) return;
|
|
4396
|
+
if (node.type === "call") {
|
|
4397
|
+
const m = node.childForFieldName("method")?.text;
|
|
4398
|
+
if (m === "require" || m === "require_relative") {
|
|
4399
|
+
const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
4400
|
+
if (s !== null && /^sinatra\b/.test(s)) found = true;
|
|
4401
|
+
}
|
|
4402
|
+
return;
|
|
4403
|
+
}
|
|
4404
|
+
if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
|
|
4405
|
+
found = true;
|
|
4406
|
+
}
|
|
4407
|
+
});
|
|
4408
|
+
return found;
|
|
4409
|
+
}
|
|
4279
4410
|
var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
|
|
4280
4411
|
var LARAVEL_RESOURCE_ROWS = [
|
|
4281
4412
|
{ action: "index", methods: ["GET"], suffix: "" },
|
|
@@ -4451,6 +4582,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
|
|
|
4451
4582
|
}
|
|
4452
4583
|
return out;
|
|
4453
4584
|
}
|
|
4585
|
+
var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4586
|
+
function isSlimAppCtor(node) {
|
|
4587
|
+
if (!node) return false;
|
|
4588
|
+
if (node.type === "scoped_call_expression") {
|
|
4589
|
+
const scope = node.childForFieldName("scope")?.text ?? "";
|
|
4590
|
+
const name = node.childForFieldName("name")?.text;
|
|
4591
|
+
return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
|
|
4592
|
+
}
|
|
4593
|
+
if (node.type === "object_creation_expression") {
|
|
4594
|
+
const cls = node.namedChild(0)?.text ?? "";
|
|
4595
|
+
return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
|
|
4596
|
+
}
|
|
4597
|
+
return false;
|
|
4598
|
+
}
|
|
4599
|
+
function isSlimAppType(node) {
|
|
4600
|
+
if (!node || node.type !== "named_type") return false;
|
|
4601
|
+
const text = node.text;
|
|
4602
|
+
return text === "App" || text.endsWith("\\App");
|
|
4603
|
+
}
|
|
4604
|
+
function collectSlimAppVars(root) {
|
|
4605
|
+
const vars = /* @__PURE__ */ new Set();
|
|
4606
|
+
walk(root, (node) => {
|
|
4607
|
+
if (node.type === "assignment_expression") {
|
|
4608
|
+
const left = node.childForFieldName("left");
|
|
4609
|
+
if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
|
|
4610
|
+
vars.add(left.text);
|
|
4611
|
+
}
|
|
4612
|
+
return;
|
|
4613
|
+
}
|
|
4614
|
+
if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
|
|
4615
|
+
const name = node.childForFieldName("name");
|
|
4616
|
+
if (name?.type === "variable_name") vars.add(name.text);
|
|
4617
|
+
}
|
|
4618
|
+
});
|
|
4619
|
+
return vars;
|
|
4620
|
+
}
|
|
4621
|
+
function slimClosureParamVars(closure) {
|
|
4622
|
+
const out = ["$this"];
|
|
4623
|
+
for (let i = 0; i < closure.namedChildCount; i++) {
|
|
4624
|
+
const params = closure.namedChild(i);
|
|
4625
|
+
if (params?.type !== "formal_parameters") continue;
|
|
4626
|
+
for (let j = 0; j < params.namedChildCount; j++) {
|
|
4627
|
+
const param = params.namedChild(j);
|
|
4628
|
+
if (param?.type !== "simple_parameter") continue;
|
|
4629
|
+
for (let k = 0; k < param.namedChildCount; k++) {
|
|
4630
|
+
const v = param.namedChild(k);
|
|
4631
|
+
if (v?.type === "variable_name") {
|
|
4632
|
+
out.push(v.text);
|
|
4633
|
+
break;
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
return out;
|
|
4639
|
+
}
|
|
4640
|
+
function phpStringArray(node) {
|
|
4641
|
+
const out = [];
|
|
4642
|
+
if (node?.type !== "array_creation_expression") return out;
|
|
4643
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4644
|
+
const el = node.namedChild(i);
|
|
4645
|
+
if (el?.type !== "array_element_initializer") continue;
|
|
4646
|
+
const s = phpStaticString(el.namedChild(0));
|
|
4647
|
+
if (s !== null) out.push(s);
|
|
4648
|
+
}
|
|
4649
|
+
return out;
|
|
4650
|
+
}
|
|
4651
|
+
function slimRoutesFromSource(source, parser) {
|
|
4652
|
+
const tree = parseSource2(parser, source);
|
|
4653
|
+
const appVars = collectSlimAppVars(tree.rootNode);
|
|
4654
|
+
if (appVars.size === 0) return [];
|
|
4655
|
+
const out = [];
|
|
4656
|
+
slimWalk(tree.rootNode, "", appVars, out);
|
|
4657
|
+
return out;
|
|
4658
|
+
}
|
|
4659
|
+
function slimWalk(node, prefix, appVars, out) {
|
|
4660
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4661
|
+
const child = node.namedChild(i);
|
|
4662
|
+
if (child) slimHandle(child, prefix, appVars, out);
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
function slimHandle(node, prefix, appVars, out) {
|
|
4666
|
+
if (node.type === "member_call_expression") {
|
|
4667
|
+
const obj = node.childForFieldName("object");
|
|
4668
|
+
const method = node.childForFieldName("name")?.text;
|
|
4669
|
+
const args = node.childForFieldName("arguments");
|
|
4670
|
+
if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
|
|
4671
|
+
const line = node.startPosition.row + 1;
|
|
4672
|
+
if (method === "group") {
|
|
4673
|
+
const groupPrefix = phpFirstString(args);
|
|
4674
|
+
const closure = laravelGroupClosure(args);
|
|
4675
|
+
if (groupPrefix !== null && closure) {
|
|
4676
|
+
const inner = new Set(appVars);
|
|
4677
|
+
for (const v of slimClosureParamVars(closure)) inner.add(v);
|
|
4678
|
+
const body = closure.childForFieldName("body");
|
|
4679
|
+
if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
|
|
4680
|
+
}
|
|
4681
|
+
return;
|
|
4682
|
+
}
|
|
4683
|
+
if (SLIM_VERBS.has(method)) {
|
|
4684
|
+
const p = phpFirstString(args);
|
|
4685
|
+
if (p !== null) {
|
|
4686
|
+
out.push({
|
|
4687
|
+
method: method.toUpperCase(),
|
|
4688
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4689
|
+
line,
|
|
4690
|
+
framework: "slim"
|
|
4691
|
+
});
|
|
4692
|
+
}
|
|
4693
|
+
return;
|
|
4694
|
+
}
|
|
4695
|
+
if (method === "any") {
|
|
4696
|
+
const p = phpFirstString(args);
|
|
4697
|
+
if (p !== null) {
|
|
4698
|
+
out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
|
|
4699
|
+
}
|
|
4700
|
+
return;
|
|
4701
|
+
}
|
|
4702
|
+
if (method === "map") {
|
|
4703
|
+
const vals = phpArgumentValues(args);
|
|
4704
|
+
const methods = phpStringArray(vals[0]);
|
|
4705
|
+
const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
|
|
4706
|
+
if (p !== null) {
|
|
4707
|
+
for (const m of methods) {
|
|
4708
|
+
out.push({
|
|
4709
|
+
method: m.toUpperCase(),
|
|
4710
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4711
|
+
line,
|
|
4712
|
+
framework: "slim"
|
|
4713
|
+
});
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
slimWalk(node, prefix, appVars, out);
|
|
4721
|
+
}
|
|
4722
|
+
var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
|
|
4723
|
+
function rustStringContent(node) {
|
|
4724
|
+
if (!node || node.type !== "string_literal") return null;
|
|
4725
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4726
|
+
if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
|
|
4727
|
+
}
|
|
4728
|
+
return "";
|
|
4729
|
+
}
|
|
4730
|
+
function actixRoutesFromSource(source, parser) {
|
|
4731
|
+
const tree = parseSource2(parser, source);
|
|
4732
|
+
const out = [];
|
|
4733
|
+
walk(tree.rootNode, (node) => {
|
|
4734
|
+
if (node.type === "attribute_item") {
|
|
4735
|
+
actixAttributeRoute(node, out);
|
|
4736
|
+
return;
|
|
4737
|
+
}
|
|
4738
|
+
if (node.type === "call_expression") {
|
|
4739
|
+
actixBuilderRoute(node, out);
|
|
4740
|
+
}
|
|
4741
|
+
});
|
|
4742
|
+
return out;
|
|
4743
|
+
}
|
|
4744
|
+
function actixAttributeRoute(attrItem, out) {
|
|
4745
|
+
const attr = attrItem.namedChild(0);
|
|
4746
|
+
if (!attr || attr.type !== "attribute") return;
|
|
4747
|
+
const nameNode = attr.namedChild(0);
|
|
4748
|
+
if (!nameNode) return;
|
|
4749
|
+
const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
|
|
4750
|
+
if (!macro) return;
|
|
4751
|
+
const tokens = attr.childForFieldName("arguments");
|
|
4752
|
+
if (!tokens || tokens.type !== "token_tree") return;
|
|
4753
|
+
const strings = [];
|
|
4754
|
+
for (let i = 0; i < tokens.namedChildCount; i++) {
|
|
4755
|
+
const s = rustStringContent(tokens.namedChild(i));
|
|
4756
|
+
if (s !== null) strings.push(s);
|
|
4757
|
+
}
|
|
4758
|
+
const pathStr = strings[0];
|
|
4759
|
+
if (pathStr === void 0 || !pathStr.startsWith("/")) return;
|
|
4760
|
+
const line = attrItem.startPosition.row + 1;
|
|
4761
|
+
const template = canonicalizeTemplate(pathStr);
|
|
4762
|
+
if (ACTIX_METHODS.has(macro)) {
|
|
4763
|
+
out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
|
|
4764
|
+
return;
|
|
4765
|
+
}
|
|
4766
|
+
if (macro === "route") {
|
|
4767
|
+
const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
|
|
4768
|
+
const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
|
|
4769
|
+
for (const m of list) {
|
|
4770
|
+
out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4774
|
+
function actixBuilderRoute(call, out) {
|
|
4775
|
+
const fn = call.childForFieldName("function");
|
|
4776
|
+
if (fn?.type !== "field_expression") return;
|
|
4777
|
+
if (fn.childForFieldName("field")?.text !== "route") return;
|
|
4778
|
+
const args = call.childForFieldName("arguments");
|
|
4779
|
+
const pathStr = rustStringContent(args?.namedChild(0));
|
|
4780
|
+
if (pathStr === null || !pathStr.startsWith("/")) return;
|
|
4781
|
+
const second = args?.namedChild(1);
|
|
4782
|
+
if (!second) return;
|
|
4783
|
+
const method = actixBuilderMethod(second);
|
|
4784
|
+
if (!method) return;
|
|
4785
|
+
out.push({
|
|
4786
|
+
method,
|
|
4787
|
+
pathTemplate: canonicalizeTemplate(pathStr),
|
|
4788
|
+
line: call.startPosition.row + 1,
|
|
4789
|
+
framework: "actix-web"
|
|
4790
|
+
});
|
|
4791
|
+
}
|
|
4792
|
+
function actixBuilderMethod(node) {
|
|
4793
|
+
let method = null;
|
|
4794
|
+
walk(node, (n) => {
|
|
4795
|
+
if (method || n.type !== "scoped_identifier") return;
|
|
4796
|
+
const verb = n.childForFieldName("name")?.text;
|
|
4797
|
+
const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
|
|
4798
|
+
if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
|
|
4799
|
+
});
|
|
4800
|
+
return method;
|
|
4801
|
+
}
|
|
4454
4802
|
function namedArgs(argsNode) {
|
|
4455
4803
|
const out = [];
|
|
4456
4804
|
if (!argsNode) return out;
|
|
@@ -4767,6 +5115,7 @@ async function addRoutes(graph, services) {
|
|
|
4767
5115
|
const goParser = makeGoParser2();
|
|
4768
5116
|
const rubyParser = makeRubyParser();
|
|
4769
5117
|
const phpParser = makePhpParser();
|
|
5118
|
+
const rustParser = makeRustParser();
|
|
4770
5119
|
let nodesAdded = 0;
|
|
4771
5120
|
let edgesAdded = 0;
|
|
4772
5121
|
for (const service of services) {
|
|
@@ -4789,7 +5138,10 @@ async function addRoutes(graph, services) {
|
|
|
4789
5138
|
const isGoService = service.node.language === "go";
|
|
4790
5139
|
const hasRails = deps["rails"] !== void 0;
|
|
4791
5140
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4792
|
-
|
|
5141
|
+
const hasSlim = deps["slim/slim"] !== void 0;
|
|
5142
|
+
const hasSinatra = deps["sinatra"] !== void 0;
|
|
5143
|
+
const hasActix = deps["actix-web"] !== void 0;
|
|
5144
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
|
|
4793
5145
|
continue;
|
|
4794
5146
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
4795
5147
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4800,7 +5152,8 @@ async function addRoutes(graph, services) {
|
|
|
4800
5152
|
const isGo = ext === ".go";
|
|
4801
5153
|
const isRb = ext === ".rb";
|
|
4802
5154
|
const isPhp = ext === ".php";
|
|
4803
|
-
|
|
5155
|
+
const isRs = ext === ".rs";
|
|
5156
|
+
if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
|
|
4804
5157
|
const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
|
|
4805
5158
|
let routes;
|
|
4806
5159
|
try {
|
|
@@ -4810,8 +5163,12 @@ async function addRoutes(graph, services) {
|
|
|
4810
5163
|
phpParser,
|
|
4811
5164
|
relFile === "routes/api.php" ? "/api" : ""
|
|
4812
5165
|
) : [];
|
|
5166
|
+
if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
|
|
4813
5167
|
} else if (isRb) {
|
|
4814
5168
|
routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
|
|
5169
|
+
if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
|
|
5170
|
+
} else if (isRs) {
|
|
5171
|
+
routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
|
|
4815
5172
|
} else if (isGo) {
|
|
4816
5173
|
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4817
5174
|
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
@@ -5046,6 +5403,37 @@ function loadIncidentThresholdsFromEnv() {
|
|
|
5046
5403
|
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
5047
5404
|
}
|
|
5048
5405
|
}
|
|
5406
|
+
var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
|
|
5407
|
+
function latencyStreamCeilingMs() {
|
|
5408
|
+
const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
|
|
5409
|
+
if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5410
|
+
const n = Number(raw);
|
|
5411
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
5412
|
+
console.warn(
|
|
5413
|
+
`[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
|
|
5414
|
+
);
|
|
5415
|
+
return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5416
|
+
}
|
|
5417
|
+
function spanServesEventStream(attrs) {
|
|
5418
|
+
for (const key of [
|
|
5419
|
+
"http.response.header.content-type",
|
|
5420
|
+
"http.response.header.content_type"
|
|
5421
|
+
]) {
|
|
5422
|
+
const v = attrs[key];
|
|
5423
|
+
const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
|
|
5424
|
+
for (const item of values) {
|
|
5425
|
+
if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
|
|
5426
|
+
return true;
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
return false;
|
|
5431
|
+
}
|
|
5432
|
+
function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
|
|
5433
|
+
if (span.websocketChannel !== void 0) return true;
|
|
5434
|
+
if (spanServesEventStream(span.attributes)) return true;
|
|
5435
|
+
return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
|
|
5436
|
+
}
|
|
5049
5437
|
function httpResponseStatusFromAttrs(attrs) {
|
|
5050
5438
|
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
5051
5439
|
const v = attrs[key];
|
|
@@ -5938,6 +6326,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
|
5938
6326
|
);
|
|
5939
6327
|
ctx.burstState.delete(key);
|
|
5940
6328
|
}
|
|
6329
|
+
var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
|
|
6330
|
+
function nextApiRouteTemplate(span) {
|
|
6331
|
+
const raw = pickAttr(span, "next.span_name") ?? span.name;
|
|
6332
|
+
const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
|
|
6333
|
+
return match ? match[1] : void 0;
|
|
6334
|
+
}
|
|
5941
6335
|
function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
|
|
5942
6336
|
const target = normalizePathTemplate(httpRoute);
|
|
5943
6337
|
const m = method?.toUpperCase();
|
|
@@ -5960,7 +6354,7 @@ async function handleSpan(ctx, span) {
|
|
|
5960
6354
|
}
|
|
5961
6355
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
5962
6356
|
const isError = span.statusCode === 2;
|
|
5963
|
-
const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6357
|
+
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
5964
6358
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
5965
6359
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
5966
6360
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -6157,12 +6551,13 @@ async function handleSpan(ctx, span) {
|
|
|
6157
6551
|
}
|
|
6158
6552
|
}
|
|
6159
6553
|
}
|
|
6160
|
-
|
|
6554
|
+
const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
|
|
6555
|
+
if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
|
|
6161
6556
|
const routeNodeId = findRouteNodeByHttpRoute(
|
|
6162
6557
|
ctx.graph,
|
|
6163
6558
|
span.service,
|
|
6164
6559
|
span.httpMethod,
|
|
6165
|
-
|
|
6560
|
+
fusionRoute
|
|
6166
6561
|
);
|
|
6167
6562
|
if (routeNodeId) {
|
|
6168
6563
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
@@ -7550,7 +7945,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
|
|
|
7550
7945
|
var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
7551
7946
|
var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
|
|
7552
7947
|
var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
|
|
7553
|
-
var
|
|
7948
|
+
var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
|
|
7554
7949
|
var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
|
|
7555
7950
|
var import_types20 = require("@neat.is/types");
|
|
7556
7951
|
var PARSE_CHUNK3 = 16384;
|
|
@@ -7571,7 +7966,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
|
|
|
7571
7966
|
".cs": import_tree_sitter_c_sharp.default,
|
|
7572
7967
|
".java": import_tree_sitter_java.default,
|
|
7573
7968
|
".kt": import_tree_sitter_kotlin.default,
|
|
7574
|
-
".rs":
|
|
7969
|
+
".rs": import_tree_sitter_rust2.default,
|
|
7575
7970
|
// C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
|
|
7576
7971
|
// `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
|
|
7577
7972
|
// headers. `.h` and `.c` are deliberately absent: they are shared with C (a
|
|
@@ -8011,15 +8406,15 @@ function collectKotlinSymbolDefs(root) {
|
|
|
8011
8406
|
});
|
|
8012
8407
|
};
|
|
8013
8408
|
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
8014
|
-
const
|
|
8409
|
+
const firstChildOfType2 = (node, types) => {
|
|
8015
8410
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8016
8411
|
const child = node.namedChild(i);
|
|
8017
8412
|
if (child && types.includes(child.type)) return child;
|
|
8018
8413
|
}
|
|
8019
8414
|
return void 0;
|
|
8020
8415
|
};
|
|
8021
|
-
const nameOf = (node, ...types) =>
|
|
8022
|
-
const bodyOf = (node) =>
|
|
8416
|
+
const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
|
|
8417
|
+
const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
|
|
8023
8418
|
let pkg;
|
|
8024
8419
|
for (let i = 0; i < root.namedChildCount; i++) {
|
|
8025
8420
|
const child = root.namedChild(i);
|
|
@@ -8520,7 +8915,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
8520
8915
|
return best;
|
|
8521
8916
|
};
|
|
8522
8917
|
const requests = [];
|
|
8523
|
-
const
|
|
8918
|
+
const walk10 = (node) => {
|
|
8524
8919
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
8525
8920
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
8526
8921
|
if (self && self.kind === "class") {
|
|
@@ -8566,10 +8961,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
8566
8961
|
}
|
|
8567
8962
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8568
8963
|
const child = node.namedChild(i);
|
|
8569
|
-
if (child)
|
|
8964
|
+
if (child) walk10(child);
|
|
8570
8965
|
}
|
|
8571
8966
|
};
|
|
8572
|
-
|
|
8967
|
+
walk10(root);
|
|
8573
8968
|
for (const req2 of requests) {
|
|
8574
8969
|
const targetSid = resolveTarget(req2.targetName, req2.wantKind);
|
|
8575
8970
|
if (!targetSid) continue;
|
|
@@ -8865,7 +9260,7 @@ async function addServerActions(graph, services) {
|
|
|
8865
9260
|
|
|
8866
9261
|
// src/extract/databases/index.ts
|
|
8867
9262
|
init_cjs_shims();
|
|
8868
|
-
var
|
|
9263
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
8869
9264
|
var import_types23 = require("@neat.is/types");
|
|
8870
9265
|
|
|
8871
9266
|
// src/extract/databases/db-config-yaml.ts
|
|
@@ -9300,9 +9695,163 @@ async function parse8(serviceDir) {
|
|
|
9300
9695
|
}
|
|
9301
9696
|
var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
9302
9697
|
|
|
9303
|
-
// src/extract/databases/
|
|
9698
|
+
// src/extract/databases/csharp.ts
|
|
9304
9699
|
init_cjs_shims();
|
|
9700
|
+
var import_node_fs22 = require("fs");
|
|
9305
9701
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
9702
|
+
var CS_EXT = ".cs";
|
|
9703
|
+
var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
|
|
9704
|
+
var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
|
|
9705
|
+
var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
|
|
9706
|
+
var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
9707
|
+
function hostIsUnresolved(host) {
|
|
9708
|
+
return host === "" || /[${}]/.test(host);
|
|
9709
|
+
}
|
|
9710
|
+
function looksLikePostgres(s) {
|
|
9711
|
+
return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
|
|
9712
|
+
}
|
|
9713
|
+
function looksLikeRedis(s) {
|
|
9714
|
+
return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
|
|
9715
|
+
}
|
|
9716
|
+
function parsePostgresConnection(raw) {
|
|
9717
|
+
const s = raw.trim();
|
|
9718
|
+
if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9719
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9720
|
+
for (const part of s.split(";")) {
|
|
9721
|
+
const eq = part.indexOf("=");
|
|
9722
|
+
if (eq < 0) continue;
|
|
9723
|
+
const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
|
|
9724
|
+
const value = part.slice(eq + 1).trim();
|
|
9725
|
+
if (value && !fields.has(key)) fields.set(key, value);
|
|
9726
|
+
}
|
|
9727
|
+
const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
|
|
9728
|
+
if (!hostRaw) return null;
|
|
9729
|
+
const host = hostRaw.split(",")[0].trim();
|
|
9730
|
+
if (hostIsUnresolved(host)) return null;
|
|
9731
|
+
const portRaw = fields.get("port");
|
|
9732
|
+
const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
|
|
9733
|
+
const database = fields.get("database") ?? fields.get("db") ?? "";
|
|
9734
|
+
return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
|
|
9735
|
+
}
|
|
9736
|
+
function parseRedisEndpoint(raw) {
|
|
9737
|
+
const s = raw.trim();
|
|
9738
|
+
if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9739
|
+
const first = s.split(",")[0].trim();
|
|
9740
|
+
const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
|
|
9741
|
+
if (!m) return null;
|
|
9742
|
+
const host = m[1];
|
|
9743
|
+
if (hostIsUnresolved(host) || host.includes("=")) return null;
|
|
9744
|
+
const port = m[2] ? Number(m[2]) : void 0;
|
|
9745
|
+
return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
|
|
9746
|
+
}
|
|
9747
|
+
async function resolveEnvUpTree(startDir, name) {
|
|
9748
|
+
let dir = import_node_path32.default.resolve(startDir);
|
|
9749
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
9750
|
+
const value = await resolveEnvVar(dir, name);
|
|
9751
|
+
if (value !== null) return value;
|
|
9752
|
+
const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
|
|
9753
|
+
const parent = import_node_path32.default.dirname(dir);
|
|
9754
|
+
if (atRepoRoot || parent === dir) break;
|
|
9755
|
+
dir = parent;
|
|
9756
|
+
}
|
|
9757
|
+
return null;
|
|
9758
|
+
}
|
|
9759
|
+
async function interpolateEnvRefs(value, dir) {
|
|
9760
|
+
const refs = /* @__PURE__ */ new Set();
|
|
9761
|
+
for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
9762
|
+
refs.add(m[1] ?? m[2]);
|
|
9763
|
+
}
|
|
9764
|
+
let out = value;
|
|
9765
|
+
for (const name of refs) {
|
|
9766
|
+
const resolved = await resolveEnvUpTree(dir, name);
|
|
9767
|
+
if (resolved === null) continue;
|
|
9768
|
+
out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
|
|
9769
|
+
}
|
|
9770
|
+
return out;
|
|
9771
|
+
}
|
|
9772
|
+
function stringLiterals(masked) {
|
|
9773
|
+
const out = [];
|
|
9774
|
+
STRING_LITERAL_RE.lastIndex = 0;
|
|
9775
|
+
let m;
|
|
9776
|
+
while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
|
|
9777
|
+
return out;
|
|
9778
|
+
}
|
|
9779
|
+
function envKeys(masked) {
|
|
9780
|
+
const out = [];
|
|
9781
|
+
ENV_READ_RE.lastIndex = 0;
|
|
9782
|
+
let m;
|
|
9783
|
+
while ((m = ENV_READ_RE.exec(masked)) !== null) {
|
|
9784
|
+
const key = m[1] ?? m[2];
|
|
9785
|
+
if (key) out.push(key);
|
|
9786
|
+
}
|
|
9787
|
+
return out;
|
|
9788
|
+
}
|
|
9789
|
+
async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
|
|
9790
|
+
const out = [];
|
|
9791
|
+
for (const key of keys) {
|
|
9792
|
+
const raw = await resolveEnvUpTree(serviceDir, key);
|
|
9793
|
+
if (raw === null) continue;
|
|
9794
|
+
const value = await interpolateEnvRefs(raw, serviceDir);
|
|
9795
|
+
if (!looksLike(value)) continue;
|
|
9796
|
+
const parsed = parse11(value);
|
|
9797
|
+
if (parsed) out.push(parsed);
|
|
9798
|
+
}
|
|
9799
|
+
for (const lit of literals) {
|
|
9800
|
+
if (!looksLike(lit)) continue;
|
|
9801
|
+
const parsed = parse11(lit);
|
|
9802
|
+
if (parsed) out.push(parsed);
|
|
9803
|
+
}
|
|
9804
|
+
return out;
|
|
9805
|
+
}
|
|
9806
|
+
async function parse9(serviceDir) {
|
|
9807
|
+
const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
|
|
9808
|
+
(f) => import_node_path32.default.extname(f) === CS_EXT
|
|
9809
|
+
);
|
|
9810
|
+
if (files.length === 0) return [];
|
|
9811
|
+
const sources = [];
|
|
9812
|
+
for (const file of files) {
|
|
9813
|
+
const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
|
|
9814
|
+
if (content !== null) sources.push({ file, content });
|
|
9815
|
+
}
|
|
9816
|
+
let pgGateFile = null;
|
|
9817
|
+
let redisGateFile = null;
|
|
9818
|
+
for (const { file, content } of sources) {
|
|
9819
|
+
if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
|
|
9820
|
+
if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
|
|
9821
|
+
}
|
|
9822
|
+
if (!pgGateFile && !redisGateFile) return [];
|
|
9823
|
+
const literals = [];
|
|
9824
|
+
const keys = [];
|
|
9825
|
+
for (const { content } of sources) {
|
|
9826
|
+
const masked = maskCommentsInSource(content);
|
|
9827
|
+
literals.push(...stringLiterals(masked));
|
|
9828
|
+
keys.push(...envKeys(masked));
|
|
9829
|
+
}
|
|
9830
|
+
const out = [];
|
|
9831
|
+
const seenHosts = /* @__PURE__ */ new Set();
|
|
9832
|
+
const push = (config, sourceFile) => {
|
|
9833
|
+
const dedupe = `${config.engine}:${config.host}`;
|
|
9834
|
+
if (seenHosts.has(dedupe)) return;
|
|
9835
|
+
seenHosts.add(dedupe);
|
|
9836
|
+
out.push({ ...config, sourceFile });
|
|
9837
|
+
};
|
|
9838
|
+
if (pgGateFile) {
|
|
9839
|
+
for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
|
|
9840
|
+
push(pg3, pgGateFile);
|
|
9841
|
+
}
|
|
9842
|
+
}
|
|
9843
|
+
if (redisGateFile) {
|
|
9844
|
+
for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
|
|
9845
|
+
push(redis, redisGateFile);
|
|
9846
|
+
}
|
|
9847
|
+
}
|
|
9848
|
+
return out;
|
|
9849
|
+
}
|
|
9850
|
+
var csharpParser = { name: "csharp", parse: parse9 };
|
|
9851
|
+
|
|
9852
|
+
// src/extract/databases/docker-compose.ts
|
|
9853
|
+
init_cjs_shims();
|
|
9854
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
9306
9855
|
function portFromService(svc) {
|
|
9307
9856
|
for (const raw of svc.ports ?? []) {
|
|
9308
9857
|
const str = String(raw);
|
|
@@ -9327,9 +9876,9 @@ function databaseFromEnv(svc) {
|
|
|
9327
9876
|
};
|
|
9328
9877
|
return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
|
|
9329
9878
|
}
|
|
9330
|
-
async function
|
|
9879
|
+
async function parse10(serviceDir) {
|
|
9331
9880
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
9332
|
-
const abs =
|
|
9881
|
+
const abs = import_node_path33.default.join(serviceDir, name);
|
|
9333
9882
|
if (!await exists(abs)) continue;
|
|
9334
9883
|
const raw = await readYaml(abs);
|
|
9335
9884
|
if (!raw?.services) return [];
|
|
@@ -9351,7 +9900,7 @@ async function parse9(serviceDir) {
|
|
|
9351
9900
|
}
|
|
9352
9901
|
return [];
|
|
9353
9902
|
}
|
|
9354
|
-
var dockerComposeParser = { name: "docker-compose", parse:
|
|
9903
|
+
var dockerComposeParser = { name: "docker-compose", parse: parse10 };
|
|
9355
9904
|
|
|
9356
9905
|
// src/extract/databases/index.ts
|
|
9357
9906
|
var DB_PARSERS = [
|
|
@@ -9363,6 +9912,7 @@ var DB_PARSERS = [
|
|
|
9363
9912
|
ormconfigParser,
|
|
9364
9913
|
typeormParser,
|
|
9365
9914
|
sequelizeParser,
|
|
9915
|
+
csharpParser,
|
|
9366
9916
|
dockerComposeParser
|
|
9367
9917
|
];
|
|
9368
9918
|
function compatibleDriversFor(engine) {
|
|
@@ -9501,7 +10051,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9501
10051
|
discoveredVia: mergedDiscoveredVia
|
|
9502
10052
|
});
|
|
9503
10053
|
}
|
|
9504
|
-
const relConfigFile = toPosix(
|
|
10054
|
+
const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
|
|
9505
10055
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9506
10056
|
graph,
|
|
9507
10057
|
service.pkg.name,
|
|
@@ -9510,7 +10060,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9510
10060
|
);
|
|
9511
10061
|
nodesAdded += fn;
|
|
9512
10062
|
edgesAdded += fe;
|
|
9513
|
-
const evidenceFile = toPosix(
|
|
10063
|
+
const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
|
|
9514
10064
|
const edge = {
|
|
9515
10065
|
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
|
|
9516
10066
|
source: fileNodeId,
|
|
@@ -9528,15 +10078,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9528
10078
|
if (allConfigs.length === 1) {
|
|
9529
10079
|
const primary = allConfigs[0];
|
|
9530
10080
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
9531
|
-
const relPath =
|
|
10081
|
+
const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
|
|
9532
10082
|
const cfgId = (0, import_types23.configId)(relPath);
|
|
9533
10083
|
if (!graph.hasNode(cfgId)) {
|
|
9534
10084
|
const cfgNode = {
|
|
9535
10085
|
id: cfgId,
|
|
9536
10086
|
type: import_types23.NodeType.ConfigNode,
|
|
9537
|
-
name:
|
|
10087
|
+
name: import_node_path34.default.basename(primary.sourceFile),
|
|
9538
10088
|
path: relPath,
|
|
9539
|
-
fileType: isConfigFile(
|
|
10089
|
+
fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
|
|
9540
10090
|
};
|
|
9541
10091
|
graph.addNode(cfgId, cfgNode);
|
|
9542
10092
|
nodesAdded++;
|
|
@@ -9577,27 +10127,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9577
10127
|
|
|
9578
10128
|
// src/extract/configs.ts
|
|
9579
10129
|
init_cjs_shims();
|
|
9580
|
-
var
|
|
9581
|
-
var
|
|
10130
|
+
var import_node_fs23 = require("fs");
|
|
10131
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
9582
10132
|
var import_types24 = require("@neat.is/types");
|
|
9583
10133
|
async function walkConfigFiles(dir, excludeDirs = []) {
|
|
9584
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10134
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
|
|
9585
10135
|
const out = [];
|
|
9586
|
-
async function
|
|
9587
|
-
const entries = await
|
|
10136
|
+
async function walk10(current) {
|
|
10137
|
+
const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
|
|
9588
10138
|
for (const entry2 of entries) {
|
|
9589
|
-
const full =
|
|
10139
|
+
const full = import_node_path35.default.join(current, entry2.name);
|
|
9590
10140
|
if (entry2.isDirectory()) {
|
|
9591
10141
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
9592
|
-
if (excluded.has(
|
|
10142
|
+
if (excluded.has(import_node_path35.default.resolve(full))) continue;
|
|
9593
10143
|
if (await isPythonVenvDir(full)) continue;
|
|
9594
|
-
await
|
|
10144
|
+
await walk10(full);
|
|
9595
10145
|
} else if (entry2.isFile() && isConfigFile(entry2.name).match) {
|
|
9596
10146
|
out.push(full);
|
|
9597
10147
|
}
|
|
9598
10148
|
}
|
|
9599
10149
|
}
|
|
9600
|
-
await
|
|
10150
|
+
await walk10(dir);
|
|
9601
10151
|
return out;
|
|
9602
10152
|
}
|
|
9603
10153
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -9606,19 +10156,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9606
10156
|
for (const service of services) {
|
|
9607
10157
|
const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
|
|
9608
10158
|
for (const file of configFiles) {
|
|
9609
|
-
const relPath =
|
|
10159
|
+
const relPath = import_node_path35.default.relative(scanPath, file);
|
|
9610
10160
|
const node = {
|
|
9611
10161
|
id: (0, import_types24.configId)(relPath),
|
|
9612
10162
|
type: import_types24.NodeType.ConfigNode,
|
|
9613
|
-
name:
|
|
10163
|
+
name: import_node_path35.default.basename(file),
|
|
9614
10164
|
path: relPath,
|
|
9615
|
-
fileType: isConfigFile(
|
|
10165
|
+
fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
|
|
9616
10166
|
};
|
|
9617
10167
|
if (!graph.hasNode(node.id)) {
|
|
9618
10168
|
graph.addNode(node.id, node);
|
|
9619
10169
|
nodesAdded++;
|
|
9620
10170
|
}
|
|
9621
|
-
const relToService = toPosix(
|
|
10171
|
+
const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
|
|
9622
10172
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9623
10173
|
graph,
|
|
9624
10174
|
service.pkg.name,
|
|
@@ -9634,7 +10184,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9634
10184
|
type: import_types24.EdgeType.CONFIGURED_BY,
|
|
9635
10185
|
provenance: import_types24.Provenance.EXTRACTED,
|
|
9636
10186
|
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
9637
|
-
evidence: { file: relPath.split(
|
|
10187
|
+
evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
|
|
9638
10188
|
};
|
|
9639
10189
|
if (!graph.hasEdge(edge.id)) {
|
|
9640
10190
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -9647,8 +10197,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9647
10197
|
|
|
9648
10198
|
// src/extract/proto.ts
|
|
9649
10199
|
init_cjs_shims();
|
|
9650
|
-
var
|
|
9651
|
-
var
|
|
10200
|
+
var import_node_fs24 = require("fs");
|
|
10201
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
9652
10202
|
var import_types25 = require("@neat.is/types");
|
|
9653
10203
|
var PROTO_EXTENSION = ".proto";
|
|
9654
10204
|
function packageOf(content) {
|
|
@@ -9686,23 +10236,23 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
9686
10236
|
return out;
|
|
9687
10237
|
}
|
|
9688
10238
|
async function walkProtoFiles(dir, excludeDirs = []) {
|
|
9689
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10239
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
|
|
9690
10240
|
const out = [];
|
|
9691
|
-
async function
|
|
9692
|
-
const entries = await
|
|
10241
|
+
async function walk10(current) {
|
|
10242
|
+
const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
9693
10243
|
for (const entry2 of entries) {
|
|
9694
|
-
const full =
|
|
10244
|
+
const full = import_node_path36.default.join(current, entry2.name);
|
|
9695
10245
|
if (entry2.isDirectory()) {
|
|
9696
10246
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
9697
|
-
if (excluded.has(
|
|
10247
|
+
if (excluded.has(import_node_path36.default.resolve(full))) continue;
|
|
9698
10248
|
if (await isPythonVenvDir(full)) continue;
|
|
9699
|
-
await
|
|
9700
|
-
} else if (entry2.isFile() &&
|
|
10249
|
+
await walk10(full);
|
|
10250
|
+
} else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
|
|
9701
10251
|
out.push(full);
|
|
9702
10252
|
}
|
|
9703
10253
|
}
|
|
9704
10254
|
}
|
|
9705
|
-
await
|
|
10255
|
+
await walk10(dir);
|
|
9706
10256
|
return out;
|
|
9707
10257
|
}
|
|
9708
10258
|
async function addGrpcMethods(graph, services) {
|
|
@@ -9712,10 +10262,10 @@ async function addGrpcMethods(graph, services) {
|
|
|
9712
10262
|
const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
|
|
9713
10263
|
for (const protoPath of protoPaths) {
|
|
9714
10264
|
if (isTestPath(protoPath)) continue;
|
|
9715
|
-
const relFile = toPosix(
|
|
10265
|
+
const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
|
|
9716
10266
|
let content;
|
|
9717
10267
|
try {
|
|
9718
|
-
content = await
|
|
10268
|
+
content = await import_node_fs24.promises.readFile(protoPath, "utf8");
|
|
9719
10269
|
} catch (err) {
|
|
9720
10270
|
recordExtractionError("proto extraction", protoPath, err);
|
|
9721
10271
|
continue;
|
|
@@ -9770,11 +10320,11 @@ async function addGrpcMethods(graph, services) {
|
|
|
9770
10320
|
|
|
9771
10321
|
// src/extract/calls/index.ts
|
|
9772
10322
|
init_cjs_shims();
|
|
9773
|
-
var
|
|
10323
|
+
var import_types44 = require("@neat.is/types");
|
|
9774
10324
|
|
|
9775
10325
|
// src/extract/calls/http.ts
|
|
9776
10326
|
init_cjs_shims();
|
|
9777
|
-
var
|
|
10327
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
9778
10328
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
9779
10329
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9780
10330
|
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
@@ -9857,7 +10407,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9857
10407
|
const seen = /* @__PURE__ */ new Set();
|
|
9858
10408
|
for (const file of files) {
|
|
9859
10409
|
if (isTestPath(file.path)) continue;
|
|
9860
|
-
const parser = parserForExt(
|
|
10410
|
+
const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
|
|
9861
10411
|
let sites;
|
|
9862
10412
|
try {
|
|
9863
10413
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -9866,7 +10416,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9866
10416
|
continue;
|
|
9867
10417
|
}
|
|
9868
10418
|
if (sites.length === 0) continue;
|
|
9869
|
-
const relFile = toPosix(
|
|
10419
|
+
const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
|
|
9870
10420
|
for (const site of sites) {
|
|
9871
10421
|
const targetId = hostToNodeId.get(site.host);
|
|
9872
10422
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -9920,7 +10470,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9920
10470
|
|
|
9921
10471
|
// src/extract/calls/route-match.ts
|
|
9922
10472
|
init_cjs_shims();
|
|
9923
|
-
var
|
|
10473
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
9924
10474
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
9925
10475
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9926
10476
|
var import_types27 = require("@neat.is/types");
|
|
@@ -10119,7 +10669,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10119
10669
|
const seen = /* @__PURE__ */ new Set();
|
|
10120
10670
|
for (const file of files) {
|
|
10121
10671
|
if (isTestPath(file.path)) continue;
|
|
10122
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
10672
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
|
|
10123
10673
|
let sites;
|
|
10124
10674
|
try {
|
|
10125
10675
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -10128,7 +10678,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10128
10678
|
continue;
|
|
10129
10679
|
}
|
|
10130
10680
|
if (sites.length === 0) continue;
|
|
10131
|
-
const relFile = toPosix(
|
|
10681
|
+
const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
|
|
10132
10682
|
for (const site of sites) {
|
|
10133
10683
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
10134
10684
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -10189,7 +10739,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10189
10739
|
|
|
10190
10740
|
// src/extract/calls/kafka.ts
|
|
10191
10741
|
init_cjs_shims();
|
|
10192
|
-
var
|
|
10742
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
10193
10743
|
var import_types28 = require("@neat.is/types");
|
|
10194
10744
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10195
10745
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -10277,13 +10827,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10277
10827
|
// call sites — verified-call-site tier (ADR-066).
|
|
10278
10828
|
confidenceKind: "verified-call-site",
|
|
10279
10829
|
evidence: {
|
|
10280
|
-
file:
|
|
10830
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
10281
10831
|
line,
|
|
10282
10832
|
snippet: snippet(file.content, line)
|
|
10283
10833
|
}
|
|
10284
10834
|
});
|
|
10285
10835
|
};
|
|
10286
|
-
if (
|
|
10836
|
+
if (import_node_path39.default.extname(file.path) === ".go") {
|
|
10287
10837
|
goSaramaEndpoints(file.content, make);
|
|
10288
10838
|
} else {
|
|
10289
10839
|
for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
|
|
@@ -10294,7 +10844,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10294
10844
|
|
|
10295
10845
|
// src/extract/calls/redis.ts
|
|
10296
10846
|
init_cjs_shims();
|
|
10297
|
-
var
|
|
10847
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
10298
10848
|
var import_types29 = require("@neat.is/types");
|
|
10299
10849
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
10300
10850
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -10317,7 +10867,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10317
10867
|
// support tier (ADR-066).
|
|
10318
10868
|
confidenceKind: "url-with-structural-support",
|
|
10319
10869
|
evidence: {
|
|
10320
|
-
file:
|
|
10870
|
+
file: import_node_path40.default.relative(serviceDir, file.path),
|
|
10321
10871
|
line,
|
|
10322
10872
|
snippet: snippet(file.content, line)
|
|
10323
10873
|
}
|
|
@@ -10328,7 +10878,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10328
10878
|
|
|
10329
10879
|
// src/extract/calls/aws.ts
|
|
10330
10880
|
init_cjs_shims();
|
|
10331
|
-
var
|
|
10881
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
10332
10882
|
var import_types30 = require("@neat.is/types");
|
|
10333
10883
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10334
10884
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -10362,7 +10912,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10362
10912
|
// (ADR-066).
|
|
10363
10913
|
confidenceKind: "verified-call-site",
|
|
10364
10914
|
evidence: {
|
|
10365
|
-
file:
|
|
10915
|
+
file: import_node_path41.default.relative(serviceDir, file.path),
|
|
10366
10916
|
line,
|
|
10367
10917
|
snippet: snippet(file.content, line)
|
|
10368
10918
|
}
|
|
@@ -10387,7 +10937,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10387
10937
|
|
|
10388
10938
|
// src/extract/calls/grpc.ts
|
|
10389
10939
|
init_cjs_shims();
|
|
10390
|
-
var
|
|
10940
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
10391
10941
|
var import_types31 = require("@neat.is/types");
|
|
10392
10942
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
10393
10943
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -10446,7 +10996,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10446
10996
|
// tier (ADR-066).
|
|
10447
10997
|
confidenceKind: "verified-call-site",
|
|
10448
10998
|
evidence: {
|
|
10449
|
-
file:
|
|
10999
|
+
file: import_node_path42.default.relative(serviceDir, file.path),
|
|
10450
11000
|
line,
|
|
10451
11001
|
snippet: snippet(file.content, line)
|
|
10452
11002
|
}
|
|
@@ -10457,7 +11007,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10457
11007
|
|
|
10458
11008
|
// src/extract/calls/supabase.ts
|
|
10459
11009
|
init_cjs_shims();
|
|
10460
|
-
var
|
|
11010
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
10461
11011
|
var import_types32 = require("@neat.is/types");
|
|
10462
11012
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
10463
11013
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -10516,7 +11066,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10516
11066
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
10517
11067
|
confidenceKind: "verified-call-site",
|
|
10518
11068
|
evidence: {
|
|
10519
|
-
file:
|
|
11069
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10520
11070
|
line,
|
|
10521
11071
|
snippet: snippet(file.content, line)
|
|
10522
11072
|
}
|
|
@@ -10543,7 +11093,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10543
11093
|
edgeType: "CALLS",
|
|
10544
11094
|
confidenceKind: "verified-call-site",
|
|
10545
11095
|
evidence: {
|
|
10546
|
-
file:
|
|
11096
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10547
11097
|
line,
|
|
10548
11098
|
snippet: snippet(file.content, line)
|
|
10549
11099
|
}
|
|
@@ -10555,7 +11105,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10555
11105
|
|
|
10556
11106
|
// src/extract/calls/firestore.ts
|
|
10557
11107
|
init_cjs_shims();
|
|
10558
|
-
var
|
|
11108
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
10559
11109
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
10560
11110
|
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
10561
11111
|
var import_types33 = require("@neat.is/types");
|
|
@@ -10594,7 +11144,7 @@ function isFirestoreClientFactory(node) {
|
|
|
10594
11144
|
}
|
|
10595
11145
|
function firestoreClientVars(root) {
|
|
10596
11146
|
const vars = /* @__PURE__ */ new Set();
|
|
10597
|
-
const
|
|
11147
|
+
const walk10 = (node) => {
|
|
10598
11148
|
if (node.type === "variable_declarator") {
|
|
10599
11149
|
const name = node.childForFieldName("name");
|
|
10600
11150
|
let value = node.childForFieldName("value");
|
|
@@ -10603,9 +11153,9 @@ function firestoreClientVars(root) {
|
|
|
10603
11153
|
vars.add(name.text);
|
|
10604
11154
|
}
|
|
10605
11155
|
}
|
|
10606
|
-
for (const c of namedChildren(node))
|
|
11156
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10607
11157
|
};
|
|
10608
|
-
|
|
11158
|
+
walk10(root);
|
|
10609
11159
|
return vars;
|
|
10610
11160
|
}
|
|
10611
11161
|
function isClientExpr(node, clientVars) {
|
|
@@ -10726,7 +11276,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10726
11276
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
10727
11277
|
if (!hasClient && !hasAdmin) return [];
|
|
10728
11278
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
10729
|
-
const tree = parseSource3(parserForExt2(
|
|
11279
|
+
const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
|
|
10730
11280
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
10731
11281
|
const collLine = /* @__PURE__ */ new Map();
|
|
10732
11282
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -10760,7 +11310,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10760
11310
|
}
|
|
10761
11311
|
s.add(field);
|
|
10762
11312
|
};
|
|
10763
|
-
const
|
|
11313
|
+
const walk10 = (node) => {
|
|
10764
11314
|
if (node.type === "call_expression") {
|
|
10765
11315
|
const fn = node.childForFieldName("function");
|
|
10766
11316
|
const line = node.startPosition.row + 1;
|
|
@@ -10800,9 +11350,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10800
11350
|
}
|
|
10801
11351
|
}
|
|
10802
11352
|
}
|
|
10803
|
-
for (const c of namedChildren(node))
|
|
11353
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10804
11354
|
};
|
|
10805
|
-
|
|
11355
|
+
walk10(tree.rootNode);
|
|
10806
11356
|
const out = [];
|
|
10807
11357
|
for (const [collPath, line] of collLine) {
|
|
10808
11358
|
const byField = writes.get(collPath);
|
|
@@ -10830,7 +11380,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10830
11380
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
10831
11381
|
...sdkWrites ? { sdkWrites } : {},
|
|
10832
11382
|
evidence: {
|
|
10833
|
-
file:
|
|
11383
|
+
file: import_node_path44.default.relative(serviceDir, file.path),
|
|
10834
11384
|
line,
|
|
10835
11385
|
snippet: snippet(file.content, line)
|
|
10836
11386
|
}
|
|
@@ -10841,7 +11391,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10841
11391
|
|
|
10842
11392
|
// src/extract/calls/mongoose.ts
|
|
10843
11393
|
init_cjs_shims();
|
|
10844
|
-
var
|
|
11394
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
10845
11395
|
var import_types34 = require("@neat.is/types");
|
|
10846
11396
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
10847
11397
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -10992,7 +11542,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
10992
11542
|
kind: r.kind,
|
|
10993
11543
|
edgeType: "CALLS",
|
|
10994
11544
|
confidenceKind: "verified-call-site",
|
|
10995
|
-
evidence: { file:
|
|
11545
|
+
evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
10996
11546
|
};
|
|
10997
11547
|
}
|
|
10998
11548
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -11082,7 +11632,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11082
11632
|
const registry = /* @__PURE__ */ new Map();
|
|
11083
11633
|
for (const f of mongooseFiles) {
|
|
11084
11634
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
11085
|
-
if (fx) registry.set(toPosix(
|
|
11635
|
+
if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
|
|
11086
11636
|
}
|
|
11087
11637
|
if (registry.size === 0) return [];
|
|
11088
11638
|
const out = [];
|
|
@@ -11093,7 +11643,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11093
11643
|
const directColl = /* @__PURE__ */ new Map();
|
|
11094
11644
|
const nsExports = /* @__PURE__ */ new Map();
|
|
11095
11645
|
for (const b of bindings) {
|
|
11096
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
11646
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
|
|
11097
11647
|
if (!resolvedRel) continue;
|
|
11098
11648
|
const fx = registry.get(resolvedRel);
|
|
11099
11649
|
if (!fx) continue;
|
|
@@ -11137,7 +11687,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11137
11687
|
|
|
11138
11688
|
// src/extract/calls/sqlalchemy.ts
|
|
11139
11689
|
init_cjs_shims();
|
|
11140
|
-
var
|
|
11690
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
11141
11691
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
11142
11692
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
11143
11693
|
var import_types35 = require("@neat.is/types");
|
|
@@ -11276,7 +11826,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
11276
11826
|
childTable,
|
|
11277
11827
|
parentTable,
|
|
11278
11828
|
evidence: {
|
|
11279
|
-
file:
|
|
11829
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11280
11830
|
line,
|
|
11281
11831
|
snippet: snippet(file.content, line)
|
|
11282
11832
|
}
|
|
@@ -11301,7 +11851,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
11301
11851
|
confidenceKind: "verified-call-site",
|
|
11302
11852
|
...columns && columns.length > 0 ? { columns } : {},
|
|
11303
11853
|
evidence: {
|
|
11304
|
-
file:
|
|
11854
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11305
11855
|
line,
|
|
11306
11856
|
snippet: snippet(file.content, line)
|
|
11307
11857
|
}
|
|
@@ -11408,7 +11958,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11408
11958
|
edgeType: "CALLS",
|
|
11409
11959
|
confidenceKind: "verified-call-site",
|
|
11410
11960
|
evidence: {
|
|
11411
|
-
file:
|
|
11961
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11412
11962
|
line,
|
|
11413
11963
|
snippet: snippet(file.content, line)
|
|
11414
11964
|
}
|
|
@@ -11420,7 +11970,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11420
11970
|
|
|
11421
11971
|
// src/extract/calls/django-orm.ts
|
|
11422
11972
|
init_cjs_shims();
|
|
11423
|
-
var
|
|
11973
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
11424
11974
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
11425
11975
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
11426
11976
|
var import_types36 = require("@neat.is/types");
|
|
@@ -11490,7 +12040,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11490
12040
|
const tree = parseSource7(makePyParser4(), file.content);
|
|
11491
12041
|
const out = [];
|
|
11492
12042
|
const seen = /* @__PURE__ */ new Set();
|
|
11493
|
-
const defaultAppLabel =
|
|
12043
|
+
const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
|
|
11494
12044
|
walk4(tree.rootNode, (node) => {
|
|
11495
12045
|
if (node.type !== "class_definition") return;
|
|
11496
12046
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -11508,7 +12058,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11508
12058
|
kind: "sql-table",
|
|
11509
12059
|
edgeType: "CALLS",
|
|
11510
12060
|
confidenceKind: "verified-call-site",
|
|
11511
|
-
evidence: { file:
|
|
12061
|
+
evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11512
12062
|
});
|
|
11513
12063
|
});
|
|
11514
12064
|
return out;
|
|
@@ -11516,7 +12066,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11516
12066
|
|
|
11517
12067
|
// src/extract/calls/drizzle.ts
|
|
11518
12068
|
init_cjs_shims();
|
|
11519
|
-
var
|
|
12069
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
11520
12070
|
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
11521
12071
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
11522
12072
|
var import_types37 = require("@neat.is/types");
|
|
@@ -11594,10 +12144,10 @@ function columnsFromObject(obj) {
|
|
|
11594
12144
|
}
|
|
11595
12145
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
11596
12146
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11597
|
-
const tree = parseSource3(parserForExt3(
|
|
12147
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11598
12148
|
const out = [];
|
|
11599
12149
|
const seen = /* @__PURE__ */ new Set();
|
|
11600
|
-
const
|
|
12150
|
+
const walk10 = (node) => {
|
|
11601
12151
|
if (node.type === "call_expression") {
|
|
11602
12152
|
const fn = node.childForFieldName("function");
|
|
11603
12153
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11617,7 +12167,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11617
12167
|
confidenceKind: "structural",
|
|
11618
12168
|
columns,
|
|
11619
12169
|
evidence: {
|
|
11620
|
-
file:
|
|
12170
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11621
12171
|
line,
|
|
11622
12172
|
snippet: snippet(file.content, line)
|
|
11623
12173
|
}
|
|
@@ -11625,9 +12175,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11625
12175
|
}
|
|
11626
12176
|
}
|
|
11627
12177
|
}
|
|
11628
|
-
for (const c of namedChildren4(node))
|
|
12178
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11629
12179
|
};
|
|
11630
|
-
|
|
12180
|
+
walk10(tree.rootNode);
|
|
11631
12181
|
return out;
|
|
11632
12182
|
}
|
|
11633
12183
|
function enclosingVarName(call) {
|
|
@@ -11649,7 +12199,7 @@ function enclosingVarName(call) {
|
|
|
11649
12199
|
function collectDrizzleTables(root) {
|
|
11650
12200
|
const tables = [];
|
|
11651
12201
|
const varToTable = /* @__PURE__ */ new Map();
|
|
11652
|
-
const
|
|
12202
|
+
const walk10 = (node) => {
|
|
11653
12203
|
if (node.type === "call_expression") {
|
|
11654
12204
|
const fn = node.childForFieldName("function");
|
|
11655
12205
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11664,9 +12214,9 @@ function collectDrizzleTables(root) {
|
|
|
11664
12214
|
}
|
|
11665
12215
|
}
|
|
11666
12216
|
}
|
|
11667
|
-
for (const c of namedChildren4(node))
|
|
12217
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11668
12218
|
};
|
|
11669
|
-
|
|
12219
|
+
walk10(root);
|
|
11670
12220
|
return { tables, varToTable };
|
|
11671
12221
|
}
|
|
11672
12222
|
function referencesTargetVar(call) {
|
|
@@ -11683,13 +12233,13 @@ function referencesTargetVar(call) {
|
|
|
11683
12233
|
}
|
|
11684
12234
|
function drizzleForeignKeys(file, serviceDir) {
|
|
11685
12235
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11686
|
-
const tree = parseSource3(parserForExt3(
|
|
12236
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11687
12237
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
11688
12238
|
const out = [];
|
|
11689
12239
|
const seen = /* @__PURE__ */ new Set();
|
|
11690
12240
|
for (const table of tables) {
|
|
11691
12241
|
if (!table.object) continue;
|
|
11692
|
-
const
|
|
12242
|
+
const walk10 = (node) => {
|
|
11693
12243
|
if (node.type === "call_expression") {
|
|
11694
12244
|
const targetVar = referencesTargetVar(node);
|
|
11695
12245
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -11702,7 +12252,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11702
12252
|
childTable: table.tableName,
|
|
11703
12253
|
parentTable,
|
|
11704
12254
|
evidence: {
|
|
11705
|
-
file:
|
|
12255
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11706
12256
|
line,
|
|
11707
12257
|
snippet: snippet(file.content, line)
|
|
11708
12258
|
}
|
|
@@ -11710,16 +12260,16 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11710
12260
|
}
|
|
11711
12261
|
}
|
|
11712
12262
|
}
|
|
11713
|
-
for (const c of namedChildren4(node))
|
|
12263
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11714
12264
|
};
|
|
11715
|
-
|
|
12265
|
+
walk10(table.object);
|
|
11716
12266
|
}
|
|
11717
12267
|
return out;
|
|
11718
12268
|
}
|
|
11719
12269
|
|
|
11720
12270
|
// src/extract/calls/prisma.ts
|
|
11721
12271
|
init_cjs_shims();
|
|
11722
|
-
var
|
|
12272
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
11723
12273
|
var import_types38 = require("@neat.is/types");
|
|
11724
12274
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
11725
12275
|
"Int",
|
|
@@ -11775,7 +12325,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11775
12325
|
confidenceKind: "structural",
|
|
11776
12326
|
columns: b.columns,
|
|
11777
12327
|
evidence: {
|
|
11778
|
-
file:
|
|
12328
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11779
12329
|
line: b.startLine,
|
|
11780
12330
|
snippet: snippet(content, b.startLine)
|
|
11781
12331
|
}
|
|
@@ -11836,7 +12386,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11836
12386
|
}
|
|
11837
12387
|
async function prismaColumnEndpoints(serviceDir) {
|
|
11838
12388
|
const schemaPath = await findFirst(serviceDir, [
|
|
11839
|
-
|
|
12389
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11840
12390
|
"schema.prisma"
|
|
11841
12391
|
]);
|
|
11842
12392
|
if (!schemaPath) return [];
|
|
@@ -11911,7 +12461,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11911
12461
|
childTable: current.table,
|
|
11912
12462
|
parentTable,
|
|
11913
12463
|
evidence: {
|
|
11914
|
-
file:
|
|
12464
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11915
12465
|
line: lineNo,
|
|
11916
12466
|
snippet: snippet(content, lineNo)
|
|
11917
12467
|
}
|
|
@@ -11926,7 +12476,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11926
12476
|
}
|
|
11927
12477
|
async function prismaForeignKeys(serviceDir) {
|
|
11928
12478
|
const schemaPath = await findFirst(serviceDir, [
|
|
11929
|
-
|
|
12479
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11930
12480
|
"schema.prisma"
|
|
11931
12481
|
]);
|
|
11932
12482
|
if (!schemaPath) return [];
|
|
@@ -11937,7 +12487,7 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
11937
12487
|
|
|
11938
12488
|
// src/extract/calls/activerecord.ts
|
|
11939
12489
|
init_cjs_shims();
|
|
11940
|
-
var
|
|
12490
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11941
12491
|
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
11942
12492
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
11943
12493
|
var import_types39 = require("@neat.is/types");
|
|
@@ -12145,7 +12695,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
12145
12695
|
confidenceKind: "structural",
|
|
12146
12696
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
12147
12697
|
evidence: {
|
|
12148
|
-
file:
|
|
12698
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12149
12699
|
line: table.line,
|
|
12150
12700
|
snippet: snippet(file.content, table.line)
|
|
12151
12701
|
}
|
|
@@ -12167,7 +12717,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
12167
12717
|
childTable,
|
|
12168
12718
|
parentTable,
|
|
12169
12719
|
evidence: {
|
|
12170
|
-
file:
|
|
12720
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12171
12721
|
line,
|
|
12172
12722
|
snippet: snippet(file.content, line)
|
|
12173
12723
|
}
|
|
@@ -12250,7 +12800,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
12250
12800
|
edgeType: "CALLS",
|
|
12251
12801
|
confidenceKind: "verified-call-site",
|
|
12252
12802
|
evidence: {
|
|
12253
|
-
file:
|
|
12803
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12254
12804
|
line,
|
|
12255
12805
|
snippet: snippet(file.content, line)
|
|
12256
12806
|
}
|
|
@@ -12287,7 +12837,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12287
12837
|
childTable,
|
|
12288
12838
|
parentTable,
|
|
12289
12839
|
evidence: {
|
|
12290
|
-
file:
|
|
12840
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12291
12841
|
line,
|
|
12292
12842
|
snippet: snippet(file.content, line)
|
|
12293
12843
|
}
|
|
@@ -12299,7 +12849,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12299
12849
|
|
|
12300
12850
|
// src/extract/calls/eloquent.ts
|
|
12301
12851
|
init_cjs_shims();
|
|
12302
|
-
var
|
|
12852
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12303
12853
|
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
12304
12854
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
12305
12855
|
var import_types40 = require("@neat.is/types");
|
|
@@ -12556,7 +13106,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
12556
13106
|
confidenceKind: "structural",
|
|
12557
13107
|
...columns.length > 0 ? { columns } : {},
|
|
12558
13108
|
evidence: {
|
|
12559
|
-
file:
|
|
13109
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12560
13110
|
line: bp.line,
|
|
12561
13111
|
snippet: snippet(file.content, bp.line)
|
|
12562
13112
|
}
|
|
@@ -12578,7 +13128,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
12578
13128
|
childTable,
|
|
12579
13129
|
parentTable,
|
|
12580
13130
|
evidence: {
|
|
12581
|
-
file:
|
|
13131
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12582
13132
|
line,
|
|
12583
13133
|
snippet: snippet(file.content, line)
|
|
12584
13134
|
}
|
|
@@ -12698,7 +13248,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
12698
13248
|
edgeType: "CALLS",
|
|
12699
13249
|
confidenceKind: "verified-call-site",
|
|
12700
13250
|
evidence: {
|
|
12701
|
-
file:
|
|
13251
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12702
13252
|
line,
|
|
12703
13253
|
snippet: snippet(file.content, line)
|
|
12704
13254
|
}
|
|
@@ -12734,7 +13284,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12734
13284
|
childTable,
|
|
12735
13285
|
parentTable,
|
|
12736
13286
|
evidence: {
|
|
12737
|
-
file:
|
|
13287
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12738
13288
|
line,
|
|
12739
13289
|
snippet: snippet(file.content, line)
|
|
12740
13290
|
}
|
|
@@ -12746,7 +13296,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12746
13296
|
|
|
12747
13297
|
// src/extract/calls/go.ts
|
|
12748
13298
|
init_cjs_shims();
|
|
12749
|
-
var
|
|
13299
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12750
13300
|
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
12751
13301
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
12752
13302
|
var import_types41 = require("@neat.is/types");
|
|
@@ -12821,7 +13371,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
12821
13371
|
return null;
|
|
12822
13372
|
}
|
|
12823
13373
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
12824
|
-
if (
|
|
13374
|
+
if (import_node_path54.default.extname(file.path) !== ".go") return [];
|
|
12825
13375
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
12826
13376
|
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12827
13377
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -12850,7 +13400,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12850
13400
|
confidenceKind: "verified-call-site",
|
|
12851
13401
|
...columns.length > 0 ? { columns } : {},
|
|
12852
13402
|
evidence: {
|
|
12853
|
-
file: toPosix(
|
|
13403
|
+
file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
|
|
12854
13404
|
line,
|
|
12855
13405
|
snippet: snippet(file.content, line)
|
|
12856
13406
|
}
|
|
@@ -12861,7 +13411,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12861
13411
|
|
|
12862
13412
|
// src/extract/calls/gorm.ts
|
|
12863
13413
|
init_cjs_shims();
|
|
12864
|
-
var
|
|
13414
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12865
13415
|
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
12866
13416
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
12867
13417
|
var import_types42 = require("@neat.is/types");
|
|
@@ -13294,7 +13844,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
13294
13844
|
seen.delete(struct.name);
|
|
13295
13845
|
}
|
|
13296
13846
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
13297
|
-
if (
|
|
13847
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13298
13848
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13299
13849
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13300
13850
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13316,7 +13866,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13316
13866
|
confidenceKind: "structural",
|
|
13317
13867
|
...columns.length > 0 ? { columns } : {},
|
|
13318
13868
|
evidence: {
|
|
13319
|
-
file: toPosix(
|
|
13869
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13320
13870
|
line: struct.line,
|
|
13321
13871
|
snippet: snippet(file.content, struct.line)
|
|
13322
13872
|
}
|
|
@@ -13325,7 +13875,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13325
13875
|
return out;
|
|
13326
13876
|
}
|
|
13327
13877
|
function gormForeignKeys(file, serviceDir) {
|
|
13328
|
-
if (
|
|
13878
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13329
13879
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13330
13880
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13331
13881
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13340,7 +13890,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13340
13890
|
childTable,
|
|
13341
13891
|
parentTable,
|
|
13342
13892
|
evidence: {
|
|
13343
|
-
file: toPosix(
|
|
13893
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13344
13894
|
line,
|
|
13345
13895
|
snippet: snippet(file.content, line)
|
|
13346
13896
|
}
|
|
@@ -13375,15 +13925,131 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13375
13925
|
return out;
|
|
13376
13926
|
}
|
|
13377
13927
|
|
|
13928
|
+
// src/extract/calls/efcore.ts
|
|
13929
|
+
init_cjs_shims();
|
|
13930
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
13931
|
+
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
13932
|
+
var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
13933
|
+
var import_types43 = require("@neat.is/types");
|
|
13934
|
+
var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
|
|
13935
|
+
var PARSE_CHUNK12 = 16384;
|
|
13936
|
+
function makeCsParser() {
|
|
13937
|
+
const p = new import_tree_sitter16.default();
|
|
13938
|
+
p.setLanguage(import_tree_sitter_c_sharp2.default);
|
|
13939
|
+
return p;
|
|
13940
|
+
}
|
|
13941
|
+
function parseSource12(parser, source) {
|
|
13942
|
+
return parser.parse(
|
|
13943
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
|
|
13944
|
+
);
|
|
13945
|
+
}
|
|
13946
|
+
function walk9(node, visit) {
|
|
13947
|
+
visit(node);
|
|
13948
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13949
|
+
const c = node.namedChild(i);
|
|
13950
|
+
if (c) walk9(c, visit);
|
|
13951
|
+
}
|
|
13952
|
+
}
|
|
13953
|
+
function firstChildOfType(node, type) {
|
|
13954
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13955
|
+
const c = node.namedChild(i);
|
|
13956
|
+
if (c?.type === type) return c;
|
|
13957
|
+
}
|
|
13958
|
+
return null;
|
|
13959
|
+
}
|
|
13960
|
+
function csStringLiteral(node) {
|
|
13961
|
+
if (node.type === "string_literal") {
|
|
13962
|
+
let out = "";
|
|
13963
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13964
|
+
const c = node.namedChild(i);
|
|
13965
|
+
if (c?.type === "string_literal_content") out += c.text;
|
|
13966
|
+
}
|
|
13967
|
+
return out;
|
|
13968
|
+
}
|
|
13969
|
+
if (node.type === "verbatim_string_literal") {
|
|
13970
|
+
const t = node.text;
|
|
13971
|
+
return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
|
|
13972
|
+
}
|
|
13973
|
+
return null;
|
|
13974
|
+
}
|
|
13975
|
+
function attributeName(attr) {
|
|
13976
|
+
const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
|
|
13977
|
+
if (!nameNode) return null;
|
|
13978
|
+
const text = nameNode.text;
|
|
13979
|
+
const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
|
|
13980
|
+
return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
|
|
13981
|
+
}
|
|
13982
|
+
function tableFromAttribute(attr) {
|
|
13983
|
+
if (attributeName(attr) !== "Table") return null;
|
|
13984
|
+
const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
|
|
13985
|
+
if (!args) return null;
|
|
13986
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
13987
|
+
const arg = args.namedChild(i);
|
|
13988
|
+
if (arg?.type !== "attribute_argument") continue;
|
|
13989
|
+
const first = arg.namedChild(0);
|
|
13990
|
+
if (!first) continue;
|
|
13991
|
+
const value = csStringLiteral(first);
|
|
13992
|
+
if (value !== null) return value;
|
|
13993
|
+
return null;
|
|
13994
|
+
}
|
|
13995
|
+
return null;
|
|
13996
|
+
}
|
|
13997
|
+
function tableFromToTable(call) {
|
|
13998
|
+
const fn = call.childForFieldName("function");
|
|
13999
|
+
if (fn?.type !== "member_access_expression") return null;
|
|
14000
|
+
const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
|
|
14001
|
+
if (method?.text !== "ToTable") return null;
|
|
14002
|
+
const args = call.childForFieldName("arguments");
|
|
14003
|
+
const firstArg2 = args?.namedChild(0);
|
|
14004
|
+
if (firstArg2?.type !== "argument") return null;
|
|
14005
|
+
const value = firstArg2.namedChild(0);
|
|
14006
|
+
return value ? csStringLiteral(value) : null;
|
|
14007
|
+
}
|
|
14008
|
+
function efcoreEndpointsFromFile(file, serviceDir) {
|
|
14009
|
+
if (import_node_path56.default.extname(file.path) !== ".cs") return [];
|
|
14010
|
+
if (!EFCORE_GATE.test(file.content)) return [];
|
|
14011
|
+
const tree = parseSource12(makeCsParser(), file.content);
|
|
14012
|
+
const out = [];
|
|
14013
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14014
|
+
const push = (name, line) => {
|
|
14015
|
+
if (!name || seen.has(name)) return;
|
|
14016
|
+
seen.add(name);
|
|
14017
|
+
out.push({
|
|
14018
|
+
infraId: (0, import_types43.infraId)("sql-table", name),
|
|
14019
|
+
name,
|
|
14020
|
+
kind: "sql-table",
|
|
14021
|
+
edgeType: "CALLS",
|
|
14022
|
+
confidenceKind: "structural",
|
|
14023
|
+
evidence: {
|
|
14024
|
+
file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
|
|
14025
|
+
line,
|
|
14026
|
+
snippet: snippet(file.content, line)
|
|
14027
|
+
}
|
|
14028
|
+
});
|
|
14029
|
+
};
|
|
14030
|
+
walk9(tree.rootNode, (node) => {
|
|
14031
|
+
if (node.type === "attribute") {
|
|
14032
|
+
const table = tableFromAttribute(node);
|
|
14033
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14034
|
+
return;
|
|
14035
|
+
}
|
|
14036
|
+
if (node.type === "invocation_expression") {
|
|
14037
|
+
const table = tableFromToTable(node);
|
|
14038
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14039
|
+
}
|
|
14040
|
+
});
|
|
14041
|
+
return out;
|
|
14042
|
+
}
|
|
14043
|
+
|
|
13378
14044
|
// src/extract/calls/index.ts
|
|
13379
14045
|
function edgeTypeFromEndpoint(ep) {
|
|
13380
14046
|
switch (ep.edgeType) {
|
|
13381
14047
|
case "PUBLISHES_TO":
|
|
13382
|
-
return
|
|
14048
|
+
return import_types44.EdgeType.PUBLISHES_TO;
|
|
13383
14049
|
case "CONSUMES_FROM":
|
|
13384
|
-
return
|
|
14050
|
+
return import_types44.EdgeType.CONSUMES_FROM;
|
|
13385
14051
|
default:
|
|
13386
|
-
return
|
|
14052
|
+
return import_types44.EdgeType.CALLS;
|
|
13387
14053
|
}
|
|
13388
14054
|
}
|
|
13389
14055
|
function isAwsKind(kind) {
|
|
@@ -13433,6 +14099,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13433
14099
|
} catch (err) {
|
|
13434
14100
|
recordExtractionError("laravel eloquent extraction", file.path, err);
|
|
13435
14101
|
}
|
|
14102
|
+
try {
|
|
14103
|
+
endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
|
|
14104
|
+
} catch (err) {
|
|
14105
|
+
recordExtractionError("efcore data-axis extraction", file.path, err);
|
|
14106
|
+
}
|
|
13436
14107
|
}
|
|
13437
14108
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
13438
14109
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
@@ -13443,7 +14114,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13443
14114
|
if (!graph.hasNode(ep.infraId)) {
|
|
13444
14115
|
const node = {
|
|
13445
14116
|
id: ep.infraId,
|
|
13446
|
-
type:
|
|
14117
|
+
type: import_types44.NodeType.InfraNode,
|
|
13447
14118
|
name: ep.name,
|
|
13448
14119
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
13449
14120
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -13456,21 +14127,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13456
14127
|
}
|
|
13457
14128
|
if (ep.columns && ep.columns.length > 0) {
|
|
13458
14129
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13459
|
-
if (node.type ===
|
|
14130
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13460
14131
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13461
14132
|
...node,
|
|
13462
14133
|
columns: foldColumns(
|
|
13463
14134
|
node.columns,
|
|
13464
14135
|
ep.columns,
|
|
13465
|
-
|
|
13466
|
-
(0,
|
|
14136
|
+
import_types44.Provenance.EXTRACTED,
|
|
14137
|
+
(0, import_types44.confidenceForExtracted)(ep.confidenceKind)
|
|
13467
14138
|
)
|
|
13468
14139
|
});
|
|
13469
14140
|
}
|
|
13470
14141
|
}
|
|
13471
14142
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
13472
14143
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13473
|
-
if (node.type ===
|
|
14144
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13474
14145
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13475
14146
|
...node,
|
|
13476
14147
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -13478,7 +14149,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13478
14149
|
}
|
|
13479
14150
|
}
|
|
13480
14151
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
13481
|
-
const confidence = (0,
|
|
14152
|
+
const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
|
|
13482
14153
|
const relFile = toPosix(ep.evidence.file);
|
|
13483
14154
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
13484
14155
|
graph,
|
|
@@ -13488,7 +14159,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13488
14159
|
);
|
|
13489
14160
|
nodesAdded += n;
|
|
13490
14161
|
edgesAdded += e;
|
|
13491
|
-
if (!(0,
|
|
14162
|
+
if (!(0, import_types44.passesExtractedFloor)(confidence)) {
|
|
13492
14163
|
noteExtractedDropped({
|
|
13493
14164
|
source: fileNodeId,
|
|
13494
14165
|
target: ep.infraId,
|
|
@@ -13508,7 +14179,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13508
14179
|
source: fileNodeId,
|
|
13509
14180
|
target: ep.infraId,
|
|
13510
14181
|
type: edgeType,
|
|
13511
|
-
provenance:
|
|
14182
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
13512
14183
|
confidence,
|
|
13513
14184
|
evidence: ep.evidence
|
|
13514
14185
|
};
|
|
@@ -13531,7 +14202,7 @@ async function addCallEdges(graph, services) {
|
|
|
13531
14202
|
|
|
13532
14203
|
// src/extract/table-edges.ts
|
|
13533
14204
|
init_cjs_shims();
|
|
13534
|
-
var
|
|
14205
|
+
var import_types45 = require("@neat.is/types");
|
|
13535
14206
|
async function addTableEdges(graph, services) {
|
|
13536
14207
|
let nodesAdded = 0;
|
|
13537
14208
|
let edgesAdded = 0;
|
|
@@ -13559,20 +14230,20 @@ async function addTableEdges(graph, services) {
|
|
|
13559
14230
|
}
|
|
13560
14231
|
refs.push(...modelRefs);
|
|
13561
14232
|
for (const ref of refs) {
|
|
13562
|
-
const childId = (0,
|
|
13563
|
-
const parentId = (0,
|
|
14233
|
+
const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
|
|
14234
|
+
const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
|
|
13564
14235
|
if (childId === parentId) continue;
|
|
13565
14236
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
13566
14237
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
13567
|
-
const edgeId = (0,
|
|
14238
|
+
const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
|
|
13568
14239
|
if (graph.hasEdge(edgeId)) continue;
|
|
13569
14240
|
const edge = {
|
|
13570
14241
|
id: edgeId,
|
|
13571
14242
|
source: childId,
|
|
13572
14243
|
target: parentId,
|
|
13573
|
-
type:
|
|
13574
|
-
provenance:
|
|
13575
|
-
confidence: (0,
|
|
14244
|
+
type: import_types45.EdgeType.REFERENCES,
|
|
14245
|
+
provenance: import_types45.Provenance.EXTRACTED,
|
|
14246
|
+
confidence: (0, import_types45.confidenceForExtracted)("structural"),
|
|
13576
14247
|
evidence: ref.evidence
|
|
13577
14248
|
};
|
|
13578
14249
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -13585,7 +14256,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
13585
14256
|
if (graph.hasNode(id)) return 0;
|
|
13586
14257
|
const node = {
|
|
13587
14258
|
id,
|
|
13588
|
-
type:
|
|
14259
|
+
type: import_types45.NodeType.InfraNode,
|
|
13589
14260
|
name,
|
|
13590
14261
|
provider: "self",
|
|
13591
14262
|
kind: "sql-table"
|
|
@@ -13599,16 +14270,16 @@ init_cjs_shims();
|
|
|
13599
14270
|
|
|
13600
14271
|
// src/extract/infra/docker-compose.ts
|
|
13601
14272
|
init_cjs_shims();
|
|
13602
|
-
var
|
|
13603
|
-
var
|
|
14273
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
14274
|
+
var import_types47 = require("@neat.is/types");
|
|
13604
14275
|
|
|
13605
14276
|
// src/extract/infra/shared.ts
|
|
13606
14277
|
init_cjs_shims();
|
|
13607
|
-
var
|
|
14278
|
+
var import_types46 = require("@neat.is/types");
|
|
13608
14279
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
13609
14280
|
return {
|
|
13610
|
-
id: (0,
|
|
13611
|
-
type:
|
|
14281
|
+
id: (0, import_types46.infraId)(kind, name),
|
|
14282
|
+
type: import_types46.NodeType.InfraNode,
|
|
13612
14283
|
name,
|
|
13613
14284
|
provider,
|
|
13614
14285
|
kind,
|
|
@@ -13652,8 +14323,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
13652
14323
|
source: anchorId,
|
|
13653
14324
|
target: node.id,
|
|
13654
14325
|
type: edgeType,
|
|
13655
|
-
provenance:
|
|
13656
|
-
confidence: (0,
|
|
14326
|
+
provenance: import_types46.Provenance.EXTRACTED,
|
|
14327
|
+
confidence: (0, import_types46.confidenceForExtracted)("structural"),
|
|
13657
14328
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
13658
14329
|
};
|
|
13659
14330
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13670,7 +14341,7 @@ function dependsOnList(value) {
|
|
|
13670
14341
|
}
|
|
13671
14342
|
function serviceNameToServiceNode(name, services) {
|
|
13672
14343
|
for (const s of services) {
|
|
13673
|
-
if (s.node.name === name ||
|
|
14344
|
+
if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
|
|
13674
14345
|
}
|
|
13675
14346
|
return null;
|
|
13676
14347
|
}
|
|
@@ -13679,7 +14350,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13679
14350
|
let edgesAdded = 0;
|
|
13680
14351
|
let composePath = null;
|
|
13681
14352
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
13682
|
-
const abs =
|
|
14353
|
+
const abs = import_node_path57.default.join(scanPath, name);
|
|
13683
14354
|
if (await exists(abs)) {
|
|
13684
14355
|
composePath = abs;
|
|
13685
14356
|
break;
|
|
@@ -13692,13 +14363,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13692
14363
|
} catch (err) {
|
|
13693
14364
|
recordExtractionError(
|
|
13694
14365
|
"infra docker-compose",
|
|
13695
|
-
|
|
14366
|
+
import_node_path57.default.relative(scanPath, composePath),
|
|
13696
14367
|
err
|
|
13697
14368
|
);
|
|
13698
14369
|
return { nodesAdded, edgesAdded };
|
|
13699
14370
|
}
|
|
13700
14371
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
13701
|
-
const evidenceFile =
|
|
14372
|
+
const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
|
|
13702
14373
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
13703
14374
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
13704
14375
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -13720,15 +14391,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13720
14391
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
13721
14392
|
const targetId = composeNameToNodeId.get(dep);
|
|
13722
14393
|
if (!targetId) continue;
|
|
13723
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
14394
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
|
|
13724
14395
|
if (graph.hasEdge(edgeId)) continue;
|
|
13725
14396
|
const edge = {
|
|
13726
14397
|
id: edgeId,
|
|
13727
14398
|
source: sourceId,
|
|
13728
14399
|
target: targetId,
|
|
13729
|
-
type:
|
|
13730
|
-
provenance:
|
|
13731
|
-
confidence: (0,
|
|
14400
|
+
type: import_types47.EdgeType.DEPENDS_ON,
|
|
14401
|
+
provenance: import_types47.Provenance.EXTRACTED,
|
|
14402
|
+
confidence: (0, import_types47.confidenceForExtracted)("structural"),
|
|
13732
14403
|
evidence: { file: evidenceFile }
|
|
13733
14404
|
};
|
|
13734
14405
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13740,9 +14411,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13740
14411
|
|
|
13741
14412
|
// src/extract/infra/dockerfile.ts
|
|
13742
14413
|
init_cjs_shims();
|
|
13743
|
-
var
|
|
13744
|
-
var
|
|
13745
|
-
var
|
|
14414
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14415
|
+
var import_node_fs25 = require("fs");
|
|
14416
|
+
var import_types48 = require("@neat.is/types");
|
|
13746
14417
|
function readDockerfile(content) {
|
|
13747
14418
|
let image = null;
|
|
13748
14419
|
const ports = [];
|
|
@@ -13771,15 +14442,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13771
14442
|
let nodesAdded = 0;
|
|
13772
14443
|
let edgesAdded = 0;
|
|
13773
14444
|
for (const service of services) {
|
|
13774
|
-
const dockerfilePath =
|
|
14445
|
+
const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
|
|
13775
14446
|
if (!await exists(dockerfilePath)) continue;
|
|
13776
14447
|
let content;
|
|
13777
14448
|
try {
|
|
13778
|
-
content = await
|
|
14449
|
+
content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
|
|
13779
14450
|
} catch (err) {
|
|
13780
14451
|
recordExtractionError(
|
|
13781
14452
|
"infra dockerfile",
|
|
13782
|
-
|
|
14453
|
+
import_node_path58.default.relative(scanPath, dockerfilePath),
|
|
13783
14454
|
err
|
|
13784
14455
|
);
|
|
13785
14456
|
continue;
|
|
@@ -13791,8 +14462,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13791
14462
|
graph.addNode(node.id, node);
|
|
13792
14463
|
nodesAdded++;
|
|
13793
14464
|
}
|
|
13794
|
-
const relDockerfile = toPosix(
|
|
13795
|
-
const evidenceFile = toPosix(
|
|
14465
|
+
const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
|
|
14466
|
+
const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
|
|
13796
14467
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
13797
14468
|
graph,
|
|
13798
14469
|
service.pkg.name,
|
|
@@ -13801,15 +14472,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13801
14472
|
);
|
|
13802
14473
|
nodesAdded += fn;
|
|
13803
14474
|
edgesAdded += fe;
|
|
13804
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
14475
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
|
|
13805
14476
|
if (!graph.hasEdge(edgeId)) {
|
|
13806
14477
|
const edge = {
|
|
13807
14478
|
id: edgeId,
|
|
13808
14479
|
source: fileNodeId,
|
|
13809
14480
|
target: node.id,
|
|
13810
|
-
type:
|
|
13811
|
-
provenance:
|
|
13812
|
-
confidence: (0,
|
|
14481
|
+
type: import_types48.EdgeType.RUNS_ON,
|
|
14482
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14483
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13813
14484
|
evidence: {
|
|
13814
14485
|
file: evidenceFile,
|
|
13815
14486
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -13824,15 +14495,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13824
14495
|
graph.addNode(portNode.id, portNode);
|
|
13825
14496
|
nodesAdded++;
|
|
13826
14497
|
}
|
|
13827
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
14498
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
|
|
13828
14499
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
13829
14500
|
const portEdge = {
|
|
13830
14501
|
id: portEdgeId,
|
|
13831
14502
|
source: fileNodeId,
|
|
13832
14503
|
target: portNode.id,
|
|
13833
|
-
type:
|
|
13834
|
-
provenance:
|
|
13835
|
-
confidence: (0,
|
|
14504
|
+
type: import_types48.EdgeType.CONNECTS_TO,
|
|
14505
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14506
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13836
14507
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
13837
14508
|
};
|
|
13838
14509
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -13844,23 +14515,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13844
14515
|
|
|
13845
14516
|
// src/extract/infra/terraform.ts
|
|
13846
14517
|
init_cjs_shims();
|
|
13847
|
-
var
|
|
13848
|
-
var
|
|
13849
|
-
var
|
|
14518
|
+
var import_node_fs26 = require("fs");
|
|
14519
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14520
|
+
var import_types49 = require("@neat.is/types");
|
|
13850
14521
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
13851
14522
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
13852
14523
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
13853
14524
|
if (depth > max) return [];
|
|
13854
14525
|
const out = [];
|
|
13855
|
-
const entries = await
|
|
14526
|
+
const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13856
14527
|
for (const entry2 of entries) {
|
|
13857
14528
|
if (entry2.isDirectory()) {
|
|
13858
14529
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
13859
|
-
const child =
|
|
14530
|
+
const child = import_node_path59.default.join(start, entry2.name);
|
|
13860
14531
|
if (await isPythonVenvDir(child)) continue;
|
|
13861
14532
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
13862
14533
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
13863
|
-
out.push(
|
|
14534
|
+
out.push(import_node_path59.default.join(start, entry2.name));
|
|
13864
14535
|
}
|
|
13865
14536
|
}
|
|
13866
14537
|
return out;
|
|
@@ -13891,8 +14562,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13891
14562
|
let edgesAdded = 0;
|
|
13892
14563
|
const files = await walkTfFiles(scanPath);
|
|
13893
14564
|
for (const file of files) {
|
|
13894
|
-
const content = await
|
|
13895
|
-
const evidenceFile = toPosix(
|
|
14565
|
+
const content = await import_node_fs26.promises.readFile(file, "utf8");
|
|
14566
|
+
const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
|
|
13896
14567
|
const resources = [];
|
|
13897
14568
|
const byKey = /* @__PURE__ */ new Map();
|
|
13898
14569
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -13927,16 +14598,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13927
14598
|
if (!target) continue;
|
|
13928
14599
|
if (seen.has(target.nodeId)) continue;
|
|
13929
14600
|
seen.add(target.nodeId);
|
|
13930
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
14601
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
|
|
13931
14602
|
if (graph.hasEdge(edgeId)) continue;
|
|
13932
14603
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
13933
14604
|
const edge = {
|
|
13934
14605
|
id: edgeId,
|
|
13935
14606
|
source: resource.nodeId,
|
|
13936
14607
|
target: target.nodeId,
|
|
13937
|
-
type:
|
|
13938
|
-
provenance:
|
|
13939
|
-
confidence: (0,
|
|
14608
|
+
type: import_types49.EdgeType.DEPENDS_ON,
|
|
14609
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
14610
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
13940
14611
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
13941
14612
|
};
|
|
13942
14613
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13949,8 +14620,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13949
14620
|
|
|
13950
14621
|
// src/extract/infra/k8s.ts
|
|
13951
14622
|
init_cjs_shims();
|
|
13952
|
-
var
|
|
13953
|
-
var
|
|
14623
|
+
var import_node_fs27 = require("fs");
|
|
14624
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
13954
14625
|
var import_yaml3 = require("yaml");
|
|
13955
14626
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
13956
14627
|
Service: "k8s-service",
|
|
@@ -13964,15 +14635,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
13964
14635
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
13965
14636
|
if (depth > max) return [];
|
|
13966
14637
|
const out = [];
|
|
13967
|
-
const entries = await
|
|
14638
|
+
const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13968
14639
|
for (const entry2 of entries) {
|
|
13969
14640
|
if (entry2.isDirectory()) {
|
|
13970
14641
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
13971
|
-
const child =
|
|
14642
|
+
const child = import_node_path60.default.join(start, entry2.name);
|
|
13972
14643
|
if (await isPythonVenvDir(child)) continue;
|
|
13973
14644
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
13974
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
13975
|
-
out.push(
|
|
14645
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry2.name))) {
|
|
14646
|
+
out.push(import_node_path60.default.join(start, entry2.name));
|
|
13976
14647
|
}
|
|
13977
14648
|
}
|
|
13978
14649
|
return out;
|
|
@@ -13981,7 +14652,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
13981
14652
|
let nodesAdded = 0;
|
|
13982
14653
|
const files = await walkYamlFiles2(scanPath);
|
|
13983
14654
|
for (const file of files) {
|
|
13984
|
-
const content = await
|
|
14655
|
+
const content = await import_node_fs27.promises.readFile(file, "utf8");
|
|
13985
14656
|
let docs;
|
|
13986
14657
|
try {
|
|
13987
14658
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -14005,16 +14676,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
14005
14676
|
|
|
14006
14677
|
// src/extract/infra/cloudflare.ts
|
|
14007
14678
|
init_cjs_shims();
|
|
14008
|
-
var
|
|
14009
|
-
var
|
|
14679
|
+
var import_node_fs28 = require("fs");
|
|
14680
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
14010
14681
|
var import_smol_toml3 = require("smol-toml");
|
|
14011
|
-
var
|
|
14682
|
+
var import_types50 = require("@neat.is/types");
|
|
14012
14683
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
14013
14684
|
async function readWranglerConfig(dir) {
|
|
14014
14685
|
for (const filename of WRANGLER_FILENAMES) {
|
|
14015
|
-
const abs =
|
|
14686
|
+
const abs = import_node_path61.default.join(dir, filename);
|
|
14016
14687
|
if (!await exists(abs)) continue;
|
|
14017
|
-
const raw = await
|
|
14688
|
+
const raw = await import_node_fs28.promises.readFile(abs, "utf8");
|
|
14018
14689
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14019
14690
|
return { config, relFile: filename, raw };
|
|
14020
14691
|
}
|
|
@@ -14056,8 +14727,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
14056
14727
|
source: anchorId,
|
|
14057
14728
|
target: node.id,
|
|
14058
14729
|
type: edgeType,
|
|
14059
|
-
provenance:
|
|
14060
|
-
confidence: (0,
|
|
14730
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14731
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14061
14732
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
14062
14733
|
};
|
|
14063
14734
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14075,11 +14746,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14075
14746
|
try {
|
|
14076
14747
|
read = await readWranglerConfig(service.dir);
|
|
14077
14748
|
} catch (err) {
|
|
14078
|
-
recordExtractionError("infra cloudflare",
|
|
14749
|
+
recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
|
|
14079
14750
|
continue;
|
|
14080
14751
|
}
|
|
14081
14752
|
if (!read || !read.config.name) continue;
|
|
14082
|
-
const evidenceFile = toPosix(
|
|
14753
|
+
const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
|
|
14083
14754
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
14084
14755
|
}
|
|
14085
14756
|
for (const worker of discovered) {
|
|
@@ -14091,7 +14762,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14091
14762
|
}
|
|
14092
14763
|
let anchorId = service.node.id;
|
|
14093
14764
|
if (config.main) {
|
|
14094
|
-
const entryRelPath = toPosix(
|
|
14765
|
+
const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
|
|
14095
14766
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
14096
14767
|
graph,
|
|
14097
14768
|
service.pkg.name,
|
|
@@ -14118,15 +14789,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14118
14789
|
nodesAdded++;
|
|
14119
14790
|
}
|
|
14120
14791
|
if (runtimeNode.id !== anchorId) {
|
|
14121
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
14792
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
|
|
14122
14793
|
if (!graph.hasEdge(runsOnId)) {
|
|
14123
14794
|
const edge = {
|
|
14124
14795
|
id: runsOnId,
|
|
14125
14796
|
source: anchorId,
|
|
14126
14797
|
target: runtimeNode.id,
|
|
14127
|
-
type:
|
|
14128
|
-
provenance:
|
|
14129
|
-
confidence: (0,
|
|
14798
|
+
type: import_types50.EdgeType.RUNS_ON,
|
|
14799
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14800
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14130
14801
|
evidence: {
|
|
14131
14802
|
file: evidenceFile,
|
|
14132
14803
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -14140,7 +14811,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14140
14811
|
const result = addResourceEdge(
|
|
14141
14812
|
graph,
|
|
14142
14813
|
anchorId,
|
|
14143
|
-
|
|
14814
|
+
import_types50.EdgeType.CONNECTS_TO,
|
|
14144
14815
|
"cloudflare-route",
|
|
14145
14816
|
route,
|
|
14146
14817
|
evidenceFile,
|
|
@@ -14164,7 +14835,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14164
14835
|
const result = addResourceEdge(
|
|
14165
14836
|
graph,
|
|
14166
14837
|
anchorId,
|
|
14167
|
-
|
|
14838
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14168
14839
|
group.kind,
|
|
14169
14840
|
name,
|
|
14170
14841
|
evidenceFile,
|
|
@@ -14178,7 +14849,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14178
14849
|
const result = addResourceEdge(
|
|
14179
14850
|
graph,
|
|
14180
14851
|
anchorId,
|
|
14181
|
-
|
|
14852
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14182
14853
|
"cloudflare-cron",
|
|
14183
14854
|
cron,
|
|
14184
14855
|
evidenceFile,
|
|
@@ -14191,7 +14862,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14191
14862
|
const result = addResourceEdge(
|
|
14192
14863
|
graph,
|
|
14193
14864
|
anchorId,
|
|
14194
|
-
|
|
14865
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14195
14866
|
"cloudflare-env-var",
|
|
14196
14867
|
varName,
|
|
14197
14868
|
evidenceFile,
|
|
@@ -14204,15 +14875,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14204
14875
|
if (!svc.service) continue;
|
|
14205
14876
|
const target = workerIndex.get(svc.service);
|
|
14206
14877
|
if (target && target.anchorId !== anchorId) {
|
|
14207
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
14878
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
|
|
14208
14879
|
if (!graph.hasEdge(edgeId)) {
|
|
14209
14880
|
const edge = {
|
|
14210
14881
|
id: edgeId,
|
|
14211
14882
|
source: anchorId,
|
|
14212
14883
|
target: target.anchorId,
|
|
14213
|
-
type:
|
|
14214
|
-
provenance:
|
|
14215
|
-
confidence: (0,
|
|
14884
|
+
type: import_types50.EdgeType.CALLS,
|
|
14885
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14886
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14216
14887
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
14217
14888
|
};
|
|
14218
14889
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14223,7 +14894,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14223
14894
|
const result = addResourceEdge(
|
|
14224
14895
|
graph,
|
|
14225
14896
|
anchorId,
|
|
14226
|
-
|
|
14897
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14227
14898
|
"cloudflare-service-binding",
|
|
14228
14899
|
svc.service,
|
|
14229
14900
|
evidenceFile,
|
|
@@ -14238,24 +14909,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14238
14909
|
|
|
14239
14910
|
// src/extract/infra/vercel.ts
|
|
14240
14911
|
init_cjs_shims();
|
|
14241
|
-
var
|
|
14242
|
-
var
|
|
14243
|
-
var
|
|
14912
|
+
var import_node_fs29 = require("fs");
|
|
14913
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
14914
|
+
var import_types51 = require("@neat.is/types");
|
|
14244
14915
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
14245
14916
|
async function readVercelConfig(dir) {
|
|
14246
14917
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
14247
|
-
const abs =
|
|
14918
|
+
const abs = import_node_path62.default.join(dir, filename);
|
|
14248
14919
|
if (!await exists(abs)) continue;
|
|
14249
|
-
const raw = await
|
|
14920
|
+
const raw = await import_node_fs29.promises.readFile(abs, "utf8");
|
|
14250
14921
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
14251
14922
|
return { config, relFile: filename, raw };
|
|
14252
14923
|
}
|
|
14253
14924
|
return null;
|
|
14254
14925
|
}
|
|
14255
14926
|
async function readLinkedProjectName(dir) {
|
|
14256
|
-
const abs =
|
|
14927
|
+
const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
|
|
14257
14928
|
if (!await exists(abs)) return void 0;
|
|
14258
|
-
const parsed = JSON.parse(await
|
|
14929
|
+
const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
|
|
14259
14930
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
14260
14931
|
}
|
|
14261
14932
|
function routeSource(route) {
|
|
@@ -14271,7 +14942,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14271
14942
|
read = await readVercelConfig(service.dir);
|
|
14272
14943
|
projectName = await readLinkedProjectName(service.dir);
|
|
14273
14944
|
} catch (err) {
|
|
14274
|
-
recordExtractionError("infra vercel",
|
|
14945
|
+
recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
|
|
14275
14946
|
continue;
|
|
14276
14947
|
}
|
|
14277
14948
|
if (!read && !projectName) continue;
|
|
@@ -14287,7 +14958,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14287
14958
|
const anchorId = service.node.id;
|
|
14288
14959
|
if (!read) continue;
|
|
14289
14960
|
const { config, relFile, raw } = read;
|
|
14290
|
-
const evidenceFile = toPosix(
|
|
14961
|
+
const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
|
|
14291
14962
|
const add = (edgeType, kind, name) => {
|
|
14292
14963
|
if (!name) return;
|
|
14293
14964
|
const result = emitPlatformResourceEdge(
|
|
@@ -14303,12 +14974,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14303
14974
|
nodesAdded += result.nodesAdded;
|
|
14304
14975
|
edgesAdded += result.edgesAdded;
|
|
14305
14976
|
};
|
|
14306
|
-
add(
|
|
14307
|
-
for (const cron of config.crons ?? []) add(
|
|
14308
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
14309
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
14977
|
+
add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
14978
|
+
for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
14979
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
14980
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
14310
14981
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
14311
|
-
add(
|
|
14982
|
+
add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
14312
14983
|
}
|
|
14313
14984
|
}
|
|
14314
14985
|
return { nodesAdded, edgesAdded };
|
|
@@ -14316,16 +14987,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14316
14987
|
|
|
14317
14988
|
// src/extract/infra/railway.ts
|
|
14318
14989
|
init_cjs_shims();
|
|
14319
|
-
var
|
|
14320
|
-
var
|
|
14990
|
+
var import_node_fs30 = require("fs");
|
|
14991
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
14321
14992
|
var import_smol_toml4 = require("smol-toml");
|
|
14322
|
-
var
|
|
14993
|
+
var import_types52 = require("@neat.is/types");
|
|
14323
14994
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
14324
14995
|
async function readRailwayConfig(dir) {
|
|
14325
14996
|
for (const filename of RAILWAY_FILENAMES) {
|
|
14326
|
-
const abs =
|
|
14997
|
+
const abs = import_node_path63.default.join(dir, filename);
|
|
14327
14998
|
if (!await exists(abs)) continue;
|
|
14328
|
-
const raw = await
|
|
14999
|
+
const raw = await import_node_fs30.promises.readFile(abs, "utf8");
|
|
14329
15000
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14330
15001
|
return { config, relFile: filename, raw };
|
|
14331
15002
|
}
|
|
@@ -14339,7 +15010,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14339
15010
|
try {
|
|
14340
15011
|
read = await readRailwayConfig(service.dir);
|
|
14341
15012
|
} catch (err) {
|
|
14342
|
-
recordExtractionError("infra railway",
|
|
15013
|
+
recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
|
|
14343
15014
|
continue;
|
|
14344
15015
|
}
|
|
14345
15016
|
if (!read) continue;
|
|
@@ -14349,7 +15020,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14349
15020
|
}
|
|
14350
15021
|
const anchorId = service.node.id;
|
|
14351
15022
|
const { config, relFile, raw } = read;
|
|
14352
|
-
const evidenceFile = toPosix(
|
|
15023
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
|
|
14353
15024
|
const add = (edgeType, kind, name) => {
|
|
14354
15025
|
if (!name) return;
|
|
14355
15026
|
const result = emitPlatformResourceEdge(
|
|
@@ -14365,24 +15036,24 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14365
15036
|
nodesAdded += result.nodesAdded;
|
|
14366
15037
|
edgesAdded += result.edgesAdded;
|
|
14367
15038
|
};
|
|
14368
|
-
add(
|
|
14369
|
-
add(
|
|
14370
|
-
add(
|
|
15039
|
+
add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
|
|
15040
|
+
add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
15041
|
+
add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
14371
15042
|
}
|
|
14372
15043
|
return { nodesAdded, edgesAdded };
|
|
14373
15044
|
}
|
|
14374
15045
|
|
|
14375
15046
|
// src/extract/infra/supabase.ts
|
|
14376
15047
|
init_cjs_shims();
|
|
14377
|
-
var
|
|
14378
|
-
var
|
|
15048
|
+
var import_node_fs31 = require("fs");
|
|
15049
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
14379
15050
|
var import_smol_toml5 = require("smol-toml");
|
|
14380
|
-
var
|
|
15051
|
+
var import_types53 = require("@neat.is/types");
|
|
14381
15052
|
async function readSupabaseConfig(dir) {
|
|
14382
|
-
const relFile =
|
|
14383
|
-
const abs =
|
|
15053
|
+
const relFile = import_node_path64.default.join("supabase", "config.toml");
|
|
15054
|
+
const abs = import_node_path64.default.join(dir, relFile);
|
|
14384
15055
|
if (!await exists(abs)) return null;
|
|
14385
|
-
const raw = await
|
|
15056
|
+
const raw = await import_node_fs31.promises.readFile(abs, "utf8");
|
|
14386
15057
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
14387
15058
|
return { config, relFile, raw };
|
|
14388
15059
|
}
|
|
@@ -14394,7 +15065,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14394
15065
|
try {
|
|
14395
15066
|
read = await readSupabaseConfig(service.dir);
|
|
14396
15067
|
} catch (err) {
|
|
14397
|
-
recordExtractionError("infra supabase",
|
|
15068
|
+
recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
|
|
14398
15069
|
continue;
|
|
14399
15070
|
}
|
|
14400
15071
|
if (!read) continue;
|
|
@@ -14409,7 +15080,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14409
15080
|
});
|
|
14410
15081
|
}
|
|
14411
15082
|
const anchorId = service.node.id;
|
|
14412
|
-
const evidenceFile = toPosix(
|
|
15083
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
|
|
14413
15084
|
const add = (edgeType, kind, name) => {
|
|
14414
15085
|
if (!name) return;
|
|
14415
15086
|
const result = emitPlatformResourceEdge(
|
|
@@ -14425,10 +15096,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14425
15096
|
nodesAdded += result.nodesAdded;
|
|
14426
15097
|
edgesAdded += result.edgesAdded;
|
|
14427
15098
|
};
|
|
14428
|
-
add(
|
|
14429
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
14430
|
-
if (config.storage) add(
|
|
14431
|
-
if (config.auth) add(
|
|
15099
|
+
add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
15100
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
15101
|
+
if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
15102
|
+
if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
14432
15103
|
}
|
|
14433
15104
|
return { nodesAdded, edgesAdded };
|
|
14434
15105
|
}
|
|
@@ -14451,14 +15122,14 @@ async function addInfra(graph, scanPath, services) {
|
|
|
14451
15122
|
|
|
14452
15123
|
// src/extract/zod-shapes.ts
|
|
14453
15124
|
init_cjs_shims();
|
|
14454
|
-
var
|
|
14455
|
-
var
|
|
15125
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15126
|
+
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
14456
15127
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
14457
|
-
var
|
|
15128
|
+
var import_types54 = require("@neat.is/types");
|
|
14458
15129
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
14459
15130
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
14460
15131
|
function parserForExt4(ext) {
|
|
14461
|
-
const p = new
|
|
15132
|
+
const p = new import_tree_sitter17.default();
|
|
14462
15133
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
14463
15134
|
return p;
|
|
14464
15135
|
}
|
|
@@ -14546,7 +15217,7 @@ function topLevelSchemas(root) {
|
|
|
14546
15217
|
}
|
|
14547
15218
|
function zodShapesFromFile(file, serviceDir) {
|
|
14548
15219
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
14549
|
-
const tree = parseSource3(parserForExt4(
|
|
15220
|
+
const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
|
|
14550
15221
|
const out = [];
|
|
14551
15222
|
const seen = /* @__PURE__ */ new Set();
|
|
14552
15223
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14560,11 +15231,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
14560
15231
|
seen.add(name);
|
|
14561
15232
|
const line = call.startPosition.row + 1;
|
|
14562
15233
|
out.push({
|
|
14563
|
-
infraId: (0,
|
|
15234
|
+
infraId: (0, import_types54.infraId)("zod-schema", name),
|
|
14564
15235
|
name,
|
|
14565
15236
|
fields,
|
|
14566
15237
|
evidence: {
|
|
14567
|
-
file:
|
|
15238
|
+
file: import_node_path65.default.relative(serviceDir, file.path),
|
|
14568
15239
|
line,
|
|
14569
15240
|
snippet: snippet(file.content, line)
|
|
14570
15241
|
}
|
|
@@ -14595,7 +15266,7 @@ async function addZodShapes(graph, services) {
|
|
|
14595
15266
|
if (!graph.hasNode(shape.infraId)) {
|
|
14596
15267
|
const node = {
|
|
14597
15268
|
id: shape.infraId,
|
|
14598
|
-
type:
|
|
15269
|
+
type: import_types54.NodeType.InfraNode,
|
|
14599
15270
|
name: shape.name,
|
|
14600
15271
|
provider: "self",
|
|
14601
15272
|
kind: "zod-schema"
|
|
@@ -14605,14 +15276,14 @@ async function addZodShapes(graph, services) {
|
|
|
14605
15276
|
}
|
|
14606
15277
|
if (shape.fields.length > 0) {
|
|
14607
15278
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
14608
|
-
if (node.type ===
|
|
15279
|
+
if (node.type === import_types54.NodeType.InfraNode) {
|
|
14609
15280
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
14610
15281
|
...node,
|
|
14611
15282
|
columns: foldColumns(
|
|
14612
15283
|
node.columns,
|
|
14613
15284
|
shape.fields,
|
|
14614
|
-
|
|
14615
|
-
(0,
|
|
15285
|
+
import_types54.Provenance.EXTRACTED,
|
|
15286
|
+
(0, import_types54.confidenceForExtracted)("structural")
|
|
14616
15287
|
)
|
|
14617
15288
|
});
|
|
14618
15289
|
}
|
|
@@ -14626,15 +15297,15 @@ async function addZodShapes(graph, services) {
|
|
|
14626
15297
|
);
|
|
14627
15298
|
nodesAdded += n;
|
|
14628
15299
|
edgesAdded += e;
|
|
14629
|
-
const edgeId = (0,
|
|
15300
|
+
const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
|
|
14630
15301
|
if (!graph.hasEdge(edgeId)) {
|
|
14631
15302
|
const edge = {
|
|
14632
15303
|
id: edgeId,
|
|
14633
15304
|
source: fileNodeId,
|
|
14634
15305
|
target: shape.infraId,
|
|
14635
|
-
type:
|
|
14636
|
-
provenance:
|
|
14637
|
-
confidence: (0,
|
|
15306
|
+
type: import_types54.EdgeType.CONTAINS,
|
|
15307
|
+
provenance: import_types54.Provenance.EXTRACTED,
|
|
15308
|
+
confidence: (0, import_types54.confidenceForExtracted)("structural"),
|
|
14638
15309
|
evidence: shape.evidence
|
|
14639
15310
|
};
|
|
14640
15311
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -14648,7 +15319,7 @@ async function addZodShapes(graph, services) {
|
|
|
14648
15319
|
|
|
14649
15320
|
// src/extract/firestore-rules.ts
|
|
14650
15321
|
init_cjs_shims();
|
|
14651
|
-
var
|
|
15322
|
+
var import_types55 = require("@neat.is/types");
|
|
14652
15323
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
14653
15324
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
14654
15325
|
function stripComments(src) {
|
|
@@ -14788,7 +15459,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
14788
15459
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
14789
15460
|
graph.forEachNode((id, attrs) => {
|
|
14790
15461
|
const node = attrs;
|
|
14791
|
-
if (node.type !==
|
|
15462
|
+
if (node.type !== import_types55.NodeType.InfraNode) return;
|
|
14792
15463
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
14793
15464
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
14794
15465
|
if (!fields || fields.size === 0) return;
|
|
@@ -14801,17 +15472,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
14801
15472
|
}
|
|
14802
15473
|
|
|
14803
15474
|
// src/extract/index.ts
|
|
14804
|
-
var
|
|
15475
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
14805
15476
|
|
|
14806
15477
|
// src/extract/retire.ts
|
|
14807
15478
|
init_cjs_shims();
|
|
14808
|
-
var
|
|
14809
|
-
var
|
|
14810
|
-
var
|
|
15479
|
+
var import_node_fs32 = require("fs");
|
|
15480
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
15481
|
+
var import_types56 = require("@neat.is/types");
|
|
14811
15482
|
function dropOrphanedFileNodes(graph) {
|
|
14812
15483
|
const orphans = [];
|
|
14813
15484
|
graph.forEachNode((id, attrs) => {
|
|
14814
|
-
if (attrs.type !==
|
|
15485
|
+
if (attrs.type !== import_types56.NodeType.FileNode) return;
|
|
14815
15486
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
14816
15487
|
orphans.push(id);
|
|
14817
15488
|
}
|
|
@@ -14824,14 +15495,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
14824
15495
|
const bases = [scanPath, ...serviceDirs];
|
|
14825
15496
|
graph.forEachEdge((id, attrs) => {
|
|
14826
15497
|
const edge = attrs;
|
|
14827
|
-
if (edge.provenance !==
|
|
15498
|
+
if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
|
|
14828
15499
|
const evidenceFile = edge.evidence?.file;
|
|
14829
15500
|
if (!evidenceFile) return;
|
|
14830
|
-
if (
|
|
14831
|
-
if (!(0,
|
|
15501
|
+
if (import_node_path66.default.isAbsolute(evidenceFile)) {
|
|
15502
|
+
if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
|
|
14832
15503
|
return;
|
|
14833
15504
|
}
|
|
14834
|
-
const found = bases.some((base) => (0,
|
|
15505
|
+
const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
|
|
14835
15506
|
if (!found) toDrop.push(id);
|
|
14836
15507
|
});
|
|
14837
15508
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -14888,7 +15559,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14888
15559
|
}
|
|
14889
15560
|
const droppedEntries = drainDroppedExtracted();
|
|
14890
15561
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
14891
|
-
const rejectedPath =
|
|
15562
|
+
const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
14892
15563
|
try {
|
|
14893
15564
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
14894
15565
|
} catch (err) {
|
|
@@ -14922,9 +15593,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14922
15593
|
|
|
14923
15594
|
// src/persist.ts
|
|
14924
15595
|
init_cjs_shims();
|
|
14925
|
-
var
|
|
14926
|
-
var
|
|
14927
|
-
var
|
|
15596
|
+
var import_node_fs33 = require("fs");
|
|
15597
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
15598
|
+
var import_types57 = require("@neat.is/types");
|
|
14928
15599
|
var SCHEMA_VERSION = 7;
|
|
14929
15600
|
function migrateV1ToV2(payload) {
|
|
14930
15601
|
const nodes = payload.graph.nodes;
|
|
@@ -14948,7 +15619,7 @@ function migrateV5ToV6(payload) {
|
|
|
14948
15619
|
if (Array.isArray(nodes)) {
|
|
14949
15620
|
for (const node of nodes) {
|
|
14950
15621
|
const attrs = node.attributes;
|
|
14951
|
-
if (!attrs || attrs.type !==
|
|
15622
|
+
if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
|
|
14952
15623
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
14953
15624
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
14954
15625
|
}
|
|
@@ -14964,12 +15635,12 @@ function migrateV2ToV3(payload) {
|
|
|
14964
15635
|
for (const edge of edges) {
|
|
14965
15636
|
const attrs = edge.attributes;
|
|
14966
15637
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
14967
|
-
attrs.provenance =
|
|
15638
|
+
attrs.provenance = import_types57.Provenance.OBSERVED;
|
|
14968
15639
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
14969
15640
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
14970
15641
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
14971
15642
|
if (type && source && target) {
|
|
14972
|
-
const newId = (0,
|
|
15643
|
+
const newId = (0, import_types57.observedEdgeId)(source, target, type);
|
|
14973
15644
|
attrs.id = newId;
|
|
14974
15645
|
if (edge.key) edge.key = newId;
|
|
14975
15646
|
}
|
|
@@ -14978,7 +15649,7 @@ function migrateV2ToV3(payload) {
|
|
|
14978
15649
|
return { ...payload, schemaVersion: 3 };
|
|
14979
15650
|
}
|
|
14980
15651
|
async function ensureDir(filePath) {
|
|
14981
|
-
await
|
|
15652
|
+
await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
|
|
14982
15653
|
}
|
|
14983
15654
|
async function saveGraphToDisk(graph, outPath) {
|
|
14984
15655
|
await ensureDir(outPath);
|
|
@@ -14988,13 +15659,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
14988
15659
|
graph: graph.export()
|
|
14989
15660
|
};
|
|
14990
15661
|
const tmp = `${outPath}.tmp`;
|
|
14991
|
-
await
|
|
14992
|
-
await
|
|
15662
|
+
await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
15663
|
+
await import_node_fs33.promises.rename(tmp, outPath);
|
|
14993
15664
|
}
|
|
14994
15665
|
async function loadGraphFromDisk(graph, outPath) {
|
|
14995
15666
|
let raw;
|
|
14996
15667
|
try {
|
|
14997
|
-
raw = await
|
|
15668
|
+
raw = await import_node_fs33.promises.readFile(outPath, "utf8");
|
|
14998
15669
|
} catch (err) {
|
|
14999
15670
|
if (err.code === "ENOENT") return;
|
|
15000
15671
|
throw err;
|
|
@@ -15068,23 +15739,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
15068
15739
|
|
|
15069
15740
|
// src/projects.ts
|
|
15070
15741
|
init_cjs_shims();
|
|
15071
|
-
var
|
|
15742
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
15072
15743
|
function pathsForProject(project, baseDir) {
|
|
15073
15744
|
if (project === DEFAULT_PROJECT) {
|
|
15074
15745
|
return {
|
|
15075
|
-
snapshotPath:
|
|
15076
|
-
errorsPath:
|
|
15077
|
-
staleEventsPath:
|
|
15078
|
-
embeddingsCachePath:
|
|
15079
|
-
policyViolationsPath:
|
|
15746
|
+
snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
|
|
15747
|
+
errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
|
|
15748
|
+
staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
|
|
15749
|
+
embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
|
|
15750
|
+
policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
|
|
15080
15751
|
};
|
|
15081
15752
|
}
|
|
15082
15753
|
return {
|
|
15083
|
-
snapshotPath:
|
|
15084
|
-
errorsPath:
|
|
15085
|
-
staleEventsPath:
|
|
15086
|
-
embeddingsCachePath:
|
|
15087
|
-
policyViolationsPath:
|
|
15754
|
+
snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
|
|
15755
|
+
errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
|
|
15756
|
+
staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
15757
|
+
embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
|
|
15758
|
+
policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
15088
15759
|
};
|
|
15089
15760
|
}
|
|
15090
15761
|
var Projects = class {
|
|
@@ -15122,19 +15793,19 @@ var Projects = class {
|
|
|
15122
15793
|
init_cjs_shims();
|
|
15123
15794
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15124
15795
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15125
|
-
var
|
|
15796
|
+
var import_types92 = require("@neat.is/types");
|
|
15126
15797
|
|
|
15127
15798
|
// src/extend/index.ts
|
|
15128
15799
|
init_cjs_shims();
|
|
15129
|
-
var
|
|
15130
|
-
var
|
|
15800
|
+
var import_node_fs35 = require("fs");
|
|
15801
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
15131
15802
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
15132
15803
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
15133
15804
|
|
|
15134
15805
|
// src/installers/package-manager.ts
|
|
15135
15806
|
init_cjs_shims();
|
|
15136
|
-
var
|
|
15137
|
-
var
|
|
15807
|
+
var import_node_fs34 = require("fs");
|
|
15808
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
15138
15809
|
var import_node_child_process = require("child_process");
|
|
15139
15810
|
var LOCKFILE_PRIORITY = [
|
|
15140
15811
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -15149,29 +15820,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
15149
15820
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
15150
15821
|
async function exists2(p) {
|
|
15151
15822
|
try {
|
|
15152
|
-
await
|
|
15823
|
+
await import_node_fs34.promises.access(p);
|
|
15153
15824
|
return true;
|
|
15154
15825
|
} catch {
|
|
15155
15826
|
return false;
|
|
15156
15827
|
}
|
|
15157
15828
|
}
|
|
15158
15829
|
async function detectPackageManager(serviceDir) {
|
|
15159
|
-
let dir =
|
|
15830
|
+
let dir = import_node_path70.default.resolve(serviceDir);
|
|
15160
15831
|
const stops = /* @__PURE__ */ new Set();
|
|
15161
15832
|
for (let i = 0; i < 64; i++) {
|
|
15162
15833
|
if (stops.has(dir)) break;
|
|
15163
15834
|
stops.add(dir);
|
|
15164
15835
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
15165
|
-
const lockPath =
|
|
15836
|
+
const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
|
|
15166
15837
|
if (await exists2(lockPath)) {
|
|
15167
15838
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
15168
15839
|
}
|
|
15169
15840
|
}
|
|
15170
|
-
const parent =
|
|
15841
|
+
const parent = import_node_path70.default.dirname(dir);
|
|
15171
15842
|
if (parent === dir) break;
|
|
15172
15843
|
dir = parent;
|
|
15173
15844
|
}
|
|
15174
|
-
return { pm: "npm", cwd:
|
|
15845
|
+
return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
15175
15846
|
}
|
|
15176
15847
|
async function runPackageManagerInstall(cmd) {
|
|
15177
15848
|
return new Promise((resolve) => {
|
|
@@ -15213,15 +15884,15 @@ ${err.message}`
|
|
|
15213
15884
|
// src/extend/index.ts
|
|
15214
15885
|
async function fileExists2(p) {
|
|
15215
15886
|
try {
|
|
15216
|
-
await
|
|
15887
|
+
await import_node_fs35.promises.access(p);
|
|
15217
15888
|
return true;
|
|
15218
15889
|
} catch {
|
|
15219
15890
|
return false;
|
|
15220
15891
|
}
|
|
15221
15892
|
}
|
|
15222
15893
|
async function readPackageJson(scanPath) {
|
|
15223
|
-
const pkgPath =
|
|
15224
|
-
const raw = await
|
|
15894
|
+
const pkgPath = import_node_path71.default.join(scanPath, "package.json");
|
|
15895
|
+
const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
|
|
15225
15896
|
return JSON.parse(raw);
|
|
15226
15897
|
}
|
|
15227
15898
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -15234,27 +15905,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
15234
15905
|
]);
|
|
15235
15906
|
async function findHookFiles(scanPath) {
|
|
15236
15907
|
const found = [];
|
|
15237
|
-
const
|
|
15238
|
-
const entries = await
|
|
15908
|
+
const walk10 = async (dir) => {
|
|
15909
|
+
const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
15239
15910
|
for (const entry2 of entries) {
|
|
15240
15911
|
if (entry2.isDirectory()) {
|
|
15241
15912
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
15242
|
-
await
|
|
15913
|
+
await walk10(import_node_path71.default.join(dir, entry2.name));
|
|
15243
15914
|
} else if (entry2.isFile()) {
|
|
15244
15915
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
15245
|
-
const rel =
|
|
15246
|
-
found.push(rel.split(
|
|
15916
|
+
const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry2.name));
|
|
15917
|
+
found.push(rel.split(import_node_path71.default.sep).join("/"));
|
|
15247
15918
|
}
|
|
15248
15919
|
}
|
|
15249
15920
|
}
|
|
15250
15921
|
};
|
|
15251
|
-
await
|
|
15922
|
+
await walk10(scanPath);
|
|
15252
15923
|
return found.sort();
|
|
15253
15924
|
}
|
|
15254
15925
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
15255
15926
|
let fallback = null;
|
|
15256
15927
|
for (const file of hookFiles) {
|
|
15257
|
-
const content = await
|
|
15928
|
+
const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
|
|
15258
15929
|
const patched = splicedContent(content, snippet2);
|
|
15259
15930
|
if (patched !== null) return { file, content, patched };
|
|
15260
15931
|
if (fallback === null) fallback = { file, content };
|
|
@@ -15262,12 +15933,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
15262
15933
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
15263
15934
|
}
|
|
15264
15935
|
function extendLogPath() {
|
|
15265
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
15936
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
15266
15937
|
}
|
|
15267
15938
|
async function appendExtendLog(entry2) {
|
|
15268
15939
|
const logPath = extendLogPath();
|
|
15269
|
-
await
|
|
15270
|
-
await
|
|
15940
|
+
await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
|
|
15941
|
+
await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
15271
15942
|
}
|
|
15272
15943
|
function splicedContent(fileContent, snippet2) {
|
|
15273
15944
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -15325,7 +15996,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
15325
15996
|
}
|
|
15326
15997
|
async function describeProjectInstrumentation(ctx) {
|
|
15327
15998
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15328
|
-
const envNeat = await fileExists2(
|
|
15999
|
+
const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
|
|
15329
16000
|
const registryInstrPackages = new Set(
|
|
15330
16001
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
15331
16002
|
);
|
|
@@ -15347,7 +16018,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
15347
16018
|
);
|
|
15348
16019
|
}
|
|
15349
16020
|
for (const file of hookFiles) {
|
|
15350
|
-
const content = await
|
|
16021
|
+
const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
|
|
15351
16022
|
if (content.includes(args.registration_snippet)) {
|
|
15352
16023
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
15353
16024
|
}
|
|
@@ -15359,18 +16030,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
15359
16030
|
);
|
|
15360
16031
|
}
|
|
15361
16032
|
const primaryFile = primary.file;
|
|
15362
|
-
const primaryPath =
|
|
16033
|
+
const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
|
|
15363
16034
|
const filesTouched = [];
|
|
15364
16035
|
const depsAdded = [];
|
|
15365
|
-
const pkgPath =
|
|
16036
|
+
const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
|
|
15366
16037
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15367
16038
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
15368
16039
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
15369
|
-
await
|
|
16040
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15370
16041
|
filesTouched.push("package.json");
|
|
15371
16042
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
15372
16043
|
}
|
|
15373
|
-
await
|
|
16044
|
+
await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
15374
16045
|
filesTouched.push(primaryFile);
|
|
15375
16046
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
15376
16047
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -15401,7 +16072,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
15401
16072
|
};
|
|
15402
16073
|
}
|
|
15403
16074
|
for (const file of hookFiles) {
|
|
15404
|
-
const content = await
|
|
16075
|
+
const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
|
|
15405
16076
|
if (content.includes(args.registration_snippet)) {
|
|
15406
16077
|
return {
|
|
15407
16078
|
library: args.library,
|
|
@@ -15436,28 +16107,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
15436
16107
|
if (!await fileExists2(logPath)) {
|
|
15437
16108
|
return { undone: false, message: "no apply found for library" };
|
|
15438
16109
|
}
|
|
15439
|
-
const raw = await
|
|
16110
|
+
const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
|
|
15440
16111
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
15441
16112
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
15442
16113
|
if (!match) {
|
|
15443
16114
|
return { undone: false, message: "no apply found for library" };
|
|
15444
16115
|
}
|
|
15445
|
-
const pkgPath =
|
|
16116
|
+
const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
|
|
15446
16117
|
if (await fileExists2(pkgPath)) {
|
|
15447
16118
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15448
16119
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
15449
16120
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
15450
16121
|
pkg.dependencies = rest;
|
|
15451
|
-
await
|
|
16122
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15452
16123
|
}
|
|
15453
16124
|
}
|
|
15454
16125
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15455
16126
|
for (const file of hookFiles) {
|
|
15456
|
-
const filePath =
|
|
15457
|
-
const content = await
|
|
16127
|
+
const filePath = import_node_path71.default.join(ctx.scanPath, file);
|
|
16128
|
+
const content = await import_node_fs35.promises.readFile(filePath, "utf8");
|
|
15458
16129
|
if (content.includes(match.registration_snippet)) {
|
|
15459
16130
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
15460
|
-
await
|
|
16131
|
+
await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
|
|
15461
16132
|
break;
|
|
15462
16133
|
}
|
|
15463
16134
|
}
|
|
@@ -15469,39 +16140,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
15469
16140
|
|
|
15470
16141
|
// src/divergences.ts
|
|
15471
16142
|
init_cjs_shims();
|
|
15472
|
-
var
|
|
16143
|
+
var import_types58 = require("@neat.is/types");
|
|
15473
16144
|
function bucketKey(source, target, type) {
|
|
15474
16145
|
return `${type}|${source}|${target}`;
|
|
15475
16146
|
}
|
|
15476
16147
|
function bucketSourceFor(graph, edge) {
|
|
15477
|
-
if (edge.type !==
|
|
15478
|
-
const parsed = (0,
|
|
16148
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
|
|
16149
|
+
const parsed = (0, import_types58.parseFileId)(edge.source);
|
|
15479
16150
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
15480
16151
|
const target = graph.getNodeAttributes(edge.target);
|
|
15481
|
-
if (target.type !==
|
|
15482
|
-
return (0,
|
|
16152
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
|
|
16153
|
+
return (0, import_types58.serviceId)(parsed.service);
|
|
15483
16154
|
}
|
|
15484
16155
|
function bucketEdges(graph) {
|
|
15485
16156
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
15486
16157
|
graph.forEachEdge((id, attrs) => {
|
|
15487
16158
|
const e = attrs;
|
|
15488
|
-
const parsed = (0,
|
|
16159
|
+
const parsed = (0, import_types58.parseEdgeId)(id);
|
|
15489
16160
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
15490
16161
|
const source = bucketSourceFor(graph, e);
|
|
15491
16162
|
const key = bucketKey(source, e.target, e.type);
|
|
15492
16163
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
15493
16164
|
switch (provenance) {
|
|
15494
|
-
case
|
|
16165
|
+
case import_types58.Provenance.EXTRACTED:
|
|
15495
16166
|
cur.extracted = e;
|
|
15496
16167
|
break;
|
|
15497
|
-
case
|
|
16168
|
+
case import_types58.Provenance.OBSERVED:
|
|
15498
16169
|
cur.observed = e;
|
|
15499
16170
|
break;
|
|
15500
|
-
case
|
|
16171
|
+
case import_types58.Provenance.INFERRED:
|
|
15501
16172
|
cur.inferred = e;
|
|
15502
16173
|
break;
|
|
15503
16174
|
default:
|
|
15504
|
-
if (e.provenance ===
|
|
16175
|
+
if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
|
|
15505
16176
|
}
|
|
15506
16177
|
buckets2.set(key, cur);
|
|
15507
16178
|
});
|
|
@@ -15510,22 +16181,22 @@ function bucketEdges(graph) {
|
|
|
15510
16181
|
function nodeIsFrontier(graph, nodeId) {
|
|
15511
16182
|
if (!graph.hasNode(nodeId)) return false;
|
|
15512
16183
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15513
|
-
return attrs.type ===
|
|
16184
|
+
return attrs.type === import_types58.NodeType.FrontierNode;
|
|
15514
16185
|
}
|
|
15515
16186
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
15516
16187
|
if (!graph.hasNode(nodeId)) return false;
|
|
15517
16188
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15518
|
-
return attrs.type ===
|
|
16189
|
+
return attrs.type === import_types58.NodeType.WebSocketChannelNode;
|
|
15519
16190
|
}
|
|
15520
16191
|
function nodeIsServerAction(graph, nodeId) {
|
|
15521
16192
|
if (!graph.hasNode(nodeId)) return false;
|
|
15522
16193
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15523
|
-
return attrs.type ===
|
|
16194
|
+
return attrs.type === import_types58.NodeType.ServerActionNode;
|
|
15524
16195
|
}
|
|
15525
16196
|
function nodeIsSymbol(graph, nodeId) {
|
|
15526
16197
|
if (!graph.hasNode(nodeId)) return false;
|
|
15527
16198
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15528
|
-
return attrs.type ===
|
|
16199
|
+
return attrs.type === import_types58.NodeType.SymbolNode;
|
|
15529
16200
|
}
|
|
15530
16201
|
function clampConfidence(n) {
|
|
15531
16202
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -15545,14 +16216,14 @@ function gradedConfidence(edge) {
|
|
|
15545
16216
|
return clampConfidence(confidenceForEdge(edge));
|
|
15546
16217
|
}
|
|
15547
16218
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
16219
|
+
import_types58.EdgeType.CALLS,
|
|
16220
|
+
import_types58.EdgeType.CONNECTS_TO,
|
|
16221
|
+
import_types58.EdgeType.PUBLISHES_TO,
|
|
16222
|
+
import_types58.EdgeType.CONSUMES_FROM
|
|
15552
16223
|
]);
|
|
15553
16224
|
function detectMissingDivergences(graph, bucket) {
|
|
15554
16225
|
const out = [];
|
|
15555
|
-
if (bucket.type ===
|
|
16226
|
+
if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
|
|
15556
16227
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
15557
16228
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
15558
16229
|
if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
|
|
@@ -15594,7 +16265,7 @@ function declaredHostFor(svc) {
|
|
|
15594
16265
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
15595
16266
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15596
16267
|
const e = graph.getEdgeAttributes(edgeId);
|
|
15597
|
-
if (e.type ===
|
|
16268
|
+
if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
|
|
15598
16269
|
return true;
|
|
15599
16270
|
}
|
|
15600
16271
|
}
|
|
@@ -15607,10 +16278,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
15607
16278
|
const out = [];
|
|
15608
16279
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15609
16280
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15610
|
-
if (edge.type !==
|
|
15611
|
-
if (edge.provenance !==
|
|
16281
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16282
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15612
16283
|
const target = graph.getNodeAttributes(edge.target);
|
|
15613
|
-
if (target.type !==
|
|
16284
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15614
16285
|
const observedHost = target.host?.trim();
|
|
15615
16286
|
if (!observedHost) continue;
|
|
15616
16287
|
if (observedHost === declaredHost) continue;
|
|
@@ -15632,10 +16303,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
15632
16303
|
const deps = svc.dependencies ?? {};
|
|
15633
16304
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15634
16305
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15635
|
-
if (edge.type !==
|
|
15636
|
-
if (edge.provenance !==
|
|
16306
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16307
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15637
16308
|
const target = graph.getNodeAttributes(edge.target);
|
|
15638
|
-
if (target.type !==
|
|
16309
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15639
16310
|
for (const pair of compatPairs()) {
|
|
15640
16311
|
if (pair.engine !== target.engine) continue;
|
|
15641
16312
|
const declared = deps[pair.driver];
|
|
@@ -15732,7 +16403,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
15732
16403
|
for (const d of all) {
|
|
15733
16404
|
if (d.type !== "host-mismatch") continue;
|
|
15734
16405
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
15735
|
-
declaredHalf.add((0,
|
|
16406
|
+
declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
|
|
15736
16407
|
}
|
|
15737
16408
|
if (observedHalf.size === 0) return all;
|
|
15738
16409
|
return all.filter((d) => {
|
|
@@ -15751,13 +16422,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15751
16422
|
}
|
|
15752
16423
|
graph.forEachNode((nodeId, attrs) => {
|
|
15753
16424
|
const n = attrs;
|
|
15754
|
-
if (n.type ===
|
|
16425
|
+
if (n.type === import_types58.NodeType.ServiceNode) {
|
|
15755
16426
|
const svc = n;
|
|
15756
16427
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
15757
16428
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
15758
16429
|
return;
|
|
15759
16430
|
}
|
|
15760
|
-
if (n.type ===
|
|
16431
|
+
if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
|
|
15761
16432
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
15762
16433
|
}
|
|
15763
16434
|
});
|
|
@@ -15793,7 +16464,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15793
16464
|
const bc = "column" in b && b.column ? b.column : "";
|
|
15794
16465
|
return ac.localeCompare(bc);
|
|
15795
16466
|
});
|
|
15796
|
-
return
|
|
16467
|
+
return import_types58.DivergenceResultSchema.parse({
|
|
15797
16468
|
divergences: filtered,
|
|
15798
16469
|
totalAffected: filtered.length,
|
|
15799
16470
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15847,7 +16518,7 @@ function queryLogEntries(opts) {
|
|
|
15847
16518
|
|
|
15848
16519
|
// src/diff.ts
|
|
15849
16520
|
init_cjs_shims();
|
|
15850
|
-
var
|
|
16521
|
+
var import_node_fs36 = require("fs");
|
|
15851
16522
|
async function loadSnapshotForDiff(target) {
|
|
15852
16523
|
if (/^https?:\/\//i.test(target)) {
|
|
15853
16524
|
const res = await fetch(target);
|
|
@@ -15856,7 +16527,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
15856
16527
|
}
|
|
15857
16528
|
return await res.json();
|
|
15858
16529
|
}
|
|
15859
|
-
const raw = await
|
|
16530
|
+
const raw = await import_node_fs36.promises.readFile(target, "utf8");
|
|
15860
16531
|
return JSON.parse(raw);
|
|
15861
16532
|
}
|
|
15862
16533
|
function indexEntries(entries) {
|
|
@@ -15924,28 +16595,28 @@ function canonicalJson(value) {
|
|
|
15924
16595
|
|
|
15925
16596
|
// src/registry.ts
|
|
15926
16597
|
init_cjs_shims();
|
|
15927
|
-
var
|
|
16598
|
+
var import_node_fs37 = require("fs");
|
|
15928
16599
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
15929
|
-
var
|
|
15930
|
-
var
|
|
16600
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
16601
|
+
var import_types59 = require("@neat.is/types");
|
|
15931
16602
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
15932
16603
|
var LOCK_RETRY_MS = 50;
|
|
15933
16604
|
function neatHome() {
|
|
15934
16605
|
const override = process.env.NEAT_HOME;
|
|
15935
|
-
if (override && override.length > 0) return
|
|
15936
|
-
return
|
|
16606
|
+
if (override && override.length > 0) return import_node_path72.default.resolve(override);
|
|
16607
|
+
return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
|
|
15937
16608
|
}
|
|
15938
16609
|
function registryPath() {
|
|
15939
|
-
return
|
|
16610
|
+
return import_node_path72.default.join(neatHome(), "projects.json");
|
|
15940
16611
|
}
|
|
15941
16612
|
function registryLockPath() {
|
|
15942
|
-
return
|
|
16613
|
+
return import_node_path72.default.join(neatHome(), "projects.json.lock");
|
|
15943
16614
|
}
|
|
15944
16615
|
function daemonPidPath() {
|
|
15945
|
-
return
|
|
16616
|
+
return import_node_path72.default.join(neatHome(), "neatd.pid");
|
|
15946
16617
|
}
|
|
15947
16618
|
function daemonsDir() {
|
|
15948
|
-
return
|
|
16619
|
+
return import_node_path72.default.join(neatHome(), "daemons");
|
|
15949
16620
|
}
|
|
15950
16621
|
function isFiniteInt(v) {
|
|
15951
16622
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -15978,7 +16649,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
15978
16649
|
const dir = daemonsDir();
|
|
15979
16650
|
let names;
|
|
15980
16651
|
try {
|
|
15981
|
-
names = await
|
|
16652
|
+
names = await import_node_fs37.promises.readdir(dir);
|
|
15982
16653
|
} catch (err) {
|
|
15983
16654
|
if (err.code === "ENOENT") return [];
|
|
15984
16655
|
throw err;
|
|
@@ -15986,10 +16657,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
15986
16657
|
const out = [];
|
|
15987
16658
|
for (const name of names) {
|
|
15988
16659
|
if (!name.endsWith(".json")) continue;
|
|
15989
|
-
const file =
|
|
16660
|
+
const file = import_node_path72.default.join(dir, name);
|
|
15990
16661
|
let raw;
|
|
15991
16662
|
try {
|
|
15992
|
-
raw = await
|
|
16663
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
15993
16664
|
} catch {
|
|
15994
16665
|
continue;
|
|
15995
16666
|
}
|
|
@@ -16015,7 +16686,7 @@ function isPidAliveDefault(pid) {
|
|
|
16015
16686
|
}
|
|
16016
16687
|
async function readPidFile(file) {
|
|
16017
16688
|
try {
|
|
16018
|
-
const raw = await
|
|
16689
|
+
const raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16019
16690
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
16020
16691
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
16021
16692
|
} catch {
|
|
@@ -16063,24 +16734,24 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
16063
16734
|
}
|
|
16064
16735
|
}
|
|
16065
16736
|
async function writeAtomically(target, contents) {
|
|
16066
|
-
await
|
|
16737
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
|
|
16067
16738
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
16068
|
-
const fd = await
|
|
16739
|
+
const fd = await import_node_fs37.promises.open(tmp, "w");
|
|
16069
16740
|
try {
|
|
16070
16741
|
await fd.writeFile(contents, "utf8");
|
|
16071
16742
|
await fd.sync();
|
|
16072
16743
|
} finally {
|
|
16073
16744
|
await fd.close();
|
|
16074
16745
|
}
|
|
16075
|
-
await
|
|
16746
|
+
await import_node_fs37.promises.rename(tmp, target);
|
|
16076
16747
|
}
|
|
16077
16748
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
16078
16749
|
const deadline = Date.now() + timeoutMs;
|
|
16079
|
-
await
|
|
16750
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
|
|
16080
16751
|
let probedHolder = false;
|
|
16081
16752
|
while (true) {
|
|
16082
16753
|
try {
|
|
16083
|
-
const fd = await
|
|
16754
|
+
const fd = await import_node_fs37.promises.open(lockPath, "wx");
|
|
16084
16755
|
try {
|
|
16085
16756
|
await fd.writeFile(`${process.pid}
|
|
16086
16757
|
`, "utf8");
|
|
@@ -16105,7 +16776,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
16105
16776
|
}
|
|
16106
16777
|
}
|
|
16107
16778
|
async function releaseLock(lockPath) {
|
|
16108
|
-
await
|
|
16779
|
+
await import_node_fs37.promises.unlink(lockPath).catch(() => {
|
|
16109
16780
|
});
|
|
16110
16781
|
}
|
|
16111
16782
|
async function withLock(fn) {
|
|
@@ -16121,7 +16792,7 @@ async function readRegistry() {
|
|
|
16121
16792
|
const file = registryPath();
|
|
16122
16793
|
let raw;
|
|
16123
16794
|
try {
|
|
16124
|
-
raw = await
|
|
16795
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16125
16796
|
} catch (err) {
|
|
16126
16797
|
if (err.code === "ENOENT") {
|
|
16127
16798
|
return { version: 1, projects: [] };
|
|
@@ -16129,10 +16800,10 @@ async function readRegistry() {
|
|
|
16129
16800
|
throw err;
|
|
16130
16801
|
}
|
|
16131
16802
|
const parsed = JSON.parse(raw);
|
|
16132
|
-
return
|
|
16803
|
+
return import_types59.RegistryFileSchema.parse(parsed);
|
|
16133
16804
|
}
|
|
16134
16805
|
async function writeRegistry(reg) {
|
|
16135
|
-
const validated =
|
|
16806
|
+
const validated = import_types59.RegistryFileSchema.parse(reg);
|
|
16136
16807
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16137
16808
|
}
|
|
16138
16809
|
async function getProject(name) {
|
|
@@ -16176,7 +16847,7 @@ function pruneTtlMs() {
|
|
|
16176
16847
|
}
|
|
16177
16848
|
async function statPathStatus(p) {
|
|
16178
16849
|
try {
|
|
16179
|
-
const stat = await
|
|
16850
|
+
const stat = await import_node_fs37.promises.stat(p);
|
|
16180
16851
|
return stat.isDirectory() ? "present" : "unknown";
|
|
16181
16852
|
} catch (err) {
|
|
16182
16853
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -16277,8 +16948,8 @@ init_auth();
|
|
|
16277
16948
|
// src/connectors-config.ts
|
|
16278
16949
|
init_cjs_shims();
|
|
16279
16950
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
16280
|
-
var
|
|
16281
|
-
var
|
|
16951
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
16952
|
+
var import_node_fs38 = require("fs");
|
|
16282
16953
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
16283
16954
|
var EnvRefUnsetError = class extends Error {
|
|
16284
16955
|
ref;
|
|
@@ -16292,17 +16963,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
16292
16963
|
};
|
|
16293
16964
|
function neatHome2() {
|
|
16294
16965
|
const override = process.env.NEAT_HOME;
|
|
16295
|
-
if (override && override.length > 0) return
|
|
16296
|
-
return
|
|
16966
|
+
if (override && override.length > 0) return import_node_path73.default.resolve(override);
|
|
16967
|
+
return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
|
|
16297
16968
|
}
|
|
16298
16969
|
function connectorsConfigPath(home = neatHome2()) {
|
|
16299
|
-
return
|
|
16970
|
+
return import_node_path73.default.join(home, "connectors.json");
|
|
16300
16971
|
}
|
|
16301
16972
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
16302
16973
|
async function warnIfModeLooserThan0600(file) {
|
|
16303
16974
|
if (process.platform === "win32") return;
|
|
16304
16975
|
try {
|
|
16305
|
-
const stat = await
|
|
16976
|
+
const stat = await import_node_fs38.promises.stat(file);
|
|
16306
16977
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
16307
16978
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
16308
16979
|
console.warn(
|
|
@@ -16316,7 +16987,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
16316
16987
|
const file = connectorsConfigPath(home);
|
|
16317
16988
|
let raw;
|
|
16318
16989
|
try {
|
|
16319
|
-
raw = await
|
|
16990
|
+
raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
16320
16991
|
} catch (err) {
|
|
16321
16992
|
if (err.code === "ENOENT") {
|
|
16322
16993
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -16483,15 +17154,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
16483
17154
|
|
|
16484
17155
|
// src/connectors/index.ts
|
|
16485
17156
|
init_cjs_shims();
|
|
16486
|
-
var
|
|
17157
|
+
var import_types60 = require("@neat.is/types");
|
|
16487
17158
|
var NO_ENV = "unknown";
|
|
16488
17159
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
16489
17160
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16490
17161
|
const sites = [];
|
|
16491
17162
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
16492
17163
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16493
|
-
if (edge.provenance !==
|
|
16494
|
-
const parsed = (0,
|
|
17164
|
+
if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
|
|
17165
|
+
const parsed = (0, import_types60.parseFileId)(edge.source);
|
|
16495
17166
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
16496
17167
|
const site = { relPath: edge.evidence.file };
|
|
16497
17168
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -16502,7 +17173,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
16502
17173
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
16503
17174
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16504
17175
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
16505
|
-
if (attrs.type !==
|
|
17176
|
+
if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
|
|
16506
17177
|
const site = { relPath: attrs.path };
|
|
16507
17178
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
16508
17179
|
return site;
|
|
@@ -17000,10 +17671,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
17000
17671
|
// src/connectors/supabase/map.ts
|
|
17001
17672
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
17002
17673
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
17003
|
-
function targetFromRestPath(
|
|
17004
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
17674
|
+
function targetFromRestPath(path78) {
|
|
17675
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path78);
|
|
17005
17676
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
17006
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
17677
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path78);
|
|
17007
17678
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
17008
17679
|
return null;
|
|
17009
17680
|
}
|
|
@@ -17114,23 +17785,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17114
17785
|
|
|
17115
17786
|
// src/connectors/supabase/resolve.ts
|
|
17116
17787
|
init_cjs_shims();
|
|
17117
|
-
var
|
|
17788
|
+
var import_types62 = require("@neat.is/types");
|
|
17118
17789
|
function createSupabaseResolveTarget(graph, config) {
|
|
17119
17790
|
return (signal, _ctx) => {
|
|
17120
17791
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17121
17792
|
return null;
|
|
17122
17793
|
}
|
|
17123
|
-
const subResourceId = (0,
|
|
17794
|
+
const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17124
17795
|
if (graph.hasNode(subResourceId)) {
|
|
17125
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
17796
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17126
17797
|
}
|
|
17127
|
-
const bareResourceId = (0,
|
|
17798
|
+
const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
|
|
17128
17799
|
if (graph.hasNode(bareResourceId)) {
|
|
17129
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
17800
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17130
17801
|
}
|
|
17131
|
-
const projectLevelId = (0,
|
|
17802
|
+
const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
|
|
17132
17803
|
if (graph.hasNode(projectLevelId)) {
|
|
17133
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
17804
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17134
17805
|
}
|
|
17135
17806
|
return null;
|
|
17136
17807
|
};
|
|
@@ -17223,7 +17894,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
17223
17894
|
|
|
17224
17895
|
// src/connectors/railway/index.ts
|
|
17225
17896
|
init_cjs_shims();
|
|
17226
|
-
var
|
|
17897
|
+
var import_types66 = require("@neat.is/types");
|
|
17227
17898
|
|
|
17228
17899
|
// src/connectors/railway/client.ts
|
|
17229
17900
|
init_cjs_shims();
|
|
@@ -17374,7 +18045,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
17374
18045
|
const out = [];
|
|
17375
18046
|
graph.forEachNode((_id, attrs) => {
|
|
17376
18047
|
const node = attrs;
|
|
17377
|
-
if (node.type !==
|
|
18048
|
+
if (node.type !== import_types66.NodeType.RouteNode) return;
|
|
17378
18049
|
const route = attrs;
|
|
17379
18050
|
if (route.service !== serviceName) return;
|
|
17380
18051
|
out.push({
|
|
@@ -17478,12 +18149,12 @@ function createRailwayResolveTarget(config) {
|
|
|
17478
18149
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
17479
18150
|
if (!serviceName) return null;
|
|
17480
18151
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
17481
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18152
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
17482
18153
|
}
|
|
17483
18154
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
17484
18155
|
const peerName = config.serviceNameById[signal.targetName];
|
|
17485
18156
|
if (!peerName) return null;
|
|
17486
|
-
return { targetNodeId: (0,
|
|
18157
|
+
return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
|
|
17487
18158
|
}
|
|
17488
18159
|
return null;
|
|
17489
18160
|
};
|
|
@@ -17607,9 +18278,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
17607
18278
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
17608
18279
|
if (secondSep === -1) return null;
|
|
17609
18280
|
const method = rest.slice(0, secondSep);
|
|
17610
|
-
const
|
|
17611
|
-
if (!resourceName || !method || !
|
|
17612
|
-
return { resourceName, method, path:
|
|
18281
|
+
const path78 = rest.slice(secondSep + 1);
|
|
18282
|
+
if (!resourceName || !method || !path78) return null;
|
|
18283
|
+
return { resourceName, method, path: path78 };
|
|
17613
18284
|
}
|
|
17614
18285
|
function resourceNameFor(type, labels) {
|
|
17615
18286
|
if (!labels) return null;
|
|
@@ -17647,14 +18318,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
17647
18318
|
if (!req2) return null;
|
|
17648
18319
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
17649
18320
|
const method = req2.requestMethod.toUpperCase();
|
|
17650
|
-
const
|
|
17651
|
-
if (
|
|
18321
|
+
const path78 = pathFromRequestUrl(req2.requestUrl);
|
|
18322
|
+
if (path78 === null) return null;
|
|
17652
18323
|
const timestamp = entry2.timestamp;
|
|
17653
18324
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
17654
18325
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
17655
18326
|
return {
|
|
17656
18327
|
targetKind: resourceType,
|
|
17657
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
18328
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
|
|
17658
18329
|
callCount: 1,
|
|
17659
18330
|
errorCount: isError ? 1 : 0,
|
|
17660
18331
|
lastObservedIso: timestamp
|
|
@@ -17671,7 +18342,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
17671
18342
|
|
|
17672
18343
|
// src/connectors/firebase/resolve.ts
|
|
17673
18344
|
init_cjs_shims();
|
|
17674
|
-
var
|
|
18345
|
+
var import_types67 = require("@neat.is/types");
|
|
17675
18346
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
17676
18347
|
switch (resourceType) {
|
|
17677
18348
|
case "cloud_function":
|
|
@@ -17686,7 +18357,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
17686
18357
|
const entries = [];
|
|
17687
18358
|
graph.forEachNode((_id, attrs) => {
|
|
17688
18359
|
const node = attrs;
|
|
17689
|
-
if (node.type !==
|
|
18360
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
17690
18361
|
const route = attrs;
|
|
17691
18362
|
if (route.service !== serviceName) return;
|
|
17692
18363
|
entries.push({
|
|
@@ -17718,7 +18389,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
17718
18389
|
return {
|
|
17719
18390
|
targetNodeId: match.routeNodeId,
|
|
17720
18391
|
serviceName,
|
|
17721
|
-
edgeType:
|
|
18392
|
+
edgeType: import_types67.EdgeType.CALLS
|
|
17722
18393
|
};
|
|
17723
18394
|
};
|
|
17724
18395
|
}
|
|
@@ -17745,7 +18416,7 @@ init_cjs_shims();
|
|
|
17745
18416
|
|
|
17746
18417
|
// src/connectors/cloudflare/connector.ts
|
|
17747
18418
|
init_cjs_shims();
|
|
17748
|
-
var
|
|
18419
|
+
var import_types69 = require("@neat.is/types");
|
|
17749
18420
|
|
|
17750
18421
|
// src/connectors/cloudflare/client.ts
|
|
17751
18422
|
init_cjs_shims();
|
|
@@ -17861,7 +18532,7 @@ function mapEventToSignal(event) {
|
|
|
17861
18532
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
17862
18533
|
const statusCode = metadata?.statusCode;
|
|
17863
18534
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
17864
|
-
const
|
|
18535
|
+
const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
17865
18536
|
return {
|
|
17866
18537
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
17867
18538
|
targetName: scriptName,
|
|
@@ -17869,7 +18540,7 @@ function mapEventToSignal(event) {
|
|
|
17869
18540
|
errorCount: isError ? 1 : 0,
|
|
17870
18541
|
lastObservedIso: observedAt.toISOString(),
|
|
17871
18542
|
method,
|
|
17872
|
-
...
|
|
18543
|
+
...path78 ? { path: path78 } : {},
|
|
17873
18544
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
17874
18545
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
17875
18546
|
};
|
|
@@ -17909,19 +18580,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
17909
18580
|
graph.forEachNode((id, attrs) => {
|
|
17910
18581
|
if (found) return;
|
|
17911
18582
|
const a = attrs;
|
|
17912
|
-
if (a.type ===
|
|
18583
|
+
if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
17913
18584
|
found = id;
|
|
17914
18585
|
}
|
|
17915
18586
|
});
|
|
17916
18587
|
return found;
|
|
17917
18588
|
}
|
|
17918
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
17919
|
-
const normalizedPath = normalizePathTemplate(
|
|
18589
|
+
function findMatchingRouteNode(graph, serviceName, method, path78) {
|
|
18590
|
+
const normalizedPath = normalizePathTemplate(path78);
|
|
17920
18591
|
let found = null;
|
|
17921
18592
|
graph.forEachNode((id, attrs) => {
|
|
17922
18593
|
if (found) return;
|
|
17923
18594
|
const a = attrs;
|
|
17924
|
-
if (a.type !==
|
|
18595
|
+
if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
|
|
17925
18596
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
17926
18597
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
17927
18598
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -17933,18 +18604,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
17933
18604
|
return (signal) => {
|
|
17934
18605
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
17935
18606
|
const scriptName = signal.targetName;
|
|
17936
|
-
const { method, path:
|
|
18607
|
+
const { method, path: path78 } = signal;
|
|
17937
18608
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
17938
|
-
if (!method || !
|
|
17939
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
18609
|
+
if (!method || !path78) return wholeFileId;
|
|
18610
|
+
return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
|
|
17940
18611
|
};
|
|
17941
18612
|
const mapping = config.workers?.[scriptName];
|
|
17942
18613
|
if (mapping) {
|
|
17943
|
-
const wholeFileId = (0,
|
|
18614
|
+
const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
|
|
17944
18615
|
return {
|
|
17945
18616
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
17946
18617
|
serviceName: mapping.service,
|
|
17947
|
-
edgeType:
|
|
18618
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
17948
18619
|
};
|
|
17949
18620
|
}
|
|
17950
18621
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -17953,13 +18624,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
17953
18624
|
return {
|
|
17954
18625
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
17955
18626
|
serviceName: fileNode.service,
|
|
17956
|
-
edgeType:
|
|
18627
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
17957
18628
|
};
|
|
17958
18629
|
}
|
|
17959
18630
|
return {
|
|
17960
|
-
targetNodeId: (0,
|
|
18631
|
+
targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
|
|
17961
18632
|
serviceName: scriptName,
|
|
17962
|
-
edgeType:
|
|
18633
|
+
edgeType: import_types69.EdgeType.CALLS,
|
|
17963
18634
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
17964
18635
|
};
|
|
17965
18636
|
};
|
|
@@ -18155,14 +18826,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18155
18826
|
|
|
18156
18827
|
// src/connectors/neon/resolve.ts
|
|
18157
18828
|
init_cjs_shims();
|
|
18158
|
-
var
|
|
18829
|
+
var import_types73 = require("@neat.is/types");
|
|
18159
18830
|
function createNeonResolveTarget(config) {
|
|
18160
18831
|
return (signal) => {
|
|
18161
18832
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18162
18833
|
return {
|
|
18163
|
-
targetNodeId: (0,
|
|
18834
|
+
targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
|
|
18164
18835
|
serviceName: config.serviceName,
|
|
18165
|
-
edgeType:
|
|
18836
|
+
edgeType: import_types73.EdgeType.CALLS,
|
|
18166
18837
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18167
18838
|
};
|
|
18168
18839
|
};
|
|
@@ -18288,9 +18959,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
18288
18959
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
18289
18960
|
if (secondSep === -1) return null;
|
|
18290
18961
|
const method = rest.slice(0, secondSep);
|
|
18291
|
-
const
|
|
18292
|
-
if (!serviceName || !method || !
|
|
18293
|
-
return { serviceName, method, path:
|
|
18962
|
+
const path78 = rest.slice(secondSep + 1);
|
|
18963
|
+
if (!serviceName || !method || !path78) return null;
|
|
18964
|
+
return { serviceName, method, path: path78 };
|
|
18294
18965
|
}
|
|
18295
18966
|
|
|
18296
18967
|
// src/connectors/cloud-run/map.ts
|
|
@@ -18319,14 +18990,14 @@ function mapLogEntryToSignal2(entry2) {
|
|
|
18319
18990
|
if (!req2) return null;
|
|
18320
18991
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
18321
18992
|
const method = req2.requestMethod.toUpperCase();
|
|
18322
|
-
const
|
|
18323
|
-
if (
|
|
18993
|
+
const path78 = pathFromRequestUrl2(req2.requestUrl);
|
|
18994
|
+
if (path78 === null) return null;
|
|
18324
18995
|
const timestamp = entry2.timestamp;
|
|
18325
18996
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
18326
18997
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
|
|
18327
18998
|
return {
|
|
18328
18999
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
18329
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
19000
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
|
|
18330
19001
|
callCount: 1,
|
|
18331
19002
|
errorCount: isError ? 1 : 0,
|
|
18332
19003
|
lastObservedIso: timestamp
|
|
@@ -18343,14 +19014,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
18343
19014
|
|
|
18344
19015
|
// src/connectors/cloud-run/resolve.ts
|
|
18345
19016
|
init_cjs_shims();
|
|
18346
|
-
var
|
|
19017
|
+
var import_types77 = require("@neat.is/types");
|
|
18347
19018
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
18348
19019
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
18349
19020
|
let found = null;
|
|
18350
19021
|
graph.forEachNode((_id, attrs) => {
|
|
18351
19022
|
if (found) return;
|
|
18352
19023
|
const node = attrs;
|
|
18353
|
-
if (node.type !==
|
|
19024
|
+
if (node.type !== import_types77.NodeType.RouteNode) return;
|
|
18354
19025
|
const route = attrs;
|
|
18355
19026
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
18356
19027
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -18365,23 +19036,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
18365
19036
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
18366
19037
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
18367
19038
|
if (!identity) return null;
|
|
18368
|
-
const { serviceName: gcpServiceName, method, path:
|
|
19039
|
+
const { serviceName: gcpServiceName, method, path: path78 } = identity;
|
|
18369
19040
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
18370
19041
|
if (mappedService) {
|
|
18371
19042
|
const routeNodeId = findMatchingRouteNode2(
|
|
18372
19043
|
graph,
|
|
18373
19044
|
mappedService,
|
|
18374
19045
|
method,
|
|
18375
|
-
normalizePathTemplate(
|
|
19046
|
+
normalizePathTemplate(path78)
|
|
18376
19047
|
);
|
|
18377
19048
|
if (routeNodeId) {
|
|
18378
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19049
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
|
|
18379
19050
|
}
|
|
18380
19051
|
}
|
|
18381
19052
|
return {
|
|
18382
|
-
targetNodeId: (0,
|
|
19053
|
+
targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
18383
19054
|
serviceName: mappedService ?? gcpServiceName,
|
|
18384
|
-
edgeType:
|
|
19055
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
18385
19056
|
ensureInfraNode: {
|
|
18386
19057
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
18387
19058
|
name: gcpServiceName,
|
|
@@ -18422,7 +19093,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
18422
19093
|
|
|
18423
19094
|
// src/connectors/render/index.ts
|
|
18424
19095
|
init_cjs_shims();
|
|
18425
|
-
var
|
|
19096
|
+
var import_types80 = require("@neat.is/types");
|
|
18426
19097
|
|
|
18427
19098
|
// src/connectors/render/types.ts
|
|
18428
19099
|
init_cjs_shims();
|
|
@@ -18500,7 +19171,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
18500
19171
|
const out = [];
|
|
18501
19172
|
graph.forEachNode((_id, attrs) => {
|
|
18502
19173
|
const node = attrs;
|
|
18503
|
-
if (node.type !==
|
|
19174
|
+
if (node.type !== import_types80.NodeType.RouteNode) return;
|
|
18504
19175
|
const route = attrs;
|
|
18505
19176
|
if (route.service !== serviceName) return;
|
|
18506
19177
|
out.push({
|
|
@@ -18585,7 +19256,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
18585
19256
|
function createRenderResolveTarget(config) {
|
|
18586
19257
|
return (signal) => {
|
|
18587
19258
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
18588
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
19259
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
|
|
18589
19260
|
}
|
|
18590
19261
|
return null;
|
|
18591
19262
|
};
|
|
@@ -18723,21 +19394,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
18723
19394
|
|
|
18724
19395
|
// src/connectors/planetscale/resolve.ts
|
|
18725
19396
|
init_cjs_shims();
|
|
18726
|
-
var
|
|
19397
|
+
var import_types84 = require("@neat.is/types");
|
|
18727
19398
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
18728
19399
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
18729
19400
|
const databaseName = `${config.organization}/${config.database}`;
|
|
18730
19401
|
return (signal, _ctx) => {
|
|
18731
19402
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18732
|
-
const tableId = (0,
|
|
19403
|
+
const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
|
|
18733
19404
|
if (graph.hasNode(tableId)) {
|
|
18734
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
19405
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
|
|
18735
19406
|
}
|
|
18736
|
-
const providerId = (0,
|
|
19407
|
+
const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
18737
19408
|
return {
|
|
18738
19409
|
targetNodeId: providerId,
|
|
18739
19410
|
serviceName: config.serviceName,
|
|
18740
|
-
edgeType:
|
|
19411
|
+
edgeType: import_types84.EdgeType.CALLS,
|
|
18741
19412
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
18742
19413
|
};
|
|
18743
19414
|
};
|
|
@@ -19002,7 +19673,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19002
19673
|
|
|
19003
19674
|
// src/connectors/eas/resolve.ts
|
|
19004
19675
|
init_cjs_shims();
|
|
19005
|
-
var
|
|
19676
|
+
var import_types89 = require("@neat.is/types");
|
|
19006
19677
|
var NO_ENV2 = "unknown";
|
|
19007
19678
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19008
19679
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19018,8 +19689,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19018
19689
|
function configNodeService(graph, configNodeId) {
|
|
19019
19690
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19020
19691
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19021
|
-
if (edge.type !==
|
|
19022
|
-
const parsed = (0,
|
|
19692
|
+
if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
|
|
19693
|
+
const parsed = (0, import_types89.parseFileId)(edge.source);
|
|
19023
19694
|
if (parsed) return parsed.service;
|
|
19024
19695
|
}
|
|
19025
19696
|
return null;
|
|
@@ -19030,7 +19701,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19030
19701
|
graph.forEachNode((id, attrs) => {
|
|
19031
19702
|
if (scoped) return;
|
|
19032
19703
|
const node = attrs;
|
|
19033
|
-
if (node.type !==
|
|
19704
|
+
if (node.type !== import_types89.NodeType.ConfigNode) return;
|
|
19034
19705
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19035
19706
|
if (anyMatch === null) anyMatch = id;
|
|
19036
19707
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19047,13 +19718,13 @@ function createEasResolveTarget(graph) {
|
|
|
19047
19718
|
if (basenames.length > 0) {
|
|
19048
19719
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19049
19720
|
if (configNodeId) {
|
|
19050
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
19721
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
|
|
19051
19722
|
}
|
|
19052
19723
|
}
|
|
19053
19724
|
return {
|
|
19054
19725
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19055
19726
|
serviceName,
|
|
19056
|
-
edgeType:
|
|
19727
|
+
edgeType: import_types89.EdgeType.CALLS
|
|
19057
19728
|
};
|
|
19058
19729
|
};
|
|
19059
19730
|
}
|
|
@@ -19778,11 +20449,11 @@ function registerRoutes(scope, ctx) {
|
|
|
19778
20449
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
19779
20450
|
const parsed = [];
|
|
19780
20451
|
for (const c of candidates) {
|
|
19781
|
-
const r =
|
|
20452
|
+
const r = import_types92.DivergenceTypeSchema.safeParse(c);
|
|
19782
20453
|
if (!r.success) {
|
|
19783
20454
|
return reply.code(400).send({
|
|
19784
20455
|
error: `unknown divergence type "${c}"`,
|
|
19785
|
-
allowed:
|
|
20456
|
+
allowed: import_types92.DivergenceTypeSchema.options
|
|
19786
20457
|
});
|
|
19787
20458
|
}
|
|
19788
20459
|
parsed.push(r.data);
|
|
@@ -20124,7 +20795,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20124
20795
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20125
20796
|
let violations = await log.readAll();
|
|
20126
20797
|
if (req2.query.severity) {
|
|
20127
|
-
const sev =
|
|
20798
|
+
const sev = import_types92.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
20128
20799
|
if (!sev.success) {
|
|
20129
20800
|
return reply.code(400).send({
|
|
20130
20801
|
error: "invalid severity",
|
|
@@ -20163,7 +20834,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20163
20834
|
scope.post("/policies/check", async (req2, reply) => {
|
|
20164
20835
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
20165
20836
|
if (!proj) return;
|
|
20166
|
-
const parsed =
|
|
20837
|
+
const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
20167
20838
|
if (!parsed.success) {
|
|
20168
20839
|
return reply.code(400).send({
|
|
20169
20840
|
error: "invalid /policies/check body",
|
|
@@ -20484,8 +21155,8 @@ init_auth();
|
|
|
20484
21155
|
|
|
20485
21156
|
// src/unrouted.ts
|
|
20486
21157
|
init_cjs_shims();
|
|
20487
|
-
var
|
|
20488
|
-
var
|
|
21158
|
+
var import_node_fs39 = require("fs");
|
|
21159
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
20489
21160
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
20490
21161
|
return {
|
|
20491
21162
|
timestamp: now.toISOString(),
|
|
@@ -20495,34 +21166,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
20495
21166
|
};
|
|
20496
21167
|
}
|
|
20497
21168
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
20498
|
-
const target =
|
|
20499
|
-
await
|
|
20500
|
-
await
|
|
21169
|
+
const target = import_node_path74.default.join(neatHome4, "errors.ndjson");
|
|
21170
|
+
await import_node_fs39.promises.mkdir(neatHome4, { recursive: true });
|
|
21171
|
+
await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
20501
21172
|
}
|
|
20502
21173
|
function unroutedErrorsPath(neatHome4) {
|
|
20503
|
-
return
|
|
21174
|
+
return import_node_path74.default.join(neatHome4, "errors.ndjson");
|
|
20504
21175
|
}
|
|
20505
21176
|
|
|
20506
21177
|
// src/daemon.ts
|
|
20507
|
-
var
|
|
21178
|
+
var import_types93 = require("@neat.is/types");
|
|
20508
21179
|
function daemonJsonPath(scanPath) {
|
|
20509
|
-
return
|
|
21180
|
+
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
20510
21181
|
}
|
|
20511
21182
|
function daemonsDiscoveryDir(home) {
|
|
20512
21183
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
20513
|
-
return
|
|
21184
|
+
return import_node_path75.default.join(base, "daemons");
|
|
20514
21185
|
}
|
|
20515
21186
|
function daemonDiscoveryPath(project, home) {
|
|
20516
|
-
return
|
|
21187
|
+
return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
20517
21188
|
}
|
|
20518
21189
|
function sanitizeDiscoveryName(project) {
|
|
20519
21190
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
20520
21191
|
}
|
|
20521
21192
|
function neatHomeFromEnv() {
|
|
20522
21193
|
const env = process.env.NEAT_HOME;
|
|
20523
|
-
if (env && env.length > 0) return
|
|
21194
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20524
21195
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20525
|
-
return
|
|
21196
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20526
21197
|
}
|
|
20527
21198
|
function resolveNeatVersion() {
|
|
20528
21199
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -20554,7 +21225,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
20554
21225
|
} catch {
|
|
20555
21226
|
}
|
|
20556
21227
|
try {
|
|
20557
|
-
await
|
|
21228
|
+
await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
20558
21229
|
} catch {
|
|
20559
21230
|
}
|
|
20560
21231
|
}
|
|
@@ -20563,12 +21234,12 @@ function reconcileDaemonRecordSync(record, home) {
|
|
|
20563
21234
|
const stopped = { ...record, status: "stopped" };
|
|
20564
21235
|
const target = daemonJsonPath(record.projectPath);
|
|
20565
21236
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
20566
|
-
(0,
|
|
20567
|
-
(0,
|
|
21237
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
|
|
21238
|
+
(0, import_node_fs40.renameSync)(tmp, target);
|
|
20568
21239
|
} catch {
|
|
20569
21240
|
}
|
|
20570
21241
|
try {
|
|
20571
|
-
(0,
|
|
21242
|
+
(0, import_node_fs40.unlinkSync)(daemonDiscoveryPath(record.project, home));
|
|
20572
21243
|
} catch {
|
|
20573
21244
|
}
|
|
20574
21245
|
}
|
|
@@ -20591,11 +21262,11 @@ function teardownSlot(slot) {
|
|
|
20591
21262
|
}
|
|
20592
21263
|
}
|
|
20593
21264
|
function neatHomeFor(opts) {
|
|
20594
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
21265
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
|
|
20595
21266
|
const env = process.env.NEAT_HOME;
|
|
20596
|
-
if (env && env.length > 0) return
|
|
21267
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20597
21268
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20598
|
-
return
|
|
21269
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20599
21270
|
}
|
|
20600
21271
|
function routeSpanToProject(serviceName, projects) {
|
|
20601
21272
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -20643,13 +21314,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
20643
21314
|
if (!serviceName) return true;
|
|
20644
21315
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
20645
21316
|
return graph.someNode(
|
|
20646
|
-
(_id, attrs) => attrs.type ===
|
|
21317
|
+
(_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
|
|
20647
21318
|
);
|
|
20648
21319
|
}
|
|
20649
21320
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
20650
|
-
const paths = pathsForProject(entry2.name,
|
|
21321
|
+
const paths = pathsForProject(entry2.name, import_node_path75.default.join(entry2.path, "neat-out"));
|
|
20651
21322
|
try {
|
|
20652
|
-
const stat = await
|
|
21323
|
+
const stat = await import_node_fs40.promises.stat(entry2.path);
|
|
20653
21324
|
if (!stat.isDirectory()) {
|
|
20654
21325
|
throw new Error(`registered path ${entry2.path} is not a directory`);
|
|
20655
21326
|
}
|
|
@@ -20767,7 +21438,7 @@ async function startDaemon(opts = {}) {
|
|
|
20767
21438
|
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;
|
|
20768
21439
|
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;
|
|
20769
21440
|
const singleProject = projectArg;
|
|
20770
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
21441
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
|
|
20771
21442
|
if (singleProject && !singleProjectPath) {
|
|
20772
21443
|
throw new Error(
|
|
20773
21444
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -20775,14 +21446,14 @@ async function startDaemon(opts = {}) {
|
|
|
20775
21446
|
}
|
|
20776
21447
|
if (!singleProject) {
|
|
20777
21448
|
try {
|
|
20778
|
-
await
|
|
21449
|
+
await import_node_fs40.promises.access(regPath);
|
|
20779
21450
|
} catch {
|
|
20780
21451
|
throw new Error(
|
|
20781
21452
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
20782
21453
|
);
|
|
20783
21454
|
}
|
|
20784
21455
|
}
|
|
20785
|
-
const pidPath =
|
|
21456
|
+
const pidPath = import_node_path75.default.join(home, "neatd.pid");
|
|
20786
21457
|
await writeAtomically(pidPath, `${process.pid}
|
|
20787
21458
|
`);
|
|
20788
21459
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -20994,7 +21665,7 @@ async function startDaemon(opts = {}) {
|
|
|
20994
21665
|
}
|
|
20995
21666
|
if (restApp) await restApp.close().catch(() => {
|
|
20996
21667
|
});
|
|
20997
|
-
await
|
|
21668
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
20998
21669
|
});
|
|
20999
21670
|
throw new Error(
|
|
21000
21671
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -21162,7 +21833,7 @@ async function startDaemon(opts = {}) {
|
|
|
21162
21833
|
});
|
|
21163
21834
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21164
21835
|
});
|
|
21165
|
-
await
|
|
21836
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21166
21837
|
});
|
|
21167
21838
|
throw new Error(
|
|
21168
21839
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -21197,7 +21868,7 @@ async function startDaemon(opts = {}) {
|
|
|
21197
21868
|
});
|
|
21198
21869
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21199
21870
|
});
|
|
21200
|
-
await
|
|
21871
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21201
21872
|
});
|
|
21202
21873
|
throw new Error(
|
|
21203
21874
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -21244,9 +21915,9 @@ async function startDaemon(opts = {}) {
|
|
|
21244
21915
|
let registryWatcher = null;
|
|
21245
21916
|
let reloadTimer = null;
|
|
21246
21917
|
if (!singleProject) try {
|
|
21247
|
-
const regDir =
|
|
21248
|
-
const regBase =
|
|
21249
|
-
registryWatcher = (0,
|
|
21918
|
+
const regDir = import_node_path75.default.dirname(regPath);
|
|
21919
|
+
const regBase = import_node_path75.default.basename(regPath);
|
|
21920
|
+
registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
|
|
21250
21921
|
if (filename !== null && filename !== regBase) return;
|
|
21251
21922
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
21252
21923
|
reloadTimer = setTimeout(() => {
|
|
@@ -21295,7 +21966,7 @@ async function startDaemon(opts = {}) {
|
|
|
21295
21966
|
if (daemonRecord) {
|
|
21296
21967
|
await clearDaemonRecord(daemonRecord, home);
|
|
21297
21968
|
}
|
|
21298
|
-
await
|
|
21969
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21299
21970
|
});
|
|
21300
21971
|
};
|
|
21301
21972
|
return {
|
|
@@ -21318,9 +21989,9 @@ init_auth();
|
|
|
21318
21989
|
// src/web-spawn.ts
|
|
21319
21990
|
init_cjs_shims();
|
|
21320
21991
|
var import_node_child_process2 = require("child_process");
|
|
21321
|
-
var
|
|
21992
|
+
var import_node_fs41 = require("fs");
|
|
21322
21993
|
var import_node_net = __toESM(require("net"), 1);
|
|
21323
|
-
var
|
|
21994
|
+
var import_node_path76 = __toESM(require("path"), 1);
|
|
21324
21995
|
var DEFAULT_WEB_PORT = 6328;
|
|
21325
21996
|
var DEFAULT_REST_PORT = 8080;
|
|
21326
21997
|
function asValidPort(value) {
|
|
@@ -21329,11 +22000,11 @@ function asValidPort(value) {
|
|
|
21329
22000
|
}
|
|
21330
22001
|
function projectRoot() {
|
|
21331
22002
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
21332
|
-
return
|
|
22003
|
+
return import_node_path76.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
21333
22004
|
}
|
|
21334
22005
|
async function readDaemonPorts(root) {
|
|
21335
22006
|
try {
|
|
21336
|
-
const raw = await
|
|
22007
|
+
const raw = await import_node_fs41.promises.readFile(import_node_path76.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
21337
22008
|
const parsed = JSON.parse(raw);
|
|
21338
22009
|
const ports = parsed?.ports ?? {};
|
|
21339
22010
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -21378,10 +22049,10 @@ function resolveWebPackageDir() {
|
|
|
21378
22049
|
eval("require")
|
|
21379
22050
|
);
|
|
21380
22051
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
21381
|
-
return
|
|
22052
|
+
return import_node_path76.default.dirname(pkgJsonPath);
|
|
21382
22053
|
}
|
|
21383
22054
|
function resolveStandaloneServerEntry(webDir) {
|
|
21384
|
-
return
|
|
22055
|
+
return import_node_path76.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
21385
22056
|
}
|
|
21386
22057
|
async function pickInternalPort() {
|
|
21387
22058
|
return new Promise((resolve, reject) => {
|
|
@@ -21434,7 +22105,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
21434
22105
|
NEAT_API_URL: apiUrl
|
|
21435
22106
|
};
|
|
21436
22107
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
21437
|
-
cwd:
|
|
22108
|
+
cwd: import_node_path76.default.dirname(serverEntry),
|
|
21438
22109
|
env,
|
|
21439
22110
|
stdio: ["ignore", "inherit", "inherit"],
|
|
21440
22111
|
detached: false
|
|
@@ -21561,15 +22232,15 @@ async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
|
|
|
21561
22232
|
}
|
|
21562
22233
|
function isLocalBehind(local, remote) {
|
|
21563
22234
|
if (local === remote) return false;
|
|
21564
|
-
const
|
|
22235
|
+
const parse11 = (v) => {
|
|
21565
22236
|
const core = v.split(/[-+]/)[0] ?? v;
|
|
21566
22237
|
return core.split(".").map((s) => {
|
|
21567
22238
|
const n = Number.parseInt(s, 10);
|
|
21568
22239
|
return Number.isFinite(n) ? n : 0;
|
|
21569
22240
|
});
|
|
21570
22241
|
};
|
|
21571
|
-
const a =
|
|
21572
|
-
const b =
|
|
22242
|
+
const a = parse11(local);
|
|
22243
|
+
const b = parse11(remote);
|
|
21573
22244
|
const len = Math.max(a.length, b.length);
|
|
21574
22245
|
for (let i = 0; i < len; i++) {
|
|
21575
22246
|
const av = a[i] ?? 0;
|
|
@@ -21610,14 +22281,14 @@ function localVersion() {
|
|
|
21610
22281
|
}
|
|
21611
22282
|
function neatHome3() {
|
|
21612
22283
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
21613
|
-
return
|
|
22284
|
+
return import_node_path77.default.resolve(process.env.NEAT_HOME);
|
|
21614
22285
|
}
|
|
21615
22286
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
21616
|
-
return
|
|
22287
|
+
return import_node_path77.default.join(home, ".neat");
|
|
21617
22288
|
}
|
|
21618
22289
|
async function readPid() {
|
|
21619
22290
|
try {
|
|
21620
|
-
const raw = await
|
|
22291
|
+
const raw = await import_node_fs42.promises.readFile(import_node_path77.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
21621
22292
|
const n = Number.parseInt(raw.trim(), 10);
|
|
21622
22293
|
return Number.isFinite(n) ? n : null;
|
|
21623
22294
|
} catch {
|