@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/index.cjs
CHANGED
|
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
|
|
|
60
60
|
]);
|
|
61
61
|
const publicRead = opts.publicRead === true;
|
|
62
62
|
app.addHook("preHandler", (req, reply, done) => {
|
|
63
|
-
const
|
|
64
|
-
if (exactUnauthPaths.has(
|
|
63
|
+
const path76 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
64
|
+
if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
|
|
65
65
|
done();
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
function resolveProtoRoot() {
|
|
196
|
-
const here =
|
|
197
|
-
return
|
|
196
|
+
const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
197
|
+
return import_node_path52.default.resolve(here, "..", "proto");
|
|
198
198
|
}
|
|
199
199
|
function loadTraceService() {
|
|
200
200
|
const protoRoot = resolveProtoRoot();
|
|
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
262
262
|
})
|
|
263
263
|
};
|
|
264
264
|
}
|
|
265
|
-
var import_node_url,
|
|
265
|
+
var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
|
|
266
266
|
var init_otel_grpc = __esm({
|
|
267
267
|
"src/otel-grpc.ts"() {
|
|
268
268
|
"use strict";
|
|
269
269
|
init_cjs_shims();
|
|
270
270
|
import_node_url = require("url");
|
|
271
|
-
|
|
271
|
+
import_node_path52 = __toESM(require("path"), 1);
|
|
272
272
|
import_node_crypto2 = require("crypto");
|
|
273
273
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
274
274
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
414
414
|
const v = attrs[key];
|
|
415
415
|
if (typeof v === "string" && v.length > 0) {
|
|
416
416
|
const q = v.indexOf("?");
|
|
417
|
-
const
|
|
418
|
-
if (
|
|
417
|
+
const path76 = q === -1 ? v : v.slice(0, q);
|
|
418
|
+
if (path76.length > 0) return path76;
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
return void 0;
|
|
@@ -477,10 +477,10 @@ function parseOtlpRequest(body) {
|
|
|
477
477
|
return out;
|
|
478
478
|
}
|
|
479
479
|
function loadProtoRoot() {
|
|
480
|
-
const here =
|
|
481
|
-
const protoRoot =
|
|
480
|
+
const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
481
|
+
const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
|
|
482
482
|
const root = new import_protobufjs.default.Root();
|
|
483
|
-
root.resolvePath = (_origin, target) =>
|
|
483
|
+
root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
|
|
484
484
|
root.loadSync(
|
|
485
485
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
486
486
|
{ keepCase: true }
|
|
@@ -735,12 +735,12 @@ function logSpanHandler(span) {
|
|
|
735
735
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
736
736
|
);
|
|
737
737
|
}
|
|
738
|
-
var
|
|
738
|
+
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;
|
|
739
739
|
var init_otel = __esm({
|
|
740
740
|
"src/otel.ts"() {
|
|
741
741
|
"use strict";
|
|
742
742
|
init_cjs_shims();
|
|
743
|
-
|
|
743
|
+
import_node_path53 = __toESM(require("path"), 1);
|
|
744
744
|
import_node_url2 = require("url");
|
|
745
745
|
import_fastify = __toESM(require("fastify"), 1);
|
|
746
746
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -1333,19 +1333,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1333
1333
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1334
1334
|
let best = { path: [start], edges: [] };
|
|
1335
1335
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1336
|
-
function step(node,
|
|
1337
|
-
if (
|
|
1338
|
-
best = { path: [...
|
|
1336
|
+
function step(node, path76, edges) {
|
|
1337
|
+
if (path76.length > best.path.length) {
|
|
1338
|
+
best = { path: [...path76], edges: [...edges] };
|
|
1339
1339
|
}
|
|
1340
|
-
if (
|
|
1340
|
+
if (path76.length - 1 >= maxDepth) return;
|
|
1341
1341
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1342
1342
|
for (const [srcId, edge] of incoming) {
|
|
1343
1343
|
if (visited.has(srcId)) continue;
|
|
1344
1344
|
visited.add(srcId);
|
|
1345
|
-
|
|
1345
|
+
path76.push(srcId);
|
|
1346
1346
|
edges.push(edge);
|
|
1347
|
-
step(srcId,
|
|
1348
|
-
|
|
1347
|
+
step(srcId, path76, edges);
|
|
1348
|
+
path76.pop();
|
|
1349
1349
|
edges.pop();
|
|
1350
1350
|
visited.delete(srcId);
|
|
1351
1351
|
}
|
|
@@ -1353,11 +1353,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1353
1353
|
step(start, [start], []);
|
|
1354
1354
|
return best;
|
|
1355
1355
|
}
|
|
1356
|
-
function databaseRootCauseShape(graph, origin,
|
|
1356
|
+
function databaseRootCauseShape(graph, origin, walk10) {
|
|
1357
1357
|
const targetDb = origin;
|
|
1358
1358
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1359
1359
|
if (candidatePairs.length === 0) return null;
|
|
1360
|
-
for (const id of
|
|
1360
|
+
for (const id of walk10.path) {
|
|
1361
1361
|
const owner = resolveOwningService(graph, id);
|
|
1362
1362
|
if (!owner) continue;
|
|
1363
1363
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1384,8 +1384,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
|
|
|
1384
1384
|
}
|
|
1385
1385
|
return null;
|
|
1386
1386
|
}
|
|
1387
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1388
|
-
for (const id of
|
|
1387
|
+
function serviceRootCauseShape(graph, _origin, walk10) {
|
|
1388
|
+
for (const id of walk10.path) {
|
|
1389
1389
|
const owner = resolveOwningService(graph, id);
|
|
1390
1390
|
if (!owner) continue;
|
|
1391
1391
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1421,15 +1421,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
1421
1421
|
}
|
|
1422
1422
|
return null;
|
|
1423
1423
|
}
|
|
1424
|
-
function fileRootCauseShape(graph, origin,
|
|
1424
|
+
function fileRootCauseShape(graph, origin, walk10) {
|
|
1425
1425
|
const owner = resolveOwningService(graph, origin.id);
|
|
1426
1426
|
if (!owner) return null;
|
|
1427
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1427
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1428
1428
|
}
|
|
1429
|
-
function symbolRootCauseShape(graph, origin,
|
|
1429
|
+
function symbolRootCauseShape(graph, origin, walk10) {
|
|
1430
1430
|
const owner = resolveOwningService(graph, origin.id);
|
|
1431
1431
|
if (!owner) return null;
|
|
1432
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1432
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1433
1433
|
}
|
|
1434
1434
|
var rootCauseShapes = {
|
|
1435
1435
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1442,25 +1442,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1442
1442
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1443
1443
|
const shape = rootCauseShapes[origin.type];
|
|
1444
1444
|
if (shape) {
|
|
1445
|
-
const
|
|
1446
|
-
const match = shape(graph, origin,
|
|
1445
|
+
const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1446
|
+
const match = shape(graph, origin, walk10);
|
|
1447
1447
|
if (match) {
|
|
1448
1448
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1449
|
-
return
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1449
|
+
return {
|
|
1450
|
+
source: "compat",
|
|
1451
|
+
result: import_types.RootCauseResultSchema.parse({
|
|
1452
|
+
rootCauseNode: match.rootCauseNode,
|
|
1453
|
+
rootCauseReason: reason,
|
|
1454
|
+
traversalPath: walk10.path,
|
|
1455
|
+
edgeProvenances: walk10.edges.map((e) => e.provenance),
|
|
1456
|
+
confidence: confidenceFromMix(walk10.edges),
|
|
1457
|
+
fixRecommendation: match.fixRecommendation
|
|
1458
|
+
})
|
|
1459
|
+
};
|
|
1457
1460
|
}
|
|
1458
1461
|
}
|
|
1459
1462
|
if (origin.type === import_types.NodeType.ServiceNode) {
|
|
1460
1463
|
const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
|
|
1461
|
-
if (crossService) return crossService;
|
|
1464
|
+
if (crossService) return { result: crossService, source: "cross-service" };
|
|
1462
1465
|
}
|
|
1463
|
-
|
|
1466
|
+
const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
|
|
1467
|
+
return incident ? { result: incident, source: "incident" } : null;
|
|
1464
1468
|
}
|
|
1465
1469
|
var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
|
|
1466
1470
|
function incidentMatchesNode(ev, nodeId) {
|
|
@@ -1556,26 +1560,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
|
|
|
1556
1560
|
return best;
|
|
1557
1561
|
}
|
|
1558
1562
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1559
|
-
const
|
|
1563
|
+
const path76 = [originServiceId];
|
|
1560
1564
|
const edges = [];
|
|
1561
1565
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1562
1566
|
let current = originServiceId;
|
|
1563
1567
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1564
1568
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1565
1569
|
if (!hop) break;
|
|
1566
|
-
|
|
1570
|
+
path76.push(hop.nextService);
|
|
1567
1571
|
edges.push(hop.edge);
|
|
1568
1572
|
visited.add(hop.nextService);
|
|
1569
1573
|
current = hop.nextService;
|
|
1570
1574
|
}
|
|
1571
1575
|
if (edges.length === 0) return null;
|
|
1572
|
-
return { path:
|
|
1576
|
+
return { path: path76, edges, culprit: current };
|
|
1577
|
+
}
|
|
1578
|
+
function isStaleCallEdge(e) {
|
|
1579
|
+
return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
|
|
1580
|
+
}
|
|
1581
|
+
function staleCallDominates(e, id, curEdge, curId) {
|
|
1582
|
+
const ev = e.signal?.spanCount ?? e.callCount ?? 0;
|
|
1583
|
+
const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
|
|
1584
|
+
if (ev !== cv) return ev > cv;
|
|
1585
|
+
return id < curId;
|
|
1586
|
+
}
|
|
1587
|
+
function dominantStaleCall(graph, serviceId15, visited) {
|
|
1588
|
+
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1589
|
+
for (const src of callSourcesForService(graph, serviceId15)) {
|
|
1590
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
1591
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
1592
|
+
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
1593
|
+
if (isFrontierNode(graph, e.target)) continue;
|
|
1594
|
+
const owner = resolveOwningService(graph, e.target);
|
|
1595
|
+
if (!owner || visited.has(owner.id)) continue;
|
|
1596
|
+
const cur = bestByCallee.get(owner.id);
|
|
1597
|
+
if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
|
|
1598
|
+
bestByCallee.set(owner.id, e);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
let best = null;
|
|
1603
|
+
for (const [id, edge] of bestByCallee) {
|
|
1604
|
+
if (!isStaleCallEdge(edge)) continue;
|
|
1605
|
+
if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
|
|
1606
|
+
best = { nextService: id, edge };
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return best;
|
|
1610
|
+
}
|
|
1611
|
+
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
1612
|
+
const path76 = [originServiceId];
|
|
1613
|
+
const edges = [];
|
|
1614
|
+
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1615
|
+
let current = originServiceId;
|
|
1616
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1617
|
+
const hop = dominantStaleCall(graph, current, visited);
|
|
1618
|
+
if (!hop) break;
|
|
1619
|
+
path76.push(hop.nextService);
|
|
1620
|
+
edges.push(hop.edge);
|
|
1621
|
+
visited.add(hop.nextService);
|
|
1622
|
+
current = hop.nextService;
|
|
1623
|
+
}
|
|
1624
|
+
if (edges.length === 0) return null;
|
|
1625
|
+
return { path: path76, edges, culprit: current };
|
|
1573
1626
|
}
|
|
1574
1627
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1575
1628
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1576
1629
|
if (!chain) return null;
|
|
1577
1630
|
const culprit = chain.culprit;
|
|
1578
|
-
const
|
|
1631
|
+
const path76 = [...chain.path];
|
|
1579
1632
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1580
1633
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1581
1634
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1583,14 +1636,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1583
1636
|
if (loc) {
|
|
1584
1637
|
let rootCauseNode = culprit;
|
|
1585
1638
|
if (loc.fileNode) {
|
|
1586
|
-
|
|
1639
|
+
path76.push(loc.fileNode);
|
|
1587
1640
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1588
1641
|
rootCauseNode = loc.fileNode;
|
|
1589
1642
|
}
|
|
1590
1643
|
return import_types.RootCauseResultSchema.parse({
|
|
1591
1644
|
rootCauseNode,
|
|
1592
1645
|
rootCauseReason: loc.rootCauseReason,
|
|
1593
|
-
traversalPath:
|
|
1646
|
+
traversalPath: path76,
|
|
1594
1647
|
edgeProvenances,
|
|
1595
1648
|
confidence,
|
|
1596
1649
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1602,7 +1655,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1602
1655
|
return import_types.RootCauseResultSchema.parse({
|
|
1603
1656
|
rootCauseNode: culprit,
|
|
1604
1657
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1605
|
-
traversalPath:
|
|
1658
|
+
traversalPath: path76,
|
|
1606
1659
|
edgeProvenances,
|
|
1607
1660
|
confidence,
|
|
1608
1661
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -1989,17 +2042,20 @@ function displayNameOf(nodeId) {
|
|
|
1989
2042
|
return nodeId.replace(/^[a-z]+:/, "");
|
|
1990
2043
|
}
|
|
1991
2044
|
function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
|
|
1992
|
-
const
|
|
1993
|
-
if (!
|
|
2045
|
+
const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
|
|
2046
|
+
if (!tagged) return null;
|
|
1994
2047
|
const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
|
|
1995
|
-
if (!navigation) return
|
|
1996
|
-
return enrichWithNavigation(graph, errorNodeId,
|
|
2048
|
+
if (!navigation) return tagged.result;
|
|
2049
|
+
return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
|
|
1997
2050
|
}
|
|
1998
|
-
function enrichWithNavigation(graph, errorNodeId,
|
|
2051
|
+
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
2052
|
+
const legacy = tagged.result;
|
|
1999
2053
|
const seedNode = legacy.rootCauseNode;
|
|
2000
2054
|
const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
|
|
2001
2055
|
const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
|
|
2002
2056
|
const candidates = [];
|
|
2057
|
+
const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
|
|
2058
|
+
const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
|
|
2003
2059
|
if (seedCtx && isVictimSeed(seedCtx)) {
|
|
2004
2060
|
const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
|
|
2005
2061
|
const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
|
|
@@ -2024,6 +2080,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
2024
2080
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
2025
2081
|
...lastProv ? { provenance: lastProv } : {}
|
|
2026
2082
|
});
|
|
2083
|
+
} else if (staleChain) {
|
|
2084
|
+
const culprit = staleChain.culprit;
|
|
2085
|
+
const culpritName = displayNameOf(culprit);
|
|
2086
|
+
const seedName = displayNameOf(seedNode);
|
|
2087
|
+
const staleConfidence = confidenceFromMix(staleChain.edges, now);
|
|
2088
|
+
candidates.push({
|
|
2089
|
+
node: culprit,
|
|
2090
|
+
classification: "primary-failure",
|
|
2091
|
+
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.`,
|
|
2092
|
+
context: nodeContext(graph, culprit, incidents, now),
|
|
2093
|
+
confidence: staleConfidence,
|
|
2094
|
+
provenance: import_types.Provenance.STALE
|
|
2095
|
+
});
|
|
2096
|
+
candidates.push({
|
|
2097
|
+
node: seedNode,
|
|
2098
|
+
classification: "symptom-only",
|
|
2099
|
+
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.`,
|
|
2100
|
+
context: seedCtx ?? EMPTY_CONTEXT,
|
|
2101
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
|
|
2102
|
+
...lastProv ? { provenance: lastProv } : {}
|
|
2103
|
+
});
|
|
2027
2104
|
} else {
|
|
2028
2105
|
candidates.push({
|
|
2029
2106
|
node: seedNode,
|
|
@@ -2037,11 +2114,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
2037
2114
|
const top = candidates[0];
|
|
2038
2115
|
let traversalPath = legacy.traversalPath;
|
|
2039
2116
|
let edgeProvenances = legacy.edgeProvenances;
|
|
2040
|
-
if (top.node
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2117
|
+
if (staleChain && top.node === staleChain.culprit) {
|
|
2118
|
+
traversalPath = staleChain.path;
|
|
2119
|
+
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
2120
|
+
} else if (top.node !== seedNode) {
|
|
2121
|
+
const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
2122
|
+
if (path76) {
|
|
2123
|
+
traversalPath = path76.nodes;
|
|
2124
|
+
edgeProvenances = path76.edges.map((e) => e.provenance);
|
|
2045
2125
|
} else {
|
|
2046
2126
|
traversalPath = [errorNodeId, top.node];
|
|
2047
2127
|
edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
|
|
@@ -2063,6 +2143,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
|
|
|
2063
2143
|
return legacy.fixRecommendation;
|
|
2064
2144
|
}
|
|
2065
2145
|
const name = top.node.replace(/^service:/, "");
|
|
2146
|
+
if (top.provenance === import_types.Provenance.STALE) {
|
|
2147
|
+
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}.`;
|
|
2148
|
+
}
|
|
2066
2149
|
if (top.classification === "primary-failure") {
|
|
2067
2150
|
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.`;
|
|
2068
2151
|
}
|
|
@@ -2581,6 +2664,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
|
2581
2664
|
var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
|
|
2582
2665
|
var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
|
|
2583
2666
|
var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
|
|
2667
|
+
var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
|
|
2584
2668
|
var import_types6 = require("@neat.is/types");
|
|
2585
2669
|
|
|
2586
2670
|
// src/extract/shared.ts
|
|
@@ -2861,7 +2945,7 @@ function buildServiceHostIndex(services) {
|
|
|
2861
2945
|
async function walkSourceFiles(dir, excludeDirs = []) {
|
|
2862
2946
|
const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
|
|
2863
2947
|
const out = [];
|
|
2864
|
-
async function
|
|
2948
|
+
async function walk10(current) {
|
|
2865
2949
|
const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2866
2950
|
for (const entry of entries) {
|
|
2867
2951
|
const full = import_node_path5.default.join(current, entry.name);
|
|
@@ -2869,7 +2953,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2869
2953
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2870
2954
|
if (excluded.has(import_node_path5.default.resolve(full))) continue;
|
|
2871
2955
|
if (await isPythonVenvDir(full)) continue;
|
|
2872
|
-
await
|
|
2956
|
+
await walk10(full);
|
|
2873
2957
|
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
2874
2958
|
// would attribute our instrumentation imports to the user's service.
|
|
2875
2959
|
!isNeatAuthoredSourceFile(entry.name)) {
|
|
@@ -2877,7 +2961,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2877
2961
|
}
|
|
2878
2962
|
}
|
|
2879
2963
|
}
|
|
2880
|
-
await
|
|
2964
|
+
await walk10(dir);
|
|
2881
2965
|
return out;
|
|
2882
2966
|
}
|
|
2883
2967
|
async function loadSourceFiles(dir, excludeDirs = []) {
|
|
@@ -3397,6 +3481,11 @@ function makePhpParser() {
|
|
|
3397
3481
|
p.setLanguage(import_tree_sitter_php.default.php_only);
|
|
3398
3482
|
return p;
|
|
3399
3483
|
}
|
|
3484
|
+
function makeRustParser() {
|
|
3485
|
+
const p = new import_tree_sitter2.default();
|
|
3486
|
+
p.setLanguage(import_tree_sitter_rust.default);
|
|
3487
|
+
return p;
|
|
3488
|
+
}
|
|
3400
3489
|
var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
3401
3490
|
"get",
|
|
3402
3491
|
"post",
|
|
@@ -3468,8 +3557,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3468
3557
|
chiWalk(tree.rootNode, "", out);
|
|
3469
3558
|
return out;
|
|
3470
3559
|
}
|
|
3471
|
-
function stripChiRegex(
|
|
3472
|
-
return
|
|
3560
|
+
function stripChiRegex(path76) {
|
|
3561
|
+
return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3473
3562
|
}
|
|
3474
3563
|
function chiWalk(node, prefix, out) {
|
|
3475
3564
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -4141,9 +4230,9 @@ function rubyRocketRoute(args) {
|
|
|
4141
4230
|
if (!pair || pair.type !== "pair") continue;
|
|
4142
4231
|
const k = pair.childForFieldName("key");
|
|
4143
4232
|
if (k?.type !== "string") continue;
|
|
4144
|
-
const
|
|
4145
|
-
if (
|
|
4146
|
-
return { path:
|
|
4233
|
+
const path76 = rubyLiteral(k);
|
|
4234
|
+
if (path76 === null) continue;
|
|
4235
|
+
return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
4147
4236
|
}
|
|
4148
4237
|
return null;
|
|
4149
4238
|
}
|
|
@@ -4313,6 +4402,48 @@ function railsRoutesFromSource(source, parser) {
|
|
|
4313
4402
|
});
|
|
4314
4403
|
return out;
|
|
4315
4404
|
}
|
|
4405
|
+
var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4406
|
+
function sinatraRoutesFromSource(source, parser) {
|
|
4407
|
+
const tree = parseSource2(parser, source);
|
|
4408
|
+
if (!fileReferencesSinatra(tree.rootNode)) return [];
|
|
4409
|
+
const out = [];
|
|
4410
|
+
walk(tree.rootNode, (node) => {
|
|
4411
|
+
if (node.type !== "call") return;
|
|
4412
|
+
if (node.childForFieldName("receiver")) return;
|
|
4413
|
+
const method = node.childForFieldName("method")?.text;
|
|
4414
|
+
if (!method || !SINATRA_VERBS.has(method)) return;
|
|
4415
|
+
if (!node.childForFieldName("block")) return;
|
|
4416
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
4417
|
+
if (first?.type !== "string") return;
|
|
4418
|
+
const p = rubyLiteral(first);
|
|
4419
|
+
if (p === null || !p.startsWith("/")) return;
|
|
4420
|
+
out.push({
|
|
4421
|
+
method: method.toUpperCase(),
|
|
4422
|
+
pathTemplate: canonicalizeTemplate(p),
|
|
4423
|
+
line: node.startPosition.row + 1,
|
|
4424
|
+
framework: "sinatra"
|
|
4425
|
+
});
|
|
4426
|
+
});
|
|
4427
|
+
return out;
|
|
4428
|
+
}
|
|
4429
|
+
function fileReferencesSinatra(root) {
|
|
4430
|
+
let found = false;
|
|
4431
|
+
walk(root, (node) => {
|
|
4432
|
+
if (found) return;
|
|
4433
|
+
if (node.type === "call") {
|
|
4434
|
+
const m = node.childForFieldName("method")?.text;
|
|
4435
|
+
if (m === "require" || m === "require_relative") {
|
|
4436
|
+
const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
4437
|
+
if (s !== null && /^sinatra\b/.test(s)) found = true;
|
|
4438
|
+
}
|
|
4439
|
+
return;
|
|
4440
|
+
}
|
|
4441
|
+
if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
|
|
4442
|
+
found = true;
|
|
4443
|
+
}
|
|
4444
|
+
});
|
|
4445
|
+
return found;
|
|
4446
|
+
}
|
|
4316
4447
|
var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
|
|
4317
4448
|
var LARAVEL_RESOURCE_ROWS = [
|
|
4318
4449
|
{ action: "index", methods: ["GET"], suffix: "" },
|
|
@@ -4488,6 +4619,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
|
|
|
4488
4619
|
}
|
|
4489
4620
|
return out;
|
|
4490
4621
|
}
|
|
4622
|
+
var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4623
|
+
function isSlimAppCtor(node) {
|
|
4624
|
+
if (!node) return false;
|
|
4625
|
+
if (node.type === "scoped_call_expression") {
|
|
4626
|
+
const scope = node.childForFieldName("scope")?.text ?? "";
|
|
4627
|
+
const name = node.childForFieldName("name")?.text;
|
|
4628
|
+
return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
|
|
4629
|
+
}
|
|
4630
|
+
if (node.type === "object_creation_expression") {
|
|
4631
|
+
const cls = node.namedChild(0)?.text ?? "";
|
|
4632
|
+
return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
|
|
4633
|
+
}
|
|
4634
|
+
return false;
|
|
4635
|
+
}
|
|
4636
|
+
function isSlimAppType(node) {
|
|
4637
|
+
if (!node || node.type !== "named_type") return false;
|
|
4638
|
+
const text = node.text;
|
|
4639
|
+
return text === "App" || text.endsWith("\\App");
|
|
4640
|
+
}
|
|
4641
|
+
function collectSlimAppVars(root) {
|
|
4642
|
+
const vars = /* @__PURE__ */ new Set();
|
|
4643
|
+
walk(root, (node) => {
|
|
4644
|
+
if (node.type === "assignment_expression") {
|
|
4645
|
+
const left = node.childForFieldName("left");
|
|
4646
|
+
if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
|
|
4647
|
+
vars.add(left.text);
|
|
4648
|
+
}
|
|
4649
|
+
return;
|
|
4650
|
+
}
|
|
4651
|
+
if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
|
|
4652
|
+
const name = node.childForFieldName("name");
|
|
4653
|
+
if (name?.type === "variable_name") vars.add(name.text);
|
|
4654
|
+
}
|
|
4655
|
+
});
|
|
4656
|
+
return vars;
|
|
4657
|
+
}
|
|
4658
|
+
function slimClosureParamVars(closure) {
|
|
4659
|
+
const out = ["$this"];
|
|
4660
|
+
for (let i = 0; i < closure.namedChildCount; i++) {
|
|
4661
|
+
const params = closure.namedChild(i);
|
|
4662
|
+
if (params?.type !== "formal_parameters") continue;
|
|
4663
|
+
for (let j = 0; j < params.namedChildCount; j++) {
|
|
4664
|
+
const param = params.namedChild(j);
|
|
4665
|
+
if (param?.type !== "simple_parameter") continue;
|
|
4666
|
+
for (let k = 0; k < param.namedChildCount; k++) {
|
|
4667
|
+
const v = param.namedChild(k);
|
|
4668
|
+
if (v?.type === "variable_name") {
|
|
4669
|
+
out.push(v.text);
|
|
4670
|
+
break;
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
return out;
|
|
4676
|
+
}
|
|
4677
|
+
function phpStringArray(node) {
|
|
4678
|
+
const out = [];
|
|
4679
|
+
if (node?.type !== "array_creation_expression") return out;
|
|
4680
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4681
|
+
const el = node.namedChild(i);
|
|
4682
|
+
if (el?.type !== "array_element_initializer") continue;
|
|
4683
|
+
const s = phpStaticString(el.namedChild(0));
|
|
4684
|
+
if (s !== null) out.push(s);
|
|
4685
|
+
}
|
|
4686
|
+
return out;
|
|
4687
|
+
}
|
|
4688
|
+
function slimRoutesFromSource(source, parser) {
|
|
4689
|
+
const tree = parseSource2(parser, source);
|
|
4690
|
+
const appVars = collectSlimAppVars(tree.rootNode);
|
|
4691
|
+
if (appVars.size === 0) return [];
|
|
4692
|
+
const out = [];
|
|
4693
|
+
slimWalk(tree.rootNode, "", appVars, out);
|
|
4694
|
+
return out;
|
|
4695
|
+
}
|
|
4696
|
+
function slimWalk(node, prefix, appVars, out) {
|
|
4697
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4698
|
+
const child = node.namedChild(i);
|
|
4699
|
+
if (child) slimHandle(child, prefix, appVars, out);
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
function slimHandle(node, prefix, appVars, out) {
|
|
4703
|
+
if (node.type === "member_call_expression") {
|
|
4704
|
+
const obj = node.childForFieldName("object");
|
|
4705
|
+
const method = node.childForFieldName("name")?.text;
|
|
4706
|
+
const args = node.childForFieldName("arguments");
|
|
4707
|
+
if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
|
|
4708
|
+
const line = node.startPosition.row + 1;
|
|
4709
|
+
if (method === "group") {
|
|
4710
|
+
const groupPrefix = phpFirstString(args);
|
|
4711
|
+
const closure = laravelGroupClosure(args);
|
|
4712
|
+
if (groupPrefix !== null && closure) {
|
|
4713
|
+
const inner = new Set(appVars);
|
|
4714
|
+
for (const v of slimClosureParamVars(closure)) inner.add(v);
|
|
4715
|
+
const body = closure.childForFieldName("body");
|
|
4716
|
+
if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
|
|
4717
|
+
}
|
|
4718
|
+
return;
|
|
4719
|
+
}
|
|
4720
|
+
if (SLIM_VERBS.has(method)) {
|
|
4721
|
+
const p = phpFirstString(args);
|
|
4722
|
+
if (p !== null) {
|
|
4723
|
+
out.push({
|
|
4724
|
+
method: method.toUpperCase(),
|
|
4725
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4726
|
+
line,
|
|
4727
|
+
framework: "slim"
|
|
4728
|
+
});
|
|
4729
|
+
}
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4732
|
+
if (method === "any") {
|
|
4733
|
+
const p = phpFirstString(args);
|
|
4734
|
+
if (p !== null) {
|
|
4735
|
+
out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
|
|
4736
|
+
}
|
|
4737
|
+
return;
|
|
4738
|
+
}
|
|
4739
|
+
if (method === "map") {
|
|
4740
|
+
const vals = phpArgumentValues(args);
|
|
4741
|
+
const methods = phpStringArray(vals[0]);
|
|
4742
|
+
const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
|
|
4743
|
+
if (p !== null) {
|
|
4744
|
+
for (const m of methods) {
|
|
4745
|
+
out.push({
|
|
4746
|
+
method: m.toUpperCase(),
|
|
4747
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4748
|
+
line,
|
|
4749
|
+
framework: "slim"
|
|
4750
|
+
});
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
slimWalk(node, prefix, appVars, out);
|
|
4758
|
+
}
|
|
4759
|
+
var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
|
|
4760
|
+
function rustStringContent(node) {
|
|
4761
|
+
if (!node || node.type !== "string_literal") return null;
|
|
4762
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4763
|
+
if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
|
|
4764
|
+
}
|
|
4765
|
+
return "";
|
|
4766
|
+
}
|
|
4767
|
+
function actixRoutesFromSource(source, parser) {
|
|
4768
|
+
const tree = parseSource2(parser, source);
|
|
4769
|
+
const out = [];
|
|
4770
|
+
walk(tree.rootNode, (node) => {
|
|
4771
|
+
if (node.type === "attribute_item") {
|
|
4772
|
+
actixAttributeRoute(node, out);
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
if (node.type === "call_expression") {
|
|
4776
|
+
actixBuilderRoute(node, out);
|
|
4777
|
+
}
|
|
4778
|
+
});
|
|
4779
|
+
return out;
|
|
4780
|
+
}
|
|
4781
|
+
function actixAttributeRoute(attrItem, out) {
|
|
4782
|
+
const attr = attrItem.namedChild(0);
|
|
4783
|
+
if (!attr || attr.type !== "attribute") return;
|
|
4784
|
+
const nameNode = attr.namedChild(0);
|
|
4785
|
+
if (!nameNode) return;
|
|
4786
|
+
const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
|
|
4787
|
+
if (!macro) return;
|
|
4788
|
+
const tokens = attr.childForFieldName("arguments");
|
|
4789
|
+
if (!tokens || tokens.type !== "token_tree") return;
|
|
4790
|
+
const strings = [];
|
|
4791
|
+
for (let i = 0; i < tokens.namedChildCount; i++) {
|
|
4792
|
+
const s = rustStringContent(tokens.namedChild(i));
|
|
4793
|
+
if (s !== null) strings.push(s);
|
|
4794
|
+
}
|
|
4795
|
+
const pathStr = strings[0];
|
|
4796
|
+
if (pathStr === void 0 || !pathStr.startsWith("/")) return;
|
|
4797
|
+
const line = attrItem.startPosition.row + 1;
|
|
4798
|
+
const template = canonicalizeTemplate(pathStr);
|
|
4799
|
+
if (ACTIX_METHODS.has(macro)) {
|
|
4800
|
+
out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
|
|
4801
|
+
return;
|
|
4802
|
+
}
|
|
4803
|
+
if (macro === "route") {
|
|
4804
|
+
const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
|
|
4805
|
+
const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
|
|
4806
|
+
for (const m of list) {
|
|
4807
|
+
out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
function actixBuilderRoute(call, out) {
|
|
4812
|
+
const fn = call.childForFieldName("function");
|
|
4813
|
+
if (fn?.type !== "field_expression") return;
|
|
4814
|
+
if (fn.childForFieldName("field")?.text !== "route") return;
|
|
4815
|
+
const args = call.childForFieldName("arguments");
|
|
4816
|
+
const pathStr = rustStringContent(args?.namedChild(0));
|
|
4817
|
+
if (pathStr === null || !pathStr.startsWith("/")) return;
|
|
4818
|
+
const second = args?.namedChild(1);
|
|
4819
|
+
if (!second) return;
|
|
4820
|
+
const method = actixBuilderMethod(second);
|
|
4821
|
+
if (!method) return;
|
|
4822
|
+
out.push({
|
|
4823
|
+
method,
|
|
4824
|
+
pathTemplate: canonicalizeTemplate(pathStr),
|
|
4825
|
+
line: call.startPosition.row + 1,
|
|
4826
|
+
framework: "actix-web"
|
|
4827
|
+
});
|
|
4828
|
+
}
|
|
4829
|
+
function actixBuilderMethod(node) {
|
|
4830
|
+
let method = null;
|
|
4831
|
+
walk(node, (n) => {
|
|
4832
|
+
if (method || n.type !== "scoped_identifier") return;
|
|
4833
|
+
const verb = n.childForFieldName("name")?.text;
|
|
4834
|
+
const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
|
|
4835
|
+
if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
|
|
4836
|
+
});
|
|
4837
|
+
return method;
|
|
4838
|
+
}
|
|
4491
4839
|
function namedArgs(argsNode) {
|
|
4492
4840
|
const out = [];
|
|
4493
4841
|
if (!argsNode) return out;
|
|
@@ -4804,6 +5152,7 @@ async function addRoutes(graph, services) {
|
|
|
4804
5152
|
const goParser = makeGoParser2();
|
|
4805
5153
|
const rubyParser = makeRubyParser();
|
|
4806
5154
|
const phpParser = makePhpParser();
|
|
5155
|
+
const rustParser = makeRustParser();
|
|
4807
5156
|
let nodesAdded = 0;
|
|
4808
5157
|
let edgesAdded = 0;
|
|
4809
5158
|
for (const service of services) {
|
|
@@ -4826,7 +5175,10 @@ async function addRoutes(graph, services) {
|
|
|
4826
5175
|
const isGoService = service.node.language === "go";
|
|
4827
5176
|
const hasRails = deps["rails"] !== void 0;
|
|
4828
5177
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4829
|
-
|
|
5178
|
+
const hasSlim = deps["slim/slim"] !== void 0;
|
|
5179
|
+
const hasSinatra = deps["sinatra"] !== void 0;
|
|
5180
|
+
const hasActix = deps["actix-web"] !== void 0;
|
|
5181
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
|
|
4830
5182
|
continue;
|
|
4831
5183
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
4832
5184
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4837,7 +5189,8 @@ async function addRoutes(graph, services) {
|
|
|
4837
5189
|
const isGo = ext === ".go";
|
|
4838
5190
|
const isRb = ext === ".rb";
|
|
4839
5191
|
const isPhp = ext === ".php";
|
|
4840
|
-
|
|
5192
|
+
const isRs = ext === ".rs";
|
|
5193
|
+
if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
|
|
4841
5194
|
const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
|
|
4842
5195
|
let routes;
|
|
4843
5196
|
try {
|
|
@@ -4847,8 +5200,12 @@ async function addRoutes(graph, services) {
|
|
|
4847
5200
|
phpParser,
|
|
4848
5201
|
relFile === "routes/api.php" ? "/api" : ""
|
|
4849
5202
|
) : [];
|
|
5203
|
+
if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
|
|
4850
5204
|
} else if (isRb) {
|
|
4851
5205
|
routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
|
|
5206
|
+
if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
|
|
5207
|
+
} else if (isRs) {
|
|
5208
|
+
routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
|
|
4852
5209
|
} else if (isGo) {
|
|
4853
5210
|
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4854
5211
|
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
@@ -5083,6 +5440,37 @@ function loadIncidentThresholdsFromEnv() {
|
|
|
5083
5440
|
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
5084
5441
|
}
|
|
5085
5442
|
}
|
|
5443
|
+
var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
|
|
5444
|
+
function latencyStreamCeilingMs() {
|
|
5445
|
+
const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
|
|
5446
|
+
if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5447
|
+
const n = Number(raw);
|
|
5448
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
5449
|
+
console.warn(
|
|
5450
|
+
`[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
|
|
5451
|
+
);
|
|
5452
|
+
return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5453
|
+
}
|
|
5454
|
+
function spanServesEventStream(attrs) {
|
|
5455
|
+
for (const key of [
|
|
5456
|
+
"http.response.header.content-type",
|
|
5457
|
+
"http.response.header.content_type"
|
|
5458
|
+
]) {
|
|
5459
|
+
const v = attrs[key];
|
|
5460
|
+
const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
|
|
5461
|
+
for (const item of values) {
|
|
5462
|
+
if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
|
|
5463
|
+
return true;
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
return false;
|
|
5468
|
+
}
|
|
5469
|
+
function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
|
|
5470
|
+
if (span.websocketChannel !== void 0) return true;
|
|
5471
|
+
if (spanServesEventStream(span.attributes)) return true;
|
|
5472
|
+
return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
|
|
5473
|
+
}
|
|
5086
5474
|
function httpResponseStatusFromAttrs(attrs) {
|
|
5087
5475
|
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
5088
5476
|
const v = attrs[key];
|
|
@@ -5975,6 +6363,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
|
5975
6363
|
);
|
|
5976
6364
|
ctx.burstState.delete(key);
|
|
5977
6365
|
}
|
|
6366
|
+
var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
|
|
6367
|
+
function nextApiRouteTemplate(span) {
|
|
6368
|
+
const raw = pickAttr(span, "next.span_name") ?? span.name;
|
|
6369
|
+
const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
|
|
6370
|
+
return match ? match[1] : void 0;
|
|
6371
|
+
}
|
|
5978
6372
|
function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
|
|
5979
6373
|
const target = normalizePathTemplate(httpRoute);
|
|
5980
6374
|
const m = method?.toUpperCase();
|
|
@@ -5997,7 +6391,7 @@ async function handleSpan(ctx, span) {
|
|
|
5997
6391
|
}
|
|
5998
6392
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
5999
6393
|
const isError = span.statusCode === 2;
|
|
6000
|
-
const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6394
|
+
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6001
6395
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
6002
6396
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
6003
6397
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -6194,12 +6588,13 @@ async function handleSpan(ctx, span) {
|
|
|
6194
6588
|
}
|
|
6195
6589
|
}
|
|
6196
6590
|
}
|
|
6197
|
-
|
|
6591
|
+
const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
|
|
6592
|
+
if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
|
|
6198
6593
|
const routeNodeId = findRouteNodeByHttpRoute(
|
|
6199
6594
|
ctx.graph,
|
|
6200
6595
|
span.service,
|
|
6201
6596
|
span.httpMethod,
|
|
6202
|
-
|
|
6597
|
+
fusionRoute
|
|
6203
6598
|
);
|
|
6204
6599
|
if (routeNodeId) {
|
|
6205
6600
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
@@ -7590,7 +7985,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
|
|
|
7590
7985
|
var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
7591
7986
|
var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
|
|
7592
7987
|
var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
|
|
7593
|
-
var
|
|
7988
|
+
var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
|
|
7594
7989
|
var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
|
|
7595
7990
|
var import_types20 = require("@neat.is/types");
|
|
7596
7991
|
var PARSE_CHUNK3 = 16384;
|
|
@@ -7611,7 +8006,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
|
|
|
7611
8006
|
".cs": import_tree_sitter_c_sharp.default,
|
|
7612
8007
|
".java": import_tree_sitter_java.default,
|
|
7613
8008
|
".kt": import_tree_sitter_kotlin.default,
|
|
7614
|
-
".rs":
|
|
8009
|
+
".rs": import_tree_sitter_rust2.default,
|
|
7615
8010
|
// C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
|
|
7616
8011
|
// `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
|
|
7617
8012
|
// headers. `.h` and `.c` are deliberately absent: they are shared with C (a
|
|
@@ -8051,15 +8446,15 @@ function collectKotlinSymbolDefs(root) {
|
|
|
8051
8446
|
});
|
|
8052
8447
|
};
|
|
8053
8448
|
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
8054
|
-
const
|
|
8449
|
+
const firstChildOfType2 = (node, types) => {
|
|
8055
8450
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8056
8451
|
const child = node.namedChild(i);
|
|
8057
8452
|
if (child && types.includes(child.type)) return child;
|
|
8058
8453
|
}
|
|
8059
8454
|
return void 0;
|
|
8060
8455
|
};
|
|
8061
|
-
const nameOf = (node, ...types) =>
|
|
8062
|
-
const bodyOf = (node) =>
|
|
8456
|
+
const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
|
|
8457
|
+
const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
|
|
8063
8458
|
let pkg;
|
|
8064
8459
|
for (let i = 0; i < root.namedChildCount; i++) {
|
|
8065
8460
|
const child = root.namedChild(i);
|
|
@@ -8560,7 +8955,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
8560
8955
|
return best;
|
|
8561
8956
|
};
|
|
8562
8957
|
const requests = [];
|
|
8563
|
-
const
|
|
8958
|
+
const walk10 = (node) => {
|
|
8564
8959
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
8565
8960
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
8566
8961
|
if (self && self.kind === "class") {
|
|
@@ -8606,10 +9001,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
8606
9001
|
}
|
|
8607
9002
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8608
9003
|
const child = node.namedChild(i);
|
|
8609
|
-
if (child)
|
|
9004
|
+
if (child) walk10(child);
|
|
8610
9005
|
}
|
|
8611
9006
|
};
|
|
8612
|
-
|
|
9007
|
+
walk10(root);
|
|
8613
9008
|
for (const req of requests) {
|
|
8614
9009
|
const targetSid = resolveTarget(req.targetName, req.wantKind);
|
|
8615
9010
|
if (!targetSid) continue;
|
|
@@ -8905,7 +9300,7 @@ async function addServerActions(graph, services) {
|
|
|
8905
9300
|
|
|
8906
9301
|
// src/extract/databases/index.ts
|
|
8907
9302
|
init_cjs_shims();
|
|
8908
|
-
var
|
|
9303
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
8909
9304
|
var import_types23 = require("@neat.is/types");
|
|
8910
9305
|
|
|
8911
9306
|
// src/extract/databases/db-config-yaml.ts
|
|
@@ -9340,9 +9735,163 @@ async function parse8(serviceDir) {
|
|
|
9340
9735
|
}
|
|
9341
9736
|
var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
9342
9737
|
|
|
9343
|
-
// src/extract/databases/
|
|
9738
|
+
// src/extract/databases/csharp.ts
|
|
9344
9739
|
init_cjs_shims();
|
|
9740
|
+
var import_node_fs22 = require("fs");
|
|
9345
9741
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
9742
|
+
var CS_EXT = ".cs";
|
|
9743
|
+
var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
|
|
9744
|
+
var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
|
|
9745
|
+
var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
|
|
9746
|
+
var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
9747
|
+
function hostIsUnresolved(host) {
|
|
9748
|
+
return host === "" || /[${}]/.test(host);
|
|
9749
|
+
}
|
|
9750
|
+
function looksLikePostgres(s) {
|
|
9751
|
+
return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
|
|
9752
|
+
}
|
|
9753
|
+
function looksLikeRedis(s) {
|
|
9754
|
+
return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
|
|
9755
|
+
}
|
|
9756
|
+
function parsePostgresConnection(raw) {
|
|
9757
|
+
const s = raw.trim();
|
|
9758
|
+
if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9759
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9760
|
+
for (const part of s.split(";")) {
|
|
9761
|
+
const eq = part.indexOf("=");
|
|
9762
|
+
if (eq < 0) continue;
|
|
9763
|
+
const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
|
|
9764
|
+
const value = part.slice(eq + 1).trim();
|
|
9765
|
+
if (value && !fields.has(key)) fields.set(key, value);
|
|
9766
|
+
}
|
|
9767
|
+
const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
|
|
9768
|
+
if (!hostRaw) return null;
|
|
9769
|
+
const host = hostRaw.split(",")[0].trim();
|
|
9770
|
+
if (hostIsUnresolved(host)) return null;
|
|
9771
|
+
const portRaw = fields.get("port");
|
|
9772
|
+
const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
|
|
9773
|
+
const database = fields.get("database") ?? fields.get("db") ?? "";
|
|
9774
|
+
return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
|
|
9775
|
+
}
|
|
9776
|
+
function parseRedisEndpoint(raw) {
|
|
9777
|
+
const s = raw.trim();
|
|
9778
|
+
if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9779
|
+
const first = s.split(",")[0].trim();
|
|
9780
|
+
const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
|
|
9781
|
+
if (!m) return null;
|
|
9782
|
+
const host = m[1];
|
|
9783
|
+
if (hostIsUnresolved(host) || host.includes("=")) return null;
|
|
9784
|
+
const port = m[2] ? Number(m[2]) : void 0;
|
|
9785
|
+
return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
|
|
9786
|
+
}
|
|
9787
|
+
async function resolveEnvUpTree(startDir, name) {
|
|
9788
|
+
let dir = import_node_path32.default.resolve(startDir);
|
|
9789
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
9790
|
+
const value = await resolveEnvVar(dir, name);
|
|
9791
|
+
if (value !== null) return value;
|
|
9792
|
+
const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
|
|
9793
|
+
const parent = import_node_path32.default.dirname(dir);
|
|
9794
|
+
if (atRepoRoot || parent === dir) break;
|
|
9795
|
+
dir = parent;
|
|
9796
|
+
}
|
|
9797
|
+
return null;
|
|
9798
|
+
}
|
|
9799
|
+
async function interpolateEnvRefs(value, dir) {
|
|
9800
|
+
const refs = /* @__PURE__ */ new Set();
|
|
9801
|
+
for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
9802
|
+
refs.add(m[1] ?? m[2]);
|
|
9803
|
+
}
|
|
9804
|
+
let out = value;
|
|
9805
|
+
for (const name of refs) {
|
|
9806
|
+
const resolved = await resolveEnvUpTree(dir, name);
|
|
9807
|
+
if (resolved === null) continue;
|
|
9808
|
+
out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
|
|
9809
|
+
}
|
|
9810
|
+
return out;
|
|
9811
|
+
}
|
|
9812
|
+
function stringLiterals(masked) {
|
|
9813
|
+
const out = [];
|
|
9814
|
+
STRING_LITERAL_RE.lastIndex = 0;
|
|
9815
|
+
let m;
|
|
9816
|
+
while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
|
|
9817
|
+
return out;
|
|
9818
|
+
}
|
|
9819
|
+
function envKeys(masked) {
|
|
9820
|
+
const out = [];
|
|
9821
|
+
ENV_READ_RE.lastIndex = 0;
|
|
9822
|
+
let m;
|
|
9823
|
+
while ((m = ENV_READ_RE.exec(masked)) !== null) {
|
|
9824
|
+
const key = m[1] ?? m[2];
|
|
9825
|
+
if (key) out.push(key);
|
|
9826
|
+
}
|
|
9827
|
+
return out;
|
|
9828
|
+
}
|
|
9829
|
+
async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
|
|
9830
|
+
const out = [];
|
|
9831
|
+
for (const key of keys) {
|
|
9832
|
+
const raw = await resolveEnvUpTree(serviceDir, key);
|
|
9833
|
+
if (raw === null) continue;
|
|
9834
|
+
const value = await interpolateEnvRefs(raw, serviceDir);
|
|
9835
|
+
if (!looksLike(value)) continue;
|
|
9836
|
+
const parsed = parse11(value);
|
|
9837
|
+
if (parsed) out.push(parsed);
|
|
9838
|
+
}
|
|
9839
|
+
for (const lit of literals) {
|
|
9840
|
+
if (!looksLike(lit)) continue;
|
|
9841
|
+
const parsed = parse11(lit);
|
|
9842
|
+
if (parsed) out.push(parsed);
|
|
9843
|
+
}
|
|
9844
|
+
return out;
|
|
9845
|
+
}
|
|
9846
|
+
async function parse9(serviceDir) {
|
|
9847
|
+
const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
|
|
9848
|
+
(f) => import_node_path32.default.extname(f) === CS_EXT
|
|
9849
|
+
);
|
|
9850
|
+
if (files.length === 0) return [];
|
|
9851
|
+
const sources = [];
|
|
9852
|
+
for (const file of files) {
|
|
9853
|
+
const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
|
|
9854
|
+
if (content !== null) sources.push({ file, content });
|
|
9855
|
+
}
|
|
9856
|
+
let pgGateFile = null;
|
|
9857
|
+
let redisGateFile = null;
|
|
9858
|
+
for (const { file, content } of sources) {
|
|
9859
|
+
if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
|
|
9860
|
+
if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
|
|
9861
|
+
}
|
|
9862
|
+
if (!pgGateFile && !redisGateFile) return [];
|
|
9863
|
+
const literals = [];
|
|
9864
|
+
const keys = [];
|
|
9865
|
+
for (const { content } of sources) {
|
|
9866
|
+
const masked = maskCommentsInSource(content);
|
|
9867
|
+
literals.push(...stringLiterals(masked));
|
|
9868
|
+
keys.push(...envKeys(masked));
|
|
9869
|
+
}
|
|
9870
|
+
const out = [];
|
|
9871
|
+
const seenHosts = /* @__PURE__ */ new Set();
|
|
9872
|
+
const push = (config, sourceFile) => {
|
|
9873
|
+
const dedupe = `${config.engine}:${config.host}`;
|
|
9874
|
+
if (seenHosts.has(dedupe)) return;
|
|
9875
|
+
seenHosts.add(dedupe);
|
|
9876
|
+
out.push({ ...config, sourceFile });
|
|
9877
|
+
};
|
|
9878
|
+
if (pgGateFile) {
|
|
9879
|
+
for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
|
|
9880
|
+
push(pg3, pgGateFile);
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
if (redisGateFile) {
|
|
9884
|
+
for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
|
|
9885
|
+
push(redis, redisGateFile);
|
|
9886
|
+
}
|
|
9887
|
+
}
|
|
9888
|
+
return out;
|
|
9889
|
+
}
|
|
9890
|
+
var csharpParser = { name: "csharp", parse: parse9 };
|
|
9891
|
+
|
|
9892
|
+
// src/extract/databases/docker-compose.ts
|
|
9893
|
+
init_cjs_shims();
|
|
9894
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
9346
9895
|
function portFromService(svc) {
|
|
9347
9896
|
for (const raw of svc.ports ?? []) {
|
|
9348
9897
|
const str = String(raw);
|
|
@@ -9367,9 +9916,9 @@ function databaseFromEnv(svc) {
|
|
|
9367
9916
|
};
|
|
9368
9917
|
return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
|
|
9369
9918
|
}
|
|
9370
|
-
async function
|
|
9919
|
+
async function parse10(serviceDir) {
|
|
9371
9920
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
9372
|
-
const abs =
|
|
9921
|
+
const abs = import_node_path33.default.join(serviceDir, name);
|
|
9373
9922
|
if (!await exists(abs)) continue;
|
|
9374
9923
|
const raw = await readYaml(abs);
|
|
9375
9924
|
if (!raw?.services) return [];
|
|
@@ -9391,7 +9940,7 @@ async function parse9(serviceDir) {
|
|
|
9391
9940
|
}
|
|
9392
9941
|
return [];
|
|
9393
9942
|
}
|
|
9394
|
-
var dockerComposeParser = { name: "docker-compose", parse:
|
|
9943
|
+
var dockerComposeParser = { name: "docker-compose", parse: parse10 };
|
|
9395
9944
|
|
|
9396
9945
|
// src/extract/databases/index.ts
|
|
9397
9946
|
var DB_PARSERS = [
|
|
@@ -9403,6 +9952,7 @@ var DB_PARSERS = [
|
|
|
9403
9952
|
ormconfigParser,
|
|
9404
9953
|
typeormParser,
|
|
9405
9954
|
sequelizeParser,
|
|
9955
|
+
csharpParser,
|
|
9406
9956
|
dockerComposeParser
|
|
9407
9957
|
];
|
|
9408
9958
|
function compatibleDriversFor(engine) {
|
|
@@ -9541,7 +10091,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9541
10091
|
discoveredVia: mergedDiscoveredVia
|
|
9542
10092
|
});
|
|
9543
10093
|
}
|
|
9544
|
-
const relConfigFile = toPosix(
|
|
10094
|
+
const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
|
|
9545
10095
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9546
10096
|
graph,
|
|
9547
10097
|
service.pkg.name,
|
|
@@ -9550,7 +10100,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9550
10100
|
);
|
|
9551
10101
|
nodesAdded += fn;
|
|
9552
10102
|
edgesAdded += fe;
|
|
9553
|
-
const evidenceFile = toPosix(
|
|
10103
|
+
const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
|
|
9554
10104
|
const edge = {
|
|
9555
10105
|
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
|
|
9556
10106
|
source: fileNodeId,
|
|
@@ -9568,15 +10118,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9568
10118
|
if (allConfigs.length === 1) {
|
|
9569
10119
|
const primary = allConfigs[0];
|
|
9570
10120
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
9571
|
-
const relPath =
|
|
10121
|
+
const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
|
|
9572
10122
|
const cfgId = (0, import_types23.configId)(relPath);
|
|
9573
10123
|
if (!graph.hasNode(cfgId)) {
|
|
9574
10124
|
const cfgNode = {
|
|
9575
10125
|
id: cfgId,
|
|
9576
10126
|
type: import_types23.NodeType.ConfigNode,
|
|
9577
|
-
name:
|
|
10127
|
+
name: import_node_path34.default.basename(primary.sourceFile),
|
|
9578
10128
|
path: relPath,
|
|
9579
|
-
fileType: isConfigFile(
|
|
10129
|
+
fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
|
|
9580
10130
|
};
|
|
9581
10131
|
graph.addNode(cfgId, cfgNode);
|
|
9582
10132
|
nodesAdded++;
|
|
@@ -9617,27 +10167,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9617
10167
|
|
|
9618
10168
|
// src/extract/configs.ts
|
|
9619
10169
|
init_cjs_shims();
|
|
9620
|
-
var
|
|
9621
|
-
var
|
|
10170
|
+
var import_node_fs23 = require("fs");
|
|
10171
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
9622
10172
|
var import_types24 = require("@neat.is/types");
|
|
9623
10173
|
async function walkConfigFiles(dir, excludeDirs = []) {
|
|
9624
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10174
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
|
|
9625
10175
|
const out = [];
|
|
9626
|
-
async function
|
|
9627
|
-
const entries = await
|
|
10176
|
+
async function walk10(current) {
|
|
10177
|
+
const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
|
|
9628
10178
|
for (const entry of entries) {
|
|
9629
|
-
const full =
|
|
10179
|
+
const full = import_node_path35.default.join(current, entry.name);
|
|
9630
10180
|
if (entry.isDirectory()) {
|
|
9631
10181
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9632
|
-
if (excluded.has(
|
|
10182
|
+
if (excluded.has(import_node_path35.default.resolve(full))) continue;
|
|
9633
10183
|
if (await isPythonVenvDir(full)) continue;
|
|
9634
|
-
await
|
|
10184
|
+
await walk10(full);
|
|
9635
10185
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
9636
10186
|
out.push(full);
|
|
9637
10187
|
}
|
|
9638
10188
|
}
|
|
9639
10189
|
}
|
|
9640
|
-
await
|
|
10190
|
+
await walk10(dir);
|
|
9641
10191
|
return out;
|
|
9642
10192
|
}
|
|
9643
10193
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -9646,19 +10196,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9646
10196
|
for (const service of services) {
|
|
9647
10197
|
const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
|
|
9648
10198
|
for (const file of configFiles) {
|
|
9649
|
-
const relPath =
|
|
10199
|
+
const relPath = import_node_path35.default.relative(scanPath, file);
|
|
9650
10200
|
const node = {
|
|
9651
10201
|
id: (0, import_types24.configId)(relPath),
|
|
9652
10202
|
type: import_types24.NodeType.ConfigNode,
|
|
9653
|
-
name:
|
|
10203
|
+
name: import_node_path35.default.basename(file),
|
|
9654
10204
|
path: relPath,
|
|
9655
|
-
fileType: isConfigFile(
|
|
10205
|
+
fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
|
|
9656
10206
|
};
|
|
9657
10207
|
if (!graph.hasNode(node.id)) {
|
|
9658
10208
|
graph.addNode(node.id, node);
|
|
9659
10209
|
nodesAdded++;
|
|
9660
10210
|
}
|
|
9661
|
-
const relToService = toPosix(
|
|
10211
|
+
const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
|
|
9662
10212
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9663
10213
|
graph,
|
|
9664
10214
|
service.pkg.name,
|
|
@@ -9674,7 +10224,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9674
10224
|
type: import_types24.EdgeType.CONFIGURED_BY,
|
|
9675
10225
|
provenance: import_types24.Provenance.EXTRACTED,
|
|
9676
10226
|
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
9677
|
-
evidence: { file: relPath.split(
|
|
10227
|
+
evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
|
|
9678
10228
|
};
|
|
9679
10229
|
if (!graph.hasEdge(edge.id)) {
|
|
9680
10230
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -9687,8 +10237,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9687
10237
|
|
|
9688
10238
|
// src/extract/proto.ts
|
|
9689
10239
|
init_cjs_shims();
|
|
9690
|
-
var
|
|
9691
|
-
var
|
|
10240
|
+
var import_node_fs24 = require("fs");
|
|
10241
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
9692
10242
|
var import_types25 = require("@neat.is/types");
|
|
9693
10243
|
var PROTO_EXTENSION = ".proto";
|
|
9694
10244
|
function packageOf(content) {
|
|
@@ -9726,23 +10276,23 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
9726
10276
|
return out;
|
|
9727
10277
|
}
|
|
9728
10278
|
async function walkProtoFiles(dir, excludeDirs = []) {
|
|
9729
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10279
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
|
|
9730
10280
|
const out = [];
|
|
9731
|
-
async function
|
|
9732
|
-
const entries = await
|
|
10281
|
+
async function walk10(current) {
|
|
10282
|
+
const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
9733
10283
|
for (const entry of entries) {
|
|
9734
|
-
const full =
|
|
10284
|
+
const full = import_node_path36.default.join(current, entry.name);
|
|
9735
10285
|
if (entry.isDirectory()) {
|
|
9736
10286
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9737
|
-
if (excluded.has(
|
|
10287
|
+
if (excluded.has(import_node_path36.default.resolve(full))) continue;
|
|
9738
10288
|
if (await isPythonVenvDir(full)) continue;
|
|
9739
|
-
await
|
|
9740
|
-
} else if (entry.isFile() &&
|
|
10289
|
+
await walk10(full);
|
|
10290
|
+
} else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
9741
10291
|
out.push(full);
|
|
9742
10292
|
}
|
|
9743
10293
|
}
|
|
9744
10294
|
}
|
|
9745
|
-
await
|
|
10295
|
+
await walk10(dir);
|
|
9746
10296
|
return out;
|
|
9747
10297
|
}
|
|
9748
10298
|
async function addGrpcMethods(graph, services) {
|
|
@@ -9752,10 +10302,10 @@ async function addGrpcMethods(graph, services) {
|
|
|
9752
10302
|
const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
|
|
9753
10303
|
for (const protoPath of protoPaths) {
|
|
9754
10304
|
if (isTestPath(protoPath)) continue;
|
|
9755
|
-
const relFile = toPosix(
|
|
10305
|
+
const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
|
|
9756
10306
|
let content;
|
|
9757
10307
|
try {
|
|
9758
|
-
content = await
|
|
10308
|
+
content = await import_node_fs24.promises.readFile(protoPath, "utf8");
|
|
9759
10309
|
} catch (err) {
|
|
9760
10310
|
recordExtractionError("proto extraction", protoPath, err);
|
|
9761
10311
|
continue;
|
|
@@ -9810,11 +10360,11 @@ async function addGrpcMethods(graph, services) {
|
|
|
9810
10360
|
|
|
9811
10361
|
// src/extract/calls/index.ts
|
|
9812
10362
|
init_cjs_shims();
|
|
9813
|
-
var
|
|
10363
|
+
var import_types44 = require("@neat.is/types");
|
|
9814
10364
|
|
|
9815
10365
|
// src/extract/calls/http.ts
|
|
9816
10366
|
init_cjs_shims();
|
|
9817
|
-
var
|
|
10367
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
9818
10368
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
9819
10369
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9820
10370
|
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
@@ -9897,7 +10447,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9897
10447
|
const seen = /* @__PURE__ */ new Set();
|
|
9898
10448
|
for (const file of files) {
|
|
9899
10449
|
if (isTestPath(file.path)) continue;
|
|
9900
|
-
const parser = parserForExt(
|
|
10450
|
+
const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
|
|
9901
10451
|
let sites;
|
|
9902
10452
|
try {
|
|
9903
10453
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -9906,7 +10456,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9906
10456
|
continue;
|
|
9907
10457
|
}
|
|
9908
10458
|
if (sites.length === 0) continue;
|
|
9909
|
-
const relFile = toPosix(
|
|
10459
|
+
const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
|
|
9910
10460
|
for (const site of sites) {
|
|
9911
10461
|
const targetId = hostToNodeId.get(site.host);
|
|
9912
10462
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -9960,7 +10510,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9960
10510
|
|
|
9961
10511
|
// src/extract/calls/route-match.ts
|
|
9962
10512
|
init_cjs_shims();
|
|
9963
|
-
var
|
|
10513
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
9964
10514
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
9965
10515
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9966
10516
|
var import_types27 = require("@neat.is/types");
|
|
@@ -10159,7 +10709,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10159
10709
|
const seen = /* @__PURE__ */ new Set();
|
|
10160
10710
|
for (const file of files) {
|
|
10161
10711
|
if (isTestPath(file.path)) continue;
|
|
10162
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
10712
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
|
|
10163
10713
|
let sites;
|
|
10164
10714
|
try {
|
|
10165
10715
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -10168,7 +10718,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10168
10718
|
continue;
|
|
10169
10719
|
}
|
|
10170
10720
|
if (sites.length === 0) continue;
|
|
10171
|
-
const relFile = toPosix(
|
|
10721
|
+
const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
|
|
10172
10722
|
for (const site of sites) {
|
|
10173
10723
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
10174
10724
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -10229,7 +10779,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10229
10779
|
|
|
10230
10780
|
// src/extract/calls/kafka.ts
|
|
10231
10781
|
init_cjs_shims();
|
|
10232
|
-
var
|
|
10782
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
10233
10783
|
var import_types28 = require("@neat.is/types");
|
|
10234
10784
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10235
10785
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -10317,13 +10867,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10317
10867
|
// call sites — verified-call-site tier (ADR-066).
|
|
10318
10868
|
confidenceKind: "verified-call-site",
|
|
10319
10869
|
evidence: {
|
|
10320
|
-
file:
|
|
10870
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
10321
10871
|
line,
|
|
10322
10872
|
snippet: snippet(file.content, line)
|
|
10323
10873
|
}
|
|
10324
10874
|
});
|
|
10325
10875
|
};
|
|
10326
|
-
if (
|
|
10876
|
+
if (import_node_path39.default.extname(file.path) === ".go") {
|
|
10327
10877
|
goSaramaEndpoints(file.content, make);
|
|
10328
10878
|
} else {
|
|
10329
10879
|
for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
|
|
@@ -10334,7 +10884,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10334
10884
|
|
|
10335
10885
|
// src/extract/calls/redis.ts
|
|
10336
10886
|
init_cjs_shims();
|
|
10337
|
-
var
|
|
10887
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
10338
10888
|
var import_types29 = require("@neat.is/types");
|
|
10339
10889
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
10340
10890
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -10357,7 +10907,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10357
10907
|
// support tier (ADR-066).
|
|
10358
10908
|
confidenceKind: "url-with-structural-support",
|
|
10359
10909
|
evidence: {
|
|
10360
|
-
file:
|
|
10910
|
+
file: import_node_path40.default.relative(serviceDir, file.path),
|
|
10361
10911
|
line,
|
|
10362
10912
|
snippet: snippet(file.content, line)
|
|
10363
10913
|
}
|
|
@@ -10368,7 +10918,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10368
10918
|
|
|
10369
10919
|
// src/extract/calls/aws.ts
|
|
10370
10920
|
init_cjs_shims();
|
|
10371
|
-
var
|
|
10921
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
10372
10922
|
var import_types30 = require("@neat.is/types");
|
|
10373
10923
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10374
10924
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -10402,7 +10952,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10402
10952
|
// (ADR-066).
|
|
10403
10953
|
confidenceKind: "verified-call-site",
|
|
10404
10954
|
evidence: {
|
|
10405
|
-
file:
|
|
10955
|
+
file: import_node_path41.default.relative(serviceDir, file.path),
|
|
10406
10956
|
line,
|
|
10407
10957
|
snippet: snippet(file.content, line)
|
|
10408
10958
|
}
|
|
@@ -10427,7 +10977,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10427
10977
|
|
|
10428
10978
|
// src/extract/calls/grpc.ts
|
|
10429
10979
|
init_cjs_shims();
|
|
10430
|
-
var
|
|
10980
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
10431
10981
|
var import_types31 = require("@neat.is/types");
|
|
10432
10982
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
10433
10983
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -10486,7 +11036,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10486
11036
|
// tier (ADR-066).
|
|
10487
11037
|
confidenceKind: "verified-call-site",
|
|
10488
11038
|
evidence: {
|
|
10489
|
-
file:
|
|
11039
|
+
file: import_node_path42.default.relative(serviceDir, file.path),
|
|
10490
11040
|
line,
|
|
10491
11041
|
snippet: snippet(file.content, line)
|
|
10492
11042
|
}
|
|
@@ -10497,7 +11047,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10497
11047
|
|
|
10498
11048
|
// src/extract/calls/supabase.ts
|
|
10499
11049
|
init_cjs_shims();
|
|
10500
|
-
var
|
|
11050
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
10501
11051
|
var import_types32 = require("@neat.is/types");
|
|
10502
11052
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
10503
11053
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -10556,7 +11106,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10556
11106
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
10557
11107
|
confidenceKind: "verified-call-site",
|
|
10558
11108
|
evidence: {
|
|
10559
|
-
file:
|
|
11109
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10560
11110
|
line,
|
|
10561
11111
|
snippet: snippet(file.content, line)
|
|
10562
11112
|
}
|
|
@@ -10583,7 +11133,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10583
11133
|
edgeType: "CALLS",
|
|
10584
11134
|
confidenceKind: "verified-call-site",
|
|
10585
11135
|
evidence: {
|
|
10586
|
-
file:
|
|
11136
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10587
11137
|
line,
|
|
10588
11138
|
snippet: snippet(file.content, line)
|
|
10589
11139
|
}
|
|
@@ -10595,7 +11145,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10595
11145
|
|
|
10596
11146
|
// src/extract/calls/firestore.ts
|
|
10597
11147
|
init_cjs_shims();
|
|
10598
|
-
var
|
|
11148
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
10599
11149
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
10600
11150
|
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
10601
11151
|
var import_types33 = require("@neat.is/types");
|
|
@@ -10634,7 +11184,7 @@ function isFirestoreClientFactory(node) {
|
|
|
10634
11184
|
}
|
|
10635
11185
|
function firestoreClientVars(root) {
|
|
10636
11186
|
const vars = /* @__PURE__ */ new Set();
|
|
10637
|
-
const
|
|
11187
|
+
const walk10 = (node) => {
|
|
10638
11188
|
if (node.type === "variable_declarator") {
|
|
10639
11189
|
const name = node.childForFieldName("name");
|
|
10640
11190
|
let value = node.childForFieldName("value");
|
|
@@ -10643,9 +11193,9 @@ function firestoreClientVars(root) {
|
|
|
10643
11193
|
vars.add(name.text);
|
|
10644
11194
|
}
|
|
10645
11195
|
}
|
|
10646
|
-
for (const c of namedChildren(node))
|
|
11196
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10647
11197
|
};
|
|
10648
|
-
|
|
11198
|
+
walk10(root);
|
|
10649
11199
|
return vars;
|
|
10650
11200
|
}
|
|
10651
11201
|
function isClientExpr(node, clientVars) {
|
|
@@ -10766,7 +11316,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10766
11316
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
10767
11317
|
if (!hasClient && !hasAdmin) return [];
|
|
10768
11318
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
10769
|
-
const tree = parseSource3(parserForExt2(
|
|
11319
|
+
const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
|
|
10770
11320
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
10771
11321
|
const collLine = /* @__PURE__ */ new Map();
|
|
10772
11322
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -10800,7 +11350,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10800
11350
|
}
|
|
10801
11351
|
s.add(field);
|
|
10802
11352
|
};
|
|
10803
|
-
const
|
|
11353
|
+
const walk10 = (node) => {
|
|
10804
11354
|
if (node.type === "call_expression") {
|
|
10805
11355
|
const fn = node.childForFieldName("function");
|
|
10806
11356
|
const line = node.startPosition.row + 1;
|
|
@@ -10840,9 +11390,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10840
11390
|
}
|
|
10841
11391
|
}
|
|
10842
11392
|
}
|
|
10843
|
-
for (const c of namedChildren(node))
|
|
11393
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10844
11394
|
};
|
|
10845
|
-
|
|
11395
|
+
walk10(tree.rootNode);
|
|
10846
11396
|
const out = [];
|
|
10847
11397
|
for (const [collPath, line] of collLine) {
|
|
10848
11398
|
const byField = writes.get(collPath);
|
|
@@ -10870,7 +11420,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10870
11420
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
10871
11421
|
...sdkWrites ? { sdkWrites } : {},
|
|
10872
11422
|
evidence: {
|
|
10873
|
-
file:
|
|
11423
|
+
file: import_node_path44.default.relative(serviceDir, file.path),
|
|
10874
11424
|
line,
|
|
10875
11425
|
snippet: snippet(file.content, line)
|
|
10876
11426
|
}
|
|
@@ -10881,7 +11431,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10881
11431
|
|
|
10882
11432
|
// src/extract/calls/mongoose.ts
|
|
10883
11433
|
init_cjs_shims();
|
|
10884
|
-
var
|
|
11434
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
10885
11435
|
var import_types34 = require("@neat.is/types");
|
|
10886
11436
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
10887
11437
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -11032,7 +11582,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
11032
11582
|
kind: r.kind,
|
|
11033
11583
|
edgeType: "CALLS",
|
|
11034
11584
|
confidenceKind: "verified-call-site",
|
|
11035
|
-
evidence: { file:
|
|
11585
|
+
evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11036
11586
|
};
|
|
11037
11587
|
}
|
|
11038
11588
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -11122,7 +11672,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11122
11672
|
const registry = /* @__PURE__ */ new Map();
|
|
11123
11673
|
for (const f of mongooseFiles) {
|
|
11124
11674
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
11125
|
-
if (fx) registry.set(toPosix(
|
|
11675
|
+
if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
|
|
11126
11676
|
}
|
|
11127
11677
|
if (registry.size === 0) return [];
|
|
11128
11678
|
const out = [];
|
|
@@ -11133,7 +11683,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11133
11683
|
const directColl = /* @__PURE__ */ new Map();
|
|
11134
11684
|
const nsExports = /* @__PURE__ */ new Map();
|
|
11135
11685
|
for (const b of bindings) {
|
|
11136
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
11686
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
|
|
11137
11687
|
if (!resolvedRel) continue;
|
|
11138
11688
|
const fx = registry.get(resolvedRel);
|
|
11139
11689
|
if (!fx) continue;
|
|
@@ -11177,7 +11727,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11177
11727
|
|
|
11178
11728
|
// src/extract/calls/sqlalchemy.ts
|
|
11179
11729
|
init_cjs_shims();
|
|
11180
|
-
var
|
|
11730
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
11181
11731
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
11182
11732
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
11183
11733
|
var import_types35 = require("@neat.is/types");
|
|
@@ -11316,7 +11866,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
11316
11866
|
childTable,
|
|
11317
11867
|
parentTable,
|
|
11318
11868
|
evidence: {
|
|
11319
|
-
file:
|
|
11869
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11320
11870
|
line,
|
|
11321
11871
|
snippet: snippet(file.content, line)
|
|
11322
11872
|
}
|
|
@@ -11341,7 +11891,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
11341
11891
|
confidenceKind: "verified-call-site",
|
|
11342
11892
|
...columns && columns.length > 0 ? { columns } : {},
|
|
11343
11893
|
evidence: {
|
|
11344
|
-
file:
|
|
11894
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11345
11895
|
line,
|
|
11346
11896
|
snippet: snippet(file.content, line)
|
|
11347
11897
|
}
|
|
@@ -11448,7 +11998,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11448
11998
|
edgeType: "CALLS",
|
|
11449
11999
|
confidenceKind: "verified-call-site",
|
|
11450
12000
|
evidence: {
|
|
11451
|
-
file:
|
|
12001
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11452
12002
|
line,
|
|
11453
12003
|
snippet: snippet(file.content, line)
|
|
11454
12004
|
}
|
|
@@ -11460,7 +12010,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11460
12010
|
|
|
11461
12011
|
// src/extract/calls/django-orm.ts
|
|
11462
12012
|
init_cjs_shims();
|
|
11463
|
-
var
|
|
12013
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
11464
12014
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
11465
12015
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
11466
12016
|
var import_types36 = require("@neat.is/types");
|
|
@@ -11530,7 +12080,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11530
12080
|
const tree = parseSource7(makePyParser4(), file.content);
|
|
11531
12081
|
const out = [];
|
|
11532
12082
|
const seen = /* @__PURE__ */ new Set();
|
|
11533
|
-
const defaultAppLabel =
|
|
12083
|
+
const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
|
|
11534
12084
|
walk4(tree.rootNode, (node) => {
|
|
11535
12085
|
if (node.type !== "class_definition") return;
|
|
11536
12086
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -11548,7 +12098,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11548
12098
|
kind: "sql-table",
|
|
11549
12099
|
edgeType: "CALLS",
|
|
11550
12100
|
confidenceKind: "verified-call-site",
|
|
11551
|
-
evidence: { file:
|
|
12101
|
+
evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11552
12102
|
});
|
|
11553
12103
|
});
|
|
11554
12104
|
return out;
|
|
@@ -11556,7 +12106,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11556
12106
|
|
|
11557
12107
|
// src/extract/calls/drizzle.ts
|
|
11558
12108
|
init_cjs_shims();
|
|
11559
|
-
var
|
|
12109
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
11560
12110
|
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
11561
12111
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
11562
12112
|
var import_types37 = require("@neat.is/types");
|
|
@@ -11634,10 +12184,10 @@ function columnsFromObject(obj) {
|
|
|
11634
12184
|
}
|
|
11635
12185
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
11636
12186
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11637
|
-
const tree = parseSource3(parserForExt3(
|
|
12187
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11638
12188
|
const out = [];
|
|
11639
12189
|
const seen = /* @__PURE__ */ new Set();
|
|
11640
|
-
const
|
|
12190
|
+
const walk10 = (node) => {
|
|
11641
12191
|
if (node.type === "call_expression") {
|
|
11642
12192
|
const fn = node.childForFieldName("function");
|
|
11643
12193
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11657,7 +12207,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11657
12207
|
confidenceKind: "structural",
|
|
11658
12208
|
columns,
|
|
11659
12209
|
evidence: {
|
|
11660
|
-
file:
|
|
12210
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11661
12211
|
line,
|
|
11662
12212
|
snippet: snippet(file.content, line)
|
|
11663
12213
|
}
|
|
@@ -11665,9 +12215,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11665
12215
|
}
|
|
11666
12216
|
}
|
|
11667
12217
|
}
|
|
11668
|
-
for (const c of namedChildren4(node))
|
|
12218
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11669
12219
|
};
|
|
11670
|
-
|
|
12220
|
+
walk10(tree.rootNode);
|
|
11671
12221
|
return out;
|
|
11672
12222
|
}
|
|
11673
12223
|
function enclosingVarName(call) {
|
|
@@ -11689,7 +12239,7 @@ function enclosingVarName(call) {
|
|
|
11689
12239
|
function collectDrizzleTables(root) {
|
|
11690
12240
|
const tables = [];
|
|
11691
12241
|
const varToTable = /* @__PURE__ */ new Map();
|
|
11692
|
-
const
|
|
12242
|
+
const walk10 = (node) => {
|
|
11693
12243
|
if (node.type === "call_expression") {
|
|
11694
12244
|
const fn = node.childForFieldName("function");
|
|
11695
12245
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11704,9 +12254,9 @@ function collectDrizzleTables(root) {
|
|
|
11704
12254
|
}
|
|
11705
12255
|
}
|
|
11706
12256
|
}
|
|
11707
|
-
for (const c of namedChildren4(node))
|
|
12257
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11708
12258
|
};
|
|
11709
|
-
|
|
12259
|
+
walk10(root);
|
|
11710
12260
|
return { tables, varToTable };
|
|
11711
12261
|
}
|
|
11712
12262
|
function referencesTargetVar(call) {
|
|
@@ -11723,13 +12273,13 @@ function referencesTargetVar(call) {
|
|
|
11723
12273
|
}
|
|
11724
12274
|
function drizzleForeignKeys(file, serviceDir) {
|
|
11725
12275
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11726
|
-
const tree = parseSource3(parserForExt3(
|
|
12276
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11727
12277
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
11728
12278
|
const out = [];
|
|
11729
12279
|
const seen = /* @__PURE__ */ new Set();
|
|
11730
12280
|
for (const table of tables) {
|
|
11731
12281
|
if (!table.object) continue;
|
|
11732
|
-
const
|
|
12282
|
+
const walk10 = (node) => {
|
|
11733
12283
|
if (node.type === "call_expression") {
|
|
11734
12284
|
const targetVar = referencesTargetVar(node);
|
|
11735
12285
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -11742,7 +12292,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11742
12292
|
childTable: table.tableName,
|
|
11743
12293
|
parentTable,
|
|
11744
12294
|
evidence: {
|
|
11745
|
-
file:
|
|
12295
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11746
12296
|
line,
|
|
11747
12297
|
snippet: snippet(file.content, line)
|
|
11748
12298
|
}
|
|
@@ -11750,16 +12300,16 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11750
12300
|
}
|
|
11751
12301
|
}
|
|
11752
12302
|
}
|
|
11753
|
-
for (const c of namedChildren4(node))
|
|
12303
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11754
12304
|
};
|
|
11755
|
-
|
|
12305
|
+
walk10(table.object);
|
|
11756
12306
|
}
|
|
11757
12307
|
return out;
|
|
11758
12308
|
}
|
|
11759
12309
|
|
|
11760
12310
|
// src/extract/calls/prisma.ts
|
|
11761
12311
|
init_cjs_shims();
|
|
11762
|
-
var
|
|
12312
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
11763
12313
|
var import_types38 = require("@neat.is/types");
|
|
11764
12314
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
11765
12315
|
"Int",
|
|
@@ -11815,7 +12365,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11815
12365
|
confidenceKind: "structural",
|
|
11816
12366
|
columns: b.columns,
|
|
11817
12367
|
evidence: {
|
|
11818
|
-
file:
|
|
12368
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11819
12369
|
line: b.startLine,
|
|
11820
12370
|
snippet: snippet(content, b.startLine)
|
|
11821
12371
|
}
|
|
@@ -11876,7 +12426,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11876
12426
|
}
|
|
11877
12427
|
async function prismaColumnEndpoints(serviceDir) {
|
|
11878
12428
|
const schemaPath = await findFirst(serviceDir, [
|
|
11879
|
-
|
|
12429
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11880
12430
|
"schema.prisma"
|
|
11881
12431
|
]);
|
|
11882
12432
|
if (!schemaPath) return [];
|
|
@@ -11951,7 +12501,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11951
12501
|
childTable: current.table,
|
|
11952
12502
|
parentTable,
|
|
11953
12503
|
evidence: {
|
|
11954
|
-
file:
|
|
12504
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11955
12505
|
line: lineNo,
|
|
11956
12506
|
snippet: snippet(content, lineNo)
|
|
11957
12507
|
}
|
|
@@ -11966,7 +12516,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11966
12516
|
}
|
|
11967
12517
|
async function prismaForeignKeys(serviceDir) {
|
|
11968
12518
|
const schemaPath = await findFirst(serviceDir, [
|
|
11969
|
-
|
|
12519
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11970
12520
|
"schema.prisma"
|
|
11971
12521
|
]);
|
|
11972
12522
|
if (!schemaPath) return [];
|
|
@@ -11977,7 +12527,7 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
11977
12527
|
|
|
11978
12528
|
// src/extract/calls/activerecord.ts
|
|
11979
12529
|
init_cjs_shims();
|
|
11980
|
-
var
|
|
12530
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11981
12531
|
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
11982
12532
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
11983
12533
|
var import_types39 = require("@neat.is/types");
|
|
@@ -12185,7 +12735,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
12185
12735
|
confidenceKind: "structural",
|
|
12186
12736
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
12187
12737
|
evidence: {
|
|
12188
|
-
file:
|
|
12738
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12189
12739
|
line: table.line,
|
|
12190
12740
|
snippet: snippet(file.content, table.line)
|
|
12191
12741
|
}
|
|
@@ -12207,7 +12757,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
12207
12757
|
childTable,
|
|
12208
12758
|
parentTable,
|
|
12209
12759
|
evidence: {
|
|
12210
|
-
file:
|
|
12760
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12211
12761
|
line,
|
|
12212
12762
|
snippet: snippet(file.content, line)
|
|
12213
12763
|
}
|
|
@@ -12290,7 +12840,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
12290
12840
|
edgeType: "CALLS",
|
|
12291
12841
|
confidenceKind: "verified-call-site",
|
|
12292
12842
|
evidence: {
|
|
12293
|
-
file:
|
|
12843
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12294
12844
|
line,
|
|
12295
12845
|
snippet: snippet(file.content, line)
|
|
12296
12846
|
}
|
|
@@ -12327,7 +12877,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12327
12877
|
childTable,
|
|
12328
12878
|
parentTable,
|
|
12329
12879
|
evidence: {
|
|
12330
|
-
file:
|
|
12880
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12331
12881
|
line,
|
|
12332
12882
|
snippet: snippet(file.content, line)
|
|
12333
12883
|
}
|
|
@@ -12339,7 +12889,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12339
12889
|
|
|
12340
12890
|
// src/extract/calls/eloquent.ts
|
|
12341
12891
|
init_cjs_shims();
|
|
12342
|
-
var
|
|
12892
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12343
12893
|
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
12344
12894
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
12345
12895
|
var import_types40 = require("@neat.is/types");
|
|
@@ -12596,7 +13146,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
12596
13146
|
confidenceKind: "structural",
|
|
12597
13147
|
...columns.length > 0 ? { columns } : {},
|
|
12598
13148
|
evidence: {
|
|
12599
|
-
file:
|
|
13149
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12600
13150
|
line: bp.line,
|
|
12601
13151
|
snippet: snippet(file.content, bp.line)
|
|
12602
13152
|
}
|
|
@@ -12618,7 +13168,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
12618
13168
|
childTable,
|
|
12619
13169
|
parentTable,
|
|
12620
13170
|
evidence: {
|
|
12621
|
-
file:
|
|
13171
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12622
13172
|
line,
|
|
12623
13173
|
snippet: snippet(file.content, line)
|
|
12624
13174
|
}
|
|
@@ -12738,7 +13288,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
12738
13288
|
edgeType: "CALLS",
|
|
12739
13289
|
confidenceKind: "verified-call-site",
|
|
12740
13290
|
evidence: {
|
|
12741
|
-
file:
|
|
13291
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12742
13292
|
line,
|
|
12743
13293
|
snippet: snippet(file.content, line)
|
|
12744
13294
|
}
|
|
@@ -12774,7 +13324,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12774
13324
|
childTable,
|
|
12775
13325
|
parentTable,
|
|
12776
13326
|
evidence: {
|
|
12777
|
-
file:
|
|
13327
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12778
13328
|
line,
|
|
12779
13329
|
snippet: snippet(file.content, line)
|
|
12780
13330
|
}
|
|
@@ -12786,7 +13336,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12786
13336
|
|
|
12787
13337
|
// src/extract/calls/go.ts
|
|
12788
13338
|
init_cjs_shims();
|
|
12789
|
-
var
|
|
13339
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12790
13340
|
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
12791
13341
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
12792
13342
|
var import_types41 = require("@neat.is/types");
|
|
@@ -12861,7 +13411,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
12861
13411
|
return null;
|
|
12862
13412
|
}
|
|
12863
13413
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
12864
|
-
if (
|
|
13414
|
+
if (import_node_path54.default.extname(file.path) !== ".go") return [];
|
|
12865
13415
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
12866
13416
|
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12867
13417
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -12890,7 +13440,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12890
13440
|
confidenceKind: "verified-call-site",
|
|
12891
13441
|
...columns.length > 0 ? { columns } : {},
|
|
12892
13442
|
evidence: {
|
|
12893
|
-
file: toPosix(
|
|
13443
|
+
file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
|
|
12894
13444
|
line,
|
|
12895
13445
|
snippet: snippet(file.content, line)
|
|
12896
13446
|
}
|
|
@@ -12901,7 +13451,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12901
13451
|
|
|
12902
13452
|
// src/extract/calls/gorm.ts
|
|
12903
13453
|
init_cjs_shims();
|
|
12904
|
-
var
|
|
13454
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12905
13455
|
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
12906
13456
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
12907
13457
|
var import_types42 = require("@neat.is/types");
|
|
@@ -13334,7 +13884,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
13334
13884
|
seen.delete(struct.name);
|
|
13335
13885
|
}
|
|
13336
13886
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
13337
|
-
if (
|
|
13887
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13338
13888
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13339
13889
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13340
13890
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13356,7 +13906,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13356
13906
|
confidenceKind: "structural",
|
|
13357
13907
|
...columns.length > 0 ? { columns } : {},
|
|
13358
13908
|
evidence: {
|
|
13359
|
-
file: toPosix(
|
|
13909
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13360
13910
|
line: struct.line,
|
|
13361
13911
|
snippet: snippet(file.content, struct.line)
|
|
13362
13912
|
}
|
|
@@ -13365,7 +13915,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13365
13915
|
return out;
|
|
13366
13916
|
}
|
|
13367
13917
|
function gormForeignKeys(file, serviceDir) {
|
|
13368
|
-
if (
|
|
13918
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13369
13919
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13370
13920
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13371
13921
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13380,7 +13930,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13380
13930
|
childTable,
|
|
13381
13931
|
parentTable,
|
|
13382
13932
|
evidence: {
|
|
13383
|
-
file: toPosix(
|
|
13933
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13384
13934
|
line,
|
|
13385
13935
|
snippet: snippet(file.content, line)
|
|
13386
13936
|
}
|
|
@@ -13415,15 +13965,131 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13415
13965
|
return out;
|
|
13416
13966
|
}
|
|
13417
13967
|
|
|
13968
|
+
// src/extract/calls/efcore.ts
|
|
13969
|
+
init_cjs_shims();
|
|
13970
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
13971
|
+
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
13972
|
+
var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
13973
|
+
var import_types43 = require("@neat.is/types");
|
|
13974
|
+
var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
|
|
13975
|
+
var PARSE_CHUNK12 = 16384;
|
|
13976
|
+
function makeCsParser() {
|
|
13977
|
+
const p = new import_tree_sitter16.default();
|
|
13978
|
+
p.setLanguage(import_tree_sitter_c_sharp2.default);
|
|
13979
|
+
return p;
|
|
13980
|
+
}
|
|
13981
|
+
function parseSource12(parser, source) {
|
|
13982
|
+
return parser.parse(
|
|
13983
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
|
|
13984
|
+
);
|
|
13985
|
+
}
|
|
13986
|
+
function walk9(node, visit) {
|
|
13987
|
+
visit(node);
|
|
13988
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13989
|
+
const c = node.namedChild(i);
|
|
13990
|
+
if (c) walk9(c, visit);
|
|
13991
|
+
}
|
|
13992
|
+
}
|
|
13993
|
+
function firstChildOfType(node, type) {
|
|
13994
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13995
|
+
const c = node.namedChild(i);
|
|
13996
|
+
if (c?.type === type) return c;
|
|
13997
|
+
}
|
|
13998
|
+
return null;
|
|
13999
|
+
}
|
|
14000
|
+
function csStringLiteral(node) {
|
|
14001
|
+
if (node.type === "string_literal") {
|
|
14002
|
+
let out = "";
|
|
14003
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14004
|
+
const c = node.namedChild(i);
|
|
14005
|
+
if (c?.type === "string_literal_content") out += c.text;
|
|
14006
|
+
}
|
|
14007
|
+
return out;
|
|
14008
|
+
}
|
|
14009
|
+
if (node.type === "verbatim_string_literal") {
|
|
14010
|
+
const t = node.text;
|
|
14011
|
+
return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
|
|
14012
|
+
}
|
|
14013
|
+
return null;
|
|
14014
|
+
}
|
|
14015
|
+
function attributeName(attr) {
|
|
14016
|
+
const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
|
|
14017
|
+
if (!nameNode) return null;
|
|
14018
|
+
const text = nameNode.text;
|
|
14019
|
+
const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
|
|
14020
|
+
return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
|
|
14021
|
+
}
|
|
14022
|
+
function tableFromAttribute(attr) {
|
|
14023
|
+
if (attributeName(attr) !== "Table") return null;
|
|
14024
|
+
const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
|
|
14025
|
+
if (!args) return null;
|
|
14026
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
14027
|
+
const arg = args.namedChild(i);
|
|
14028
|
+
if (arg?.type !== "attribute_argument") continue;
|
|
14029
|
+
const first = arg.namedChild(0);
|
|
14030
|
+
if (!first) continue;
|
|
14031
|
+
const value = csStringLiteral(first);
|
|
14032
|
+
if (value !== null) return value;
|
|
14033
|
+
return null;
|
|
14034
|
+
}
|
|
14035
|
+
return null;
|
|
14036
|
+
}
|
|
14037
|
+
function tableFromToTable(call) {
|
|
14038
|
+
const fn = call.childForFieldName("function");
|
|
14039
|
+
if (fn?.type !== "member_access_expression") return null;
|
|
14040
|
+
const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
|
|
14041
|
+
if (method?.text !== "ToTable") return null;
|
|
14042
|
+
const args = call.childForFieldName("arguments");
|
|
14043
|
+
const firstArg2 = args?.namedChild(0);
|
|
14044
|
+
if (firstArg2?.type !== "argument") return null;
|
|
14045
|
+
const value = firstArg2.namedChild(0);
|
|
14046
|
+
return value ? csStringLiteral(value) : null;
|
|
14047
|
+
}
|
|
14048
|
+
function efcoreEndpointsFromFile(file, serviceDir) {
|
|
14049
|
+
if (import_node_path56.default.extname(file.path) !== ".cs") return [];
|
|
14050
|
+
if (!EFCORE_GATE.test(file.content)) return [];
|
|
14051
|
+
const tree = parseSource12(makeCsParser(), file.content);
|
|
14052
|
+
const out = [];
|
|
14053
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14054
|
+
const push = (name, line) => {
|
|
14055
|
+
if (!name || seen.has(name)) return;
|
|
14056
|
+
seen.add(name);
|
|
14057
|
+
out.push({
|
|
14058
|
+
infraId: (0, import_types43.infraId)("sql-table", name),
|
|
14059
|
+
name,
|
|
14060
|
+
kind: "sql-table",
|
|
14061
|
+
edgeType: "CALLS",
|
|
14062
|
+
confidenceKind: "structural",
|
|
14063
|
+
evidence: {
|
|
14064
|
+
file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
|
|
14065
|
+
line,
|
|
14066
|
+
snippet: snippet(file.content, line)
|
|
14067
|
+
}
|
|
14068
|
+
});
|
|
14069
|
+
};
|
|
14070
|
+
walk9(tree.rootNode, (node) => {
|
|
14071
|
+
if (node.type === "attribute") {
|
|
14072
|
+
const table = tableFromAttribute(node);
|
|
14073
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14074
|
+
return;
|
|
14075
|
+
}
|
|
14076
|
+
if (node.type === "invocation_expression") {
|
|
14077
|
+
const table = tableFromToTable(node);
|
|
14078
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14079
|
+
}
|
|
14080
|
+
});
|
|
14081
|
+
return out;
|
|
14082
|
+
}
|
|
14083
|
+
|
|
13418
14084
|
// src/extract/calls/index.ts
|
|
13419
14085
|
function edgeTypeFromEndpoint(ep) {
|
|
13420
14086
|
switch (ep.edgeType) {
|
|
13421
14087
|
case "PUBLISHES_TO":
|
|
13422
|
-
return
|
|
14088
|
+
return import_types44.EdgeType.PUBLISHES_TO;
|
|
13423
14089
|
case "CONSUMES_FROM":
|
|
13424
|
-
return
|
|
14090
|
+
return import_types44.EdgeType.CONSUMES_FROM;
|
|
13425
14091
|
default:
|
|
13426
|
-
return
|
|
14092
|
+
return import_types44.EdgeType.CALLS;
|
|
13427
14093
|
}
|
|
13428
14094
|
}
|
|
13429
14095
|
function isAwsKind(kind) {
|
|
@@ -13473,6 +14139,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13473
14139
|
} catch (err) {
|
|
13474
14140
|
recordExtractionError("laravel eloquent extraction", file.path, err);
|
|
13475
14141
|
}
|
|
14142
|
+
try {
|
|
14143
|
+
endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
|
|
14144
|
+
} catch (err) {
|
|
14145
|
+
recordExtractionError("efcore data-axis extraction", file.path, err);
|
|
14146
|
+
}
|
|
13476
14147
|
}
|
|
13477
14148
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
13478
14149
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
@@ -13483,7 +14154,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13483
14154
|
if (!graph.hasNode(ep.infraId)) {
|
|
13484
14155
|
const node = {
|
|
13485
14156
|
id: ep.infraId,
|
|
13486
|
-
type:
|
|
14157
|
+
type: import_types44.NodeType.InfraNode,
|
|
13487
14158
|
name: ep.name,
|
|
13488
14159
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
13489
14160
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -13496,21 +14167,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13496
14167
|
}
|
|
13497
14168
|
if (ep.columns && ep.columns.length > 0) {
|
|
13498
14169
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13499
|
-
if (node.type ===
|
|
14170
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13500
14171
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13501
14172
|
...node,
|
|
13502
14173
|
columns: foldColumns(
|
|
13503
14174
|
node.columns,
|
|
13504
14175
|
ep.columns,
|
|
13505
|
-
|
|
13506
|
-
(0,
|
|
14176
|
+
import_types44.Provenance.EXTRACTED,
|
|
14177
|
+
(0, import_types44.confidenceForExtracted)(ep.confidenceKind)
|
|
13507
14178
|
)
|
|
13508
14179
|
});
|
|
13509
14180
|
}
|
|
13510
14181
|
}
|
|
13511
14182
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
13512
14183
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13513
|
-
if (node.type ===
|
|
14184
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13514
14185
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13515
14186
|
...node,
|
|
13516
14187
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -13518,7 +14189,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13518
14189
|
}
|
|
13519
14190
|
}
|
|
13520
14191
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
13521
|
-
const confidence = (0,
|
|
14192
|
+
const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
|
|
13522
14193
|
const relFile = toPosix(ep.evidence.file);
|
|
13523
14194
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
13524
14195
|
graph,
|
|
@@ -13528,7 +14199,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13528
14199
|
);
|
|
13529
14200
|
nodesAdded += n;
|
|
13530
14201
|
edgesAdded += e;
|
|
13531
|
-
if (!(0,
|
|
14202
|
+
if (!(0, import_types44.passesExtractedFloor)(confidence)) {
|
|
13532
14203
|
noteExtractedDropped({
|
|
13533
14204
|
source: fileNodeId,
|
|
13534
14205
|
target: ep.infraId,
|
|
@@ -13548,7 +14219,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13548
14219
|
source: fileNodeId,
|
|
13549
14220
|
target: ep.infraId,
|
|
13550
14221
|
type: edgeType,
|
|
13551
|
-
provenance:
|
|
14222
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
13552
14223
|
confidence,
|
|
13553
14224
|
evidence: ep.evidence
|
|
13554
14225
|
};
|
|
@@ -13571,7 +14242,7 @@ async function addCallEdges(graph, services) {
|
|
|
13571
14242
|
|
|
13572
14243
|
// src/extract/table-edges.ts
|
|
13573
14244
|
init_cjs_shims();
|
|
13574
|
-
var
|
|
14245
|
+
var import_types45 = require("@neat.is/types");
|
|
13575
14246
|
async function addTableEdges(graph, services) {
|
|
13576
14247
|
let nodesAdded = 0;
|
|
13577
14248
|
let edgesAdded = 0;
|
|
@@ -13599,20 +14270,20 @@ async function addTableEdges(graph, services) {
|
|
|
13599
14270
|
}
|
|
13600
14271
|
refs.push(...modelRefs);
|
|
13601
14272
|
for (const ref of refs) {
|
|
13602
|
-
const childId = (0,
|
|
13603
|
-
const parentId = (0,
|
|
14273
|
+
const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
|
|
14274
|
+
const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
|
|
13604
14275
|
if (childId === parentId) continue;
|
|
13605
14276
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
13606
14277
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
13607
|
-
const edgeId = (0,
|
|
14278
|
+
const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
|
|
13608
14279
|
if (graph.hasEdge(edgeId)) continue;
|
|
13609
14280
|
const edge = {
|
|
13610
14281
|
id: edgeId,
|
|
13611
14282
|
source: childId,
|
|
13612
14283
|
target: parentId,
|
|
13613
|
-
type:
|
|
13614
|
-
provenance:
|
|
13615
|
-
confidence: (0,
|
|
14284
|
+
type: import_types45.EdgeType.REFERENCES,
|
|
14285
|
+
provenance: import_types45.Provenance.EXTRACTED,
|
|
14286
|
+
confidence: (0, import_types45.confidenceForExtracted)("structural"),
|
|
13616
14287
|
evidence: ref.evidence
|
|
13617
14288
|
};
|
|
13618
14289
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -13625,7 +14296,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
13625
14296
|
if (graph.hasNode(id)) return 0;
|
|
13626
14297
|
const node = {
|
|
13627
14298
|
id,
|
|
13628
|
-
type:
|
|
14299
|
+
type: import_types45.NodeType.InfraNode,
|
|
13629
14300
|
name,
|
|
13630
14301
|
provider: "self",
|
|
13631
14302
|
kind: "sql-table"
|
|
@@ -13639,16 +14310,16 @@ init_cjs_shims();
|
|
|
13639
14310
|
|
|
13640
14311
|
// src/extract/infra/docker-compose.ts
|
|
13641
14312
|
init_cjs_shims();
|
|
13642
|
-
var
|
|
13643
|
-
var
|
|
14313
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
14314
|
+
var import_types47 = require("@neat.is/types");
|
|
13644
14315
|
|
|
13645
14316
|
// src/extract/infra/shared.ts
|
|
13646
14317
|
init_cjs_shims();
|
|
13647
|
-
var
|
|
14318
|
+
var import_types46 = require("@neat.is/types");
|
|
13648
14319
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
13649
14320
|
return {
|
|
13650
|
-
id: (0,
|
|
13651
|
-
type:
|
|
14321
|
+
id: (0, import_types46.infraId)(kind, name),
|
|
14322
|
+
type: import_types46.NodeType.InfraNode,
|
|
13652
14323
|
name,
|
|
13653
14324
|
provider,
|
|
13654
14325
|
kind,
|
|
@@ -13692,8 +14363,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
13692
14363
|
source: anchorId,
|
|
13693
14364
|
target: node.id,
|
|
13694
14365
|
type: edgeType,
|
|
13695
|
-
provenance:
|
|
13696
|
-
confidence: (0,
|
|
14366
|
+
provenance: import_types46.Provenance.EXTRACTED,
|
|
14367
|
+
confidence: (0, import_types46.confidenceForExtracted)("structural"),
|
|
13697
14368
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
13698
14369
|
};
|
|
13699
14370
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13710,7 +14381,7 @@ function dependsOnList(value) {
|
|
|
13710
14381
|
}
|
|
13711
14382
|
function serviceNameToServiceNode(name, services) {
|
|
13712
14383
|
for (const s of services) {
|
|
13713
|
-
if (s.node.name === name ||
|
|
14384
|
+
if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
|
|
13714
14385
|
}
|
|
13715
14386
|
return null;
|
|
13716
14387
|
}
|
|
@@ -13719,7 +14390,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13719
14390
|
let edgesAdded = 0;
|
|
13720
14391
|
let composePath = null;
|
|
13721
14392
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
13722
|
-
const abs =
|
|
14393
|
+
const abs = import_node_path57.default.join(scanPath, name);
|
|
13723
14394
|
if (await exists(abs)) {
|
|
13724
14395
|
composePath = abs;
|
|
13725
14396
|
break;
|
|
@@ -13732,13 +14403,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13732
14403
|
} catch (err) {
|
|
13733
14404
|
recordExtractionError(
|
|
13734
14405
|
"infra docker-compose",
|
|
13735
|
-
|
|
14406
|
+
import_node_path57.default.relative(scanPath, composePath),
|
|
13736
14407
|
err
|
|
13737
14408
|
);
|
|
13738
14409
|
return { nodesAdded, edgesAdded };
|
|
13739
14410
|
}
|
|
13740
14411
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
13741
|
-
const evidenceFile =
|
|
14412
|
+
const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
|
|
13742
14413
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
13743
14414
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
13744
14415
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -13760,15 +14431,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13760
14431
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
13761
14432
|
const targetId = composeNameToNodeId.get(dep);
|
|
13762
14433
|
if (!targetId) continue;
|
|
13763
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
14434
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
|
|
13764
14435
|
if (graph.hasEdge(edgeId)) continue;
|
|
13765
14436
|
const edge = {
|
|
13766
14437
|
id: edgeId,
|
|
13767
14438
|
source: sourceId,
|
|
13768
14439
|
target: targetId,
|
|
13769
|
-
type:
|
|
13770
|
-
provenance:
|
|
13771
|
-
confidence: (0,
|
|
14440
|
+
type: import_types47.EdgeType.DEPENDS_ON,
|
|
14441
|
+
provenance: import_types47.Provenance.EXTRACTED,
|
|
14442
|
+
confidence: (0, import_types47.confidenceForExtracted)("structural"),
|
|
13772
14443
|
evidence: { file: evidenceFile }
|
|
13773
14444
|
};
|
|
13774
14445
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13780,9 +14451,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13780
14451
|
|
|
13781
14452
|
// src/extract/infra/dockerfile.ts
|
|
13782
14453
|
init_cjs_shims();
|
|
13783
|
-
var
|
|
13784
|
-
var
|
|
13785
|
-
var
|
|
14454
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14455
|
+
var import_node_fs25 = require("fs");
|
|
14456
|
+
var import_types48 = require("@neat.is/types");
|
|
13786
14457
|
function readDockerfile(content) {
|
|
13787
14458
|
let image = null;
|
|
13788
14459
|
const ports = [];
|
|
@@ -13811,15 +14482,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13811
14482
|
let nodesAdded = 0;
|
|
13812
14483
|
let edgesAdded = 0;
|
|
13813
14484
|
for (const service of services) {
|
|
13814
|
-
const dockerfilePath =
|
|
14485
|
+
const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
|
|
13815
14486
|
if (!await exists(dockerfilePath)) continue;
|
|
13816
14487
|
let content;
|
|
13817
14488
|
try {
|
|
13818
|
-
content = await
|
|
14489
|
+
content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
|
|
13819
14490
|
} catch (err) {
|
|
13820
14491
|
recordExtractionError(
|
|
13821
14492
|
"infra dockerfile",
|
|
13822
|
-
|
|
14493
|
+
import_node_path58.default.relative(scanPath, dockerfilePath),
|
|
13823
14494
|
err
|
|
13824
14495
|
);
|
|
13825
14496
|
continue;
|
|
@@ -13831,8 +14502,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13831
14502
|
graph.addNode(node.id, node);
|
|
13832
14503
|
nodesAdded++;
|
|
13833
14504
|
}
|
|
13834
|
-
const relDockerfile = toPosix(
|
|
13835
|
-
const evidenceFile = toPosix(
|
|
14505
|
+
const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
|
|
14506
|
+
const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
|
|
13836
14507
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
13837
14508
|
graph,
|
|
13838
14509
|
service.pkg.name,
|
|
@@ -13841,15 +14512,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13841
14512
|
);
|
|
13842
14513
|
nodesAdded += fn;
|
|
13843
14514
|
edgesAdded += fe;
|
|
13844
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
14515
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
|
|
13845
14516
|
if (!graph.hasEdge(edgeId)) {
|
|
13846
14517
|
const edge = {
|
|
13847
14518
|
id: edgeId,
|
|
13848
14519
|
source: fileNodeId,
|
|
13849
14520
|
target: node.id,
|
|
13850
|
-
type:
|
|
13851
|
-
provenance:
|
|
13852
|
-
confidence: (0,
|
|
14521
|
+
type: import_types48.EdgeType.RUNS_ON,
|
|
14522
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14523
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13853
14524
|
evidence: {
|
|
13854
14525
|
file: evidenceFile,
|
|
13855
14526
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -13864,15 +14535,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13864
14535
|
graph.addNode(portNode.id, portNode);
|
|
13865
14536
|
nodesAdded++;
|
|
13866
14537
|
}
|
|
13867
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
14538
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
|
|
13868
14539
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
13869
14540
|
const portEdge = {
|
|
13870
14541
|
id: portEdgeId,
|
|
13871
14542
|
source: fileNodeId,
|
|
13872
14543
|
target: portNode.id,
|
|
13873
|
-
type:
|
|
13874
|
-
provenance:
|
|
13875
|
-
confidence: (0,
|
|
14544
|
+
type: import_types48.EdgeType.CONNECTS_TO,
|
|
14545
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14546
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13876
14547
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
13877
14548
|
};
|
|
13878
14549
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -13884,23 +14555,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13884
14555
|
|
|
13885
14556
|
// src/extract/infra/terraform.ts
|
|
13886
14557
|
init_cjs_shims();
|
|
13887
|
-
var
|
|
13888
|
-
var
|
|
13889
|
-
var
|
|
14558
|
+
var import_node_fs26 = require("fs");
|
|
14559
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14560
|
+
var import_types49 = require("@neat.is/types");
|
|
13890
14561
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
13891
14562
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
13892
14563
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
13893
14564
|
if (depth > max) return [];
|
|
13894
14565
|
const out = [];
|
|
13895
|
-
const entries = await
|
|
14566
|
+
const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13896
14567
|
for (const entry of entries) {
|
|
13897
14568
|
if (entry.isDirectory()) {
|
|
13898
14569
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
13899
|
-
const child =
|
|
14570
|
+
const child = import_node_path59.default.join(start, entry.name);
|
|
13900
14571
|
if (await isPythonVenvDir(child)) continue;
|
|
13901
14572
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
13902
14573
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
13903
|
-
out.push(
|
|
14574
|
+
out.push(import_node_path59.default.join(start, entry.name));
|
|
13904
14575
|
}
|
|
13905
14576
|
}
|
|
13906
14577
|
return out;
|
|
@@ -13931,8 +14602,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13931
14602
|
let edgesAdded = 0;
|
|
13932
14603
|
const files = await walkTfFiles(scanPath);
|
|
13933
14604
|
for (const file of files) {
|
|
13934
|
-
const content = await
|
|
13935
|
-
const evidenceFile = toPosix(
|
|
14605
|
+
const content = await import_node_fs26.promises.readFile(file, "utf8");
|
|
14606
|
+
const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
|
|
13936
14607
|
const resources = [];
|
|
13937
14608
|
const byKey = /* @__PURE__ */ new Map();
|
|
13938
14609
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -13967,16 +14638,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13967
14638
|
if (!target) continue;
|
|
13968
14639
|
if (seen.has(target.nodeId)) continue;
|
|
13969
14640
|
seen.add(target.nodeId);
|
|
13970
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
14641
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
|
|
13971
14642
|
if (graph.hasEdge(edgeId)) continue;
|
|
13972
14643
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
13973
14644
|
const edge = {
|
|
13974
14645
|
id: edgeId,
|
|
13975
14646
|
source: resource.nodeId,
|
|
13976
14647
|
target: target.nodeId,
|
|
13977
|
-
type:
|
|
13978
|
-
provenance:
|
|
13979
|
-
confidence: (0,
|
|
14648
|
+
type: import_types49.EdgeType.DEPENDS_ON,
|
|
14649
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
14650
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
13980
14651
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
13981
14652
|
};
|
|
13982
14653
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13989,8 +14660,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13989
14660
|
|
|
13990
14661
|
// src/extract/infra/k8s.ts
|
|
13991
14662
|
init_cjs_shims();
|
|
13992
|
-
var
|
|
13993
|
-
var
|
|
14663
|
+
var import_node_fs27 = require("fs");
|
|
14664
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
13994
14665
|
var import_yaml3 = require("yaml");
|
|
13995
14666
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
13996
14667
|
Service: "k8s-service",
|
|
@@ -14004,15 +14675,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
14004
14675
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
14005
14676
|
if (depth > max) return [];
|
|
14006
14677
|
const out = [];
|
|
14007
|
-
const entries = await
|
|
14678
|
+
const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
14008
14679
|
for (const entry of entries) {
|
|
14009
14680
|
if (entry.isDirectory()) {
|
|
14010
14681
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
14011
|
-
const child =
|
|
14682
|
+
const child = import_node_path60.default.join(start, entry.name);
|
|
14012
14683
|
if (await isPythonVenvDir(child)) continue;
|
|
14013
14684
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
14014
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
14015
|
-
out.push(
|
|
14685
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
|
|
14686
|
+
out.push(import_node_path60.default.join(start, entry.name));
|
|
14016
14687
|
}
|
|
14017
14688
|
}
|
|
14018
14689
|
return out;
|
|
@@ -14021,7 +14692,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
14021
14692
|
let nodesAdded = 0;
|
|
14022
14693
|
const files = await walkYamlFiles2(scanPath);
|
|
14023
14694
|
for (const file of files) {
|
|
14024
|
-
const content = await
|
|
14695
|
+
const content = await import_node_fs27.promises.readFile(file, "utf8");
|
|
14025
14696
|
let docs;
|
|
14026
14697
|
try {
|
|
14027
14698
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -14045,16 +14716,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
14045
14716
|
|
|
14046
14717
|
// src/extract/infra/cloudflare.ts
|
|
14047
14718
|
init_cjs_shims();
|
|
14048
|
-
var
|
|
14049
|
-
var
|
|
14719
|
+
var import_node_fs28 = require("fs");
|
|
14720
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
14050
14721
|
var import_smol_toml3 = require("smol-toml");
|
|
14051
|
-
var
|
|
14722
|
+
var import_types50 = require("@neat.is/types");
|
|
14052
14723
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
14053
14724
|
async function readWranglerConfig(dir) {
|
|
14054
14725
|
for (const filename of WRANGLER_FILENAMES) {
|
|
14055
|
-
const abs =
|
|
14726
|
+
const abs = import_node_path61.default.join(dir, filename);
|
|
14056
14727
|
if (!await exists(abs)) continue;
|
|
14057
|
-
const raw = await
|
|
14728
|
+
const raw = await import_node_fs28.promises.readFile(abs, "utf8");
|
|
14058
14729
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14059
14730
|
return { config, relFile: filename, raw };
|
|
14060
14731
|
}
|
|
@@ -14096,8 +14767,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
14096
14767
|
source: anchorId,
|
|
14097
14768
|
target: node.id,
|
|
14098
14769
|
type: edgeType,
|
|
14099
|
-
provenance:
|
|
14100
|
-
confidence: (0,
|
|
14770
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14771
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14101
14772
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
14102
14773
|
};
|
|
14103
14774
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14115,11 +14786,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14115
14786
|
try {
|
|
14116
14787
|
read = await readWranglerConfig(service.dir);
|
|
14117
14788
|
} catch (err) {
|
|
14118
|
-
recordExtractionError("infra cloudflare",
|
|
14789
|
+
recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
|
|
14119
14790
|
continue;
|
|
14120
14791
|
}
|
|
14121
14792
|
if (!read || !read.config.name) continue;
|
|
14122
|
-
const evidenceFile = toPosix(
|
|
14793
|
+
const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
|
|
14123
14794
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
14124
14795
|
}
|
|
14125
14796
|
for (const worker of discovered) {
|
|
@@ -14131,7 +14802,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14131
14802
|
}
|
|
14132
14803
|
let anchorId = service.node.id;
|
|
14133
14804
|
if (config.main) {
|
|
14134
|
-
const entryRelPath = toPosix(
|
|
14805
|
+
const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
|
|
14135
14806
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
14136
14807
|
graph,
|
|
14137
14808
|
service.pkg.name,
|
|
@@ -14158,15 +14829,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14158
14829
|
nodesAdded++;
|
|
14159
14830
|
}
|
|
14160
14831
|
if (runtimeNode.id !== anchorId) {
|
|
14161
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
14832
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
|
|
14162
14833
|
if (!graph.hasEdge(runsOnId)) {
|
|
14163
14834
|
const edge = {
|
|
14164
14835
|
id: runsOnId,
|
|
14165
14836
|
source: anchorId,
|
|
14166
14837
|
target: runtimeNode.id,
|
|
14167
|
-
type:
|
|
14168
|
-
provenance:
|
|
14169
|
-
confidence: (0,
|
|
14838
|
+
type: import_types50.EdgeType.RUNS_ON,
|
|
14839
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14840
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14170
14841
|
evidence: {
|
|
14171
14842
|
file: evidenceFile,
|
|
14172
14843
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -14180,7 +14851,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14180
14851
|
const result = addResourceEdge(
|
|
14181
14852
|
graph,
|
|
14182
14853
|
anchorId,
|
|
14183
|
-
|
|
14854
|
+
import_types50.EdgeType.CONNECTS_TO,
|
|
14184
14855
|
"cloudflare-route",
|
|
14185
14856
|
route,
|
|
14186
14857
|
evidenceFile,
|
|
@@ -14204,7 +14875,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14204
14875
|
const result = addResourceEdge(
|
|
14205
14876
|
graph,
|
|
14206
14877
|
anchorId,
|
|
14207
|
-
|
|
14878
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14208
14879
|
group.kind,
|
|
14209
14880
|
name,
|
|
14210
14881
|
evidenceFile,
|
|
@@ -14218,7 +14889,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14218
14889
|
const result = addResourceEdge(
|
|
14219
14890
|
graph,
|
|
14220
14891
|
anchorId,
|
|
14221
|
-
|
|
14892
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14222
14893
|
"cloudflare-cron",
|
|
14223
14894
|
cron,
|
|
14224
14895
|
evidenceFile,
|
|
@@ -14231,7 +14902,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14231
14902
|
const result = addResourceEdge(
|
|
14232
14903
|
graph,
|
|
14233
14904
|
anchorId,
|
|
14234
|
-
|
|
14905
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14235
14906
|
"cloudflare-env-var",
|
|
14236
14907
|
varName,
|
|
14237
14908
|
evidenceFile,
|
|
@@ -14244,15 +14915,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14244
14915
|
if (!svc.service) continue;
|
|
14245
14916
|
const target = workerIndex.get(svc.service);
|
|
14246
14917
|
if (target && target.anchorId !== anchorId) {
|
|
14247
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
14918
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
|
|
14248
14919
|
if (!graph.hasEdge(edgeId)) {
|
|
14249
14920
|
const edge = {
|
|
14250
14921
|
id: edgeId,
|
|
14251
14922
|
source: anchorId,
|
|
14252
14923
|
target: target.anchorId,
|
|
14253
|
-
type:
|
|
14254
|
-
provenance:
|
|
14255
|
-
confidence: (0,
|
|
14924
|
+
type: import_types50.EdgeType.CALLS,
|
|
14925
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14926
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14256
14927
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
14257
14928
|
};
|
|
14258
14929
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14263,7 +14934,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14263
14934
|
const result = addResourceEdge(
|
|
14264
14935
|
graph,
|
|
14265
14936
|
anchorId,
|
|
14266
|
-
|
|
14937
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14267
14938
|
"cloudflare-service-binding",
|
|
14268
14939
|
svc.service,
|
|
14269
14940
|
evidenceFile,
|
|
@@ -14278,24 +14949,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14278
14949
|
|
|
14279
14950
|
// src/extract/infra/vercel.ts
|
|
14280
14951
|
init_cjs_shims();
|
|
14281
|
-
var
|
|
14282
|
-
var
|
|
14283
|
-
var
|
|
14952
|
+
var import_node_fs29 = require("fs");
|
|
14953
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
14954
|
+
var import_types51 = require("@neat.is/types");
|
|
14284
14955
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
14285
14956
|
async function readVercelConfig(dir) {
|
|
14286
14957
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
14287
|
-
const abs =
|
|
14958
|
+
const abs = import_node_path62.default.join(dir, filename);
|
|
14288
14959
|
if (!await exists(abs)) continue;
|
|
14289
|
-
const raw = await
|
|
14960
|
+
const raw = await import_node_fs29.promises.readFile(abs, "utf8");
|
|
14290
14961
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
14291
14962
|
return { config, relFile: filename, raw };
|
|
14292
14963
|
}
|
|
14293
14964
|
return null;
|
|
14294
14965
|
}
|
|
14295
14966
|
async function readLinkedProjectName(dir) {
|
|
14296
|
-
const abs =
|
|
14967
|
+
const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
|
|
14297
14968
|
if (!await exists(abs)) return void 0;
|
|
14298
|
-
const parsed = JSON.parse(await
|
|
14969
|
+
const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
|
|
14299
14970
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
14300
14971
|
}
|
|
14301
14972
|
function routeSource(route) {
|
|
@@ -14311,7 +14982,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14311
14982
|
read = await readVercelConfig(service.dir);
|
|
14312
14983
|
projectName = await readLinkedProjectName(service.dir);
|
|
14313
14984
|
} catch (err) {
|
|
14314
|
-
recordExtractionError("infra vercel",
|
|
14985
|
+
recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
|
|
14315
14986
|
continue;
|
|
14316
14987
|
}
|
|
14317
14988
|
if (!read && !projectName) continue;
|
|
@@ -14327,7 +14998,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14327
14998
|
const anchorId = service.node.id;
|
|
14328
14999
|
if (!read) continue;
|
|
14329
15000
|
const { config, relFile, raw } = read;
|
|
14330
|
-
const evidenceFile = toPosix(
|
|
15001
|
+
const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
|
|
14331
15002
|
const add = (edgeType, kind, name) => {
|
|
14332
15003
|
if (!name) return;
|
|
14333
15004
|
const result = emitPlatformResourceEdge(
|
|
@@ -14343,12 +15014,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14343
15014
|
nodesAdded += result.nodesAdded;
|
|
14344
15015
|
edgesAdded += result.edgesAdded;
|
|
14345
15016
|
};
|
|
14346
|
-
add(
|
|
14347
|
-
for (const cron of config.crons ?? []) add(
|
|
14348
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
14349
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
15017
|
+
add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
15018
|
+
for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
15019
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
15020
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
14350
15021
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
14351
|
-
add(
|
|
15022
|
+
add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
14352
15023
|
}
|
|
14353
15024
|
}
|
|
14354
15025
|
return { nodesAdded, edgesAdded };
|
|
@@ -14356,16 +15027,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14356
15027
|
|
|
14357
15028
|
// src/extract/infra/railway.ts
|
|
14358
15029
|
init_cjs_shims();
|
|
14359
|
-
var
|
|
14360
|
-
var
|
|
15030
|
+
var import_node_fs30 = require("fs");
|
|
15031
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
14361
15032
|
var import_smol_toml4 = require("smol-toml");
|
|
14362
|
-
var
|
|
15033
|
+
var import_types52 = require("@neat.is/types");
|
|
14363
15034
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
14364
15035
|
async function readRailwayConfig(dir) {
|
|
14365
15036
|
for (const filename of RAILWAY_FILENAMES) {
|
|
14366
|
-
const abs =
|
|
15037
|
+
const abs = import_node_path63.default.join(dir, filename);
|
|
14367
15038
|
if (!await exists(abs)) continue;
|
|
14368
|
-
const raw = await
|
|
15039
|
+
const raw = await import_node_fs30.promises.readFile(abs, "utf8");
|
|
14369
15040
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14370
15041
|
return { config, relFile: filename, raw };
|
|
14371
15042
|
}
|
|
@@ -14379,7 +15050,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14379
15050
|
try {
|
|
14380
15051
|
read = await readRailwayConfig(service.dir);
|
|
14381
15052
|
} catch (err) {
|
|
14382
|
-
recordExtractionError("infra railway",
|
|
15053
|
+
recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
|
|
14383
15054
|
continue;
|
|
14384
15055
|
}
|
|
14385
15056
|
if (!read) continue;
|
|
@@ -14389,7 +15060,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14389
15060
|
}
|
|
14390
15061
|
const anchorId = service.node.id;
|
|
14391
15062
|
const { config, relFile, raw } = read;
|
|
14392
|
-
const evidenceFile = toPosix(
|
|
15063
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
|
|
14393
15064
|
const add = (edgeType, kind, name) => {
|
|
14394
15065
|
if (!name) return;
|
|
14395
15066
|
const result = emitPlatformResourceEdge(
|
|
@@ -14405,24 +15076,24 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14405
15076
|
nodesAdded += result.nodesAdded;
|
|
14406
15077
|
edgesAdded += result.edgesAdded;
|
|
14407
15078
|
};
|
|
14408
|
-
add(
|
|
14409
|
-
add(
|
|
14410
|
-
add(
|
|
15079
|
+
add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
|
|
15080
|
+
add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
15081
|
+
add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
14411
15082
|
}
|
|
14412
15083
|
return { nodesAdded, edgesAdded };
|
|
14413
15084
|
}
|
|
14414
15085
|
|
|
14415
15086
|
// src/extract/infra/supabase.ts
|
|
14416
15087
|
init_cjs_shims();
|
|
14417
|
-
var
|
|
14418
|
-
var
|
|
15088
|
+
var import_node_fs31 = require("fs");
|
|
15089
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
14419
15090
|
var import_smol_toml5 = require("smol-toml");
|
|
14420
|
-
var
|
|
15091
|
+
var import_types53 = require("@neat.is/types");
|
|
14421
15092
|
async function readSupabaseConfig(dir) {
|
|
14422
|
-
const relFile =
|
|
14423
|
-
const abs =
|
|
15093
|
+
const relFile = import_node_path64.default.join("supabase", "config.toml");
|
|
15094
|
+
const abs = import_node_path64.default.join(dir, relFile);
|
|
14424
15095
|
if (!await exists(abs)) return null;
|
|
14425
|
-
const raw = await
|
|
15096
|
+
const raw = await import_node_fs31.promises.readFile(abs, "utf8");
|
|
14426
15097
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
14427
15098
|
return { config, relFile, raw };
|
|
14428
15099
|
}
|
|
@@ -14434,7 +15105,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14434
15105
|
try {
|
|
14435
15106
|
read = await readSupabaseConfig(service.dir);
|
|
14436
15107
|
} catch (err) {
|
|
14437
|
-
recordExtractionError("infra supabase",
|
|
15108
|
+
recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
|
|
14438
15109
|
continue;
|
|
14439
15110
|
}
|
|
14440
15111
|
if (!read) continue;
|
|
@@ -14449,7 +15120,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14449
15120
|
});
|
|
14450
15121
|
}
|
|
14451
15122
|
const anchorId = service.node.id;
|
|
14452
|
-
const evidenceFile = toPosix(
|
|
15123
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
|
|
14453
15124
|
const add = (edgeType, kind, name) => {
|
|
14454
15125
|
if (!name) return;
|
|
14455
15126
|
const result = emitPlatformResourceEdge(
|
|
@@ -14465,10 +15136,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14465
15136
|
nodesAdded += result.nodesAdded;
|
|
14466
15137
|
edgesAdded += result.edgesAdded;
|
|
14467
15138
|
};
|
|
14468
|
-
add(
|
|
14469
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
14470
|
-
if (config.storage) add(
|
|
14471
|
-
if (config.auth) add(
|
|
15139
|
+
add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
15140
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
15141
|
+
if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
15142
|
+
if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
14472
15143
|
}
|
|
14473
15144
|
return { nodesAdded, edgesAdded };
|
|
14474
15145
|
}
|
|
@@ -14491,14 +15162,14 @@ async function addInfra(graph, scanPath, services) {
|
|
|
14491
15162
|
|
|
14492
15163
|
// src/extract/zod-shapes.ts
|
|
14493
15164
|
init_cjs_shims();
|
|
14494
|
-
var
|
|
14495
|
-
var
|
|
15165
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15166
|
+
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
14496
15167
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
14497
|
-
var
|
|
15168
|
+
var import_types54 = require("@neat.is/types");
|
|
14498
15169
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
14499
15170
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
14500
15171
|
function parserForExt4(ext) {
|
|
14501
|
-
const p = new
|
|
15172
|
+
const p = new import_tree_sitter17.default();
|
|
14502
15173
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
14503
15174
|
return p;
|
|
14504
15175
|
}
|
|
@@ -14586,7 +15257,7 @@ function topLevelSchemas(root) {
|
|
|
14586
15257
|
}
|
|
14587
15258
|
function zodShapesFromFile(file, serviceDir) {
|
|
14588
15259
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
14589
|
-
const tree = parseSource3(parserForExt4(
|
|
15260
|
+
const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
|
|
14590
15261
|
const out = [];
|
|
14591
15262
|
const seen = /* @__PURE__ */ new Set();
|
|
14592
15263
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14600,11 +15271,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
14600
15271
|
seen.add(name);
|
|
14601
15272
|
const line = call.startPosition.row + 1;
|
|
14602
15273
|
out.push({
|
|
14603
|
-
infraId: (0,
|
|
15274
|
+
infraId: (0, import_types54.infraId)("zod-schema", name),
|
|
14604
15275
|
name,
|
|
14605
15276
|
fields,
|
|
14606
15277
|
evidence: {
|
|
14607
|
-
file:
|
|
15278
|
+
file: import_node_path65.default.relative(serviceDir, file.path),
|
|
14608
15279
|
line,
|
|
14609
15280
|
snippet: snippet(file.content, line)
|
|
14610
15281
|
}
|
|
@@ -14635,7 +15306,7 @@ async function addZodShapes(graph, services) {
|
|
|
14635
15306
|
if (!graph.hasNode(shape.infraId)) {
|
|
14636
15307
|
const node = {
|
|
14637
15308
|
id: shape.infraId,
|
|
14638
|
-
type:
|
|
15309
|
+
type: import_types54.NodeType.InfraNode,
|
|
14639
15310
|
name: shape.name,
|
|
14640
15311
|
provider: "self",
|
|
14641
15312
|
kind: "zod-schema"
|
|
@@ -14645,14 +15316,14 @@ async function addZodShapes(graph, services) {
|
|
|
14645
15316
|
}
|
|
14646
15317
|
if (shape.fields.length > 0) {
|
|
14647
15318
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
14648
|
-
if (node.type ===
|
|
15319
|
+
if (node.type === import_types54.NodeType.InfraNode) {
|
|
14649
15320
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
14650
15321
|
...node,
|
|
14651
15322
|
columns: foldColumns(
|
|
14652
15323
|
node.columns,
|
|
14653
15324
|
shape.fields,
|
|
14654
|
-
|
|
14655
|
-
(0,
|
|
15325
|
+
import_types54.Provenance.EXTRACTED,
|
|
15326
|
+
(0, import_types54.confidenceForExtracted)("structural")
|
|
14656
15327
|
)
|
|
14657
15328
|
});
|
|
14658
15329
|
}
|
|
@@ -14666,15 +15337,15 @@ async function addZodShapes(graph, services) {
|
|
|
14666
15337
|
);
|
|
14667
15338
|
nodesAdded += n;
|
|
14668
15339
|
edgesAdded += e;
|
|
14669
|
-
const edgeId = (0,
|
|
15340
|
+
const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
|
|
14670
15341
|
if (!graph.hasEdge(edgeId)) {
|
|
14671
15342
|
const edge = {
|
|
14672
15343
|
id: edgeId,
|
|
14673
15344
|
source: fileNodeId,
|
|
14674
15345
|
target: shape.infraId,
|
|
14675
|
-
type:
|
|
14676
|
-
provenance:
|
|
14677
|
-
confidence: (0,
|
|
15346
|
+
type: import_types54.EdgeType.CONTAINS,
|
|
15347
|
+
provenance: import_types54.Provenance.EXTRACTED,
|
|
15348
|
+
confidence: (0, import_types54.confidenceForExtracted)("structural"),
|
|
14678
15349
|
evidence: shape.evidence
|
|
14679
15350
|
};
|
|
14680
15351
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -14688,7 +15359,7 @@ async function addZodShapes(graph, services) {
|
|
|
14688
15359
|
|
|
14689
15360
|
// src/extract/firestore-rules.ts
|
|
14690
15361
|
init_cjs_shims();
|
|
14691
|
-
var
|
|
15362
|
+
var import_types55 = require("@neat.is/types");
|
|
14692
15363
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
14693
15364
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
14694
15365
|
function stripComments(src) {
|
|
@@ -14828,7 +15499,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
14828
15499
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
14829
15500
|
graph.forEachNode((id, attrs) => {
|
|
14830
15501
|
const node = attrs;
|
|
14831
|
-
if (node.type !==
|
|
15502
|
+
if (node.type !== import_types55.NodeType.InfraNode) return;
|
|
14832
15503
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
14833
15504
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
14834
15505
|
if (!fields || fields.size === 0) return;
|
|
@@ -14841,17 +15512,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
14841
15512
|
}
|
|
14842
15513
|
|
|
14843
15514
|
// src/extract/index.ts
|
|
14844
|
-
var
|
|
15515
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
14845
15516
|
|
|
14846
15517
|
// src/extract/retire.ts
|
|
14847
15518
|
init_cjs_shims();
|
|
14848
|
-
var
|
|
14849
|
-
var
|
|
14850
|
-
var
|
|
15519
|
+
var import_node_fs32 = require("fs");
|
|
15520
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
15521
|
+
var import_types56 = require("@neat.is/types");
|
|
14851
15522
|
function dropOrphanedFileNodes(graph) {
|
|
14852
15523
|
const orphans = [];
|
|
14853
15524
|
graph.forEachNode((id, attrs) => {
|
|
14854
|
-
if (attrs.type !==
|
|
15525
|
+
if (attrs.type !== import_types56.NodeType.FileNode) return;
|
|
14855
15526
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
14856
15527
|
orphans.push(id);
|
|
14857
15528
|
}
|
|
@@ -14864,14 +15535,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
14864
15535
|
const bases = [scanPath, ...serviceDirs];
|
|
14865
15536
|
graph.forEachEdge((id, attrs) => {
|
|
14866
15537
|
const edge = attrs;
|
|
14867
|
-
if (edge.provenance !==
|
|
15538
|
+
if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
|
|
14868
15539
|
const evidenceFile = edge.evidence?.file;
|
|
14869
15540
|
if (!evidenceFile) return;
|
|
14870
|
-
if (
|
|
14871
|
-
if (!(0,
|
|
15541
|
+
if (import_node_path66.default.isAbsolute(evidenceFile)) {
|
|
15542
|
+
if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
|
|
14872
15543
|
return;
|
|
14873
15544
|
}
|
|
14874
|
-
const found = bases.some((base) => (0,
|
|
15545
|
+
const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
|
|
14875
15546
|
if (!found) toDrop.push(id);
|
|
14876
15547
|
});
|
|
14877
15548
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -14928,7 +15599,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14928
15599
|
}
|
|
14929
15600
|
const droppedEntries = drainDroppedExtracted();
|
|
14930
15601
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
14931
|
-
const rejectedPath =
|
|
15602
|
+
const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
14932
15603
|
try {
|
|
14933
15604
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
14934
15605
|
} catch (err) {
|
|
@@ -14962,9 +15633,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14962
15633
|
|
|
14963
15634
|
// src/persist.ts
|
|
14964
15635
|
init_cjs_shims();
|
|
14965
|
-
var
|
|
14966
|
-
var
|
|
14967
|
-
var
|
|
15636
|
+
var import_node_fs33 = require("fs");
|
|
15637
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
15638
|
+
var import_types57 = require("@neat.is/types");
|
|
14968
15639
|
var SCHEMA_VERSION = 7;
|
|
14969
15640
|
function migrateV1ToV2(payload) {
|
|
14970
15641
|
const nodes = payload.graph.nodes;
|
|
@@ -14988,7 +15659,7 @@ function migrateV5ToV6(payload) {
|
|
|
14988
15659
|
if (Array.isArray(nodes)) {
|
|
14989
15660
|
for (const node of nodes) {
|
|
14990
15661
|
const attrs = node.attributes;
|
|
14991
|
-
if (!attrs || attrs.type !==
|
|
15662
|
+
if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
|
|
14992
15663
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
14993
15664
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
14994
15665
|
}
|
|
@@ -15004,12 +15675,12 @@ function migrateV2ToV3(payload) {
|
|
|
15004
15675
|
for (const edge of edges) {
|
|
15005
15676
|
const attrs = edge.attributes;
|
|
15006
15677
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
15007
|
-
attrs.provenance =
|
|
15678
|
+
attrs.provenance = import_types57.Provenance.OBSERVED;
|
|
15008
15679
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
15009
15680
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
15010
15681
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
15011
15682
|
if (type && source && target) {
|
|
15012
|
-
const newId = (0,
|
|
15683
|
+
const newId = (0, import_types57.observedEdgeId)(source, target, type);
|
|
15013
15684
|
attrs.id = newId;
|
|
15014
15685
|
if (edge.key) edge.key = newId;
|
|
15015
15686
|
}
|
|
@@ -15018,7 +15689,7 @@ function migrateV2ToV3(payload) {
|
|
|
15018
15689
|
return { ...payload, schemaVersion: 3 };
|
|
15019
15690
|
}
|
|
15020
15691
|
async function ensureDir(filePath) {
|
|
15021
|
-
await
|
|
15692
|
+
await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
|
|
15022
15693
|
}
|
|
15023
15694
|
async function saveGraphToDisk(graph, outPath) {
|
|
15024
15695
|
await ensureDir(outPath);
|
|
@@ -15028,13 +15699,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
15028
15699
|
graph: graph.export()
|
|
15029
15700
|
};
|
|
15030
15701
|
const tmp = `${outPath}.tmp`;
|
|
15031
|
-
await
|
|
15032
|
-
await
|
|
15702
|
+
await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
15703
|
+
await import_node_fs33.promises.rename(tmp, outPath);
|
|
15033
15704
|
}
|
|
15034
15705
|
async function loadGraphFromDisk(graph, outPath) {
|
|
15035
15706
|
let raw;
|
|
15036
15707
|
try {
|
|
15037
|
-
raw = await
|
|
15708
|
+
raw = await import_node_fs33.promises.readFile(outPath, "utf8");
|
|
15038
15709
|
} catch (err) {
|
|
15039
15710
|
if (err.code === "ENOENT") return;
|
|
15040
15711
|
throw err;
|
|
@@ -15110,19 +15781,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
15110
15781
|
init_cjs_shims();
|
|
15111
15782
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15112
15783
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15113
|
-
var
|
|
15784
|
+
var import_types92 = require("@neat.is/types");
|
|
15114
15785
|
|
|
15115
15786
|
// src/extend/index.ts
|
|
15116
15787
|
init_cjs_shims();
|
|
15117
|
-
var
|
|
15118
|
-
var
|
|
15788
|
+
var import_node_fs35 = require("fs");
|
|
15789
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
15119
15790
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
15120
15791
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
15121
15792
|
|
|
15122
15793
|
// src/installers/package-manager.ts
|
|
15123
15794
|
init_cjs_shims();
|
|
15124
|
-
var
|
|
15125
|
-
var
|
|
15795
|
+
var import_node_fs34 = require("fs");
|
|
15796
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
15126
15797
|
var import_node_child_process = require("child_process");
|
|
15127
15798
|
var LOCKFILE_PRIORITY = [
|
|
15128
15799
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -15137,29 +15808,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
15137
15808
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
15138
15809
|
async function exists2(p) {
|
|
15139
15810
|
try {
|
|
15140
|
-
await
|
|
15811
|
+
await import_node_fs34.promises.access(p);
|
|
15141
15812
|
return true;
|
|
15142
15813
|
} catch {
|
|
15143
15814
|
return false;
|
|
15144
15815
|
}
|
|
15145
15816
|
}
|
|
15146
15817
|
async function detectPackageManager(serviceDir) {
|
|
15147
|
-
let dir =
|
|
15818
|
+
let dir = import_node_path69.default.resolve(serviceDir);
|
|
15148
15819
|
const stops = /* @__PURE__ */ new Set();
|
|
15149
15820
|
for (let i = 0; i < 64; i++) {
|
|
15150
15821
|
if (stops.has(dir)) break;
|
|
15151
15822
|
stops.add(dir);
|
|
15152
15823
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
15153
|
-
const lockPath =
|
|
15824
|
+
const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
|
|
15154
15825
|
if (await exists2(lockPath)) {
|
|
15155
15826
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
15156
15827
|
}
|
|
15157
15828
|
}
|
|
15158
|
-
const parent =
|
|
15829
|
+
const parent = import_node_path69.default.dirname(dir);
|
|
15159
15830
|
if (parent === dir) break;
|
|
15160
15831
|
dir = parent;
|
|
15161
15832
|
}
|
|
15162
|
-
return { pm: "npm", cwd:
|
|
15833
|
+
return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
15163
15834
|
}
|
|
15164
15835
|
async function runPackageManagerInstall(cmd) {
|
|
15165
15836
|
return new Promise((resolve) => {
|
|
@@ -15201,15 +15872,15 @@ ${err.message}`
|
|
|
15201
15872
|
// src/extend/index.ts
|
|
15202
15873
|
async function fileExists2(p) {
|
|
15203
15874
|
try {
|
|
15204
|
-
await
|
|
15875
|
+
await import_node_fs35.promises.access(p);
|
|
15205
15876
|
return true;
|
|
15206
15877
|
} catch {
|
|
15207
15878
|
return false;
|
|
15208
15879
|
}
|
|
15209
15880
|
}
|
|
15210
15881
|
async function readPackageJson(scanPath) {
|
|
15211
|
-
const pkgPath =
|
|
15212
|
-
const raw = await
|
|
15882
|
+
const pkgPath = import_node_path70.default.join(scanPath, "package.json");
|
|
15883
|
+
const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
|
|
15213
15884
|
return JSON.parse(raw);
|
|
15214
15885
|
}
|
|
15215
15886
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -15222,27 +15893,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
15222
15893
|
]);
|
|
15223
15894
|
async function findHookFiles(scanPath) {
|
|
15224
15895
|
const found = [];
|
|
15225
|
-
const
|
|
15226
|
-
const entries = await
|
|
15896
|
+
const walk10 = async (dir) => {
|
|
15897
|
+
const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
15227
15898
|
for (const entry of entries) {
|
|
15228
15899
|
if (entry.isDirectory()) {
|
|
15229
15900
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
15230
|
-
await
|
|
15901
|
+
await walk10(import_node_path70.default.join(dir, entry.name));
|
|
15231
15902
|
} else if (entry.isFile()) {
|
|
15232
15903
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
15233
|
-
const rel =
|
|
15234
|
-
found.push(rel.split(
|
|
15904
|
+
const rel = import_node_path70.default.relative(scanPath, import_node_path70.default.join(dir, entry.name));
|
|
15905
|
+
found.push(rel.split(import_node_path70.default.sep).join("/"));
|
|
15235
15906
|
}
|
|
15236
15907
|
}
|
|
15237
15908
|
}
|
|
15238
15909
|
};
|
|
15239
|
-
await
|
|
15910
|
+
await walk10(scanPath);
|
|
15240
15911
|
return found.sort();
|
|
15241
15912
|
}
|
|
15242
15913
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
15243
15914
|
let fallback = null;
|
|
15244
15915
|
for (const file of hookFiles) {
|
|
15245
|
-
const content = await
|
|
15916
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
|
|
15246
15917
|
const patched = splicedContent(content, snippet2);
|
|
15247
15918
|
if (patched !== null) return { file, content, patched };
|
|
15248
15919
|
if (fallback === null) fallback = { file, content };
|
|
@@ -15250,12 +15921,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
15250
15921
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
15251
15922
|
}
|
|
15252
15923
|
function extendLogPath() {
|
|
15253
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
15924
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path70.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
15254
15925
|
}
|
|
15255
15926
|
async function appendExtendLog(entry) {
|
|
15256
15927
|
const logPath = extendLogPath();
|
|
15257
|
-
await
|
|
15258
|
-
await
|
|
15928
|
+
await import_node_fs35.promises.mkdir(import_node_path70.default.dirname(logPath), { recursive: true });
|
|
15929
|
+
await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
15259
15930
|
}
|
|
15260
15931
|
function splicedContent(fileContent, snippet2) {
|
|
15261
15932
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -15313,7 +15984,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
15313
15984
|
}
|
|
15314
15985
|
async function describeProjectInstrumentation(ctx) {
|
|
15315
15986
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15316
|
-
const envNeat = await fileExists2(
|
|
15987
|
+
const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
|
|
15317
15988
|
const registryInstrPackages = new Set(
|
|
15318
15989
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
15319
15990
|
);
|
|
@@ -15335,7 +16006,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
15335
16006
|
);
|
|
15336
16007
|
}
|
|
15337
16008
|
for (const file of hookFiles) {
|
|
15338
|
-
const content = await
|
|
16009
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
|
|
15339
16010
|
if (content.includes(args.registration_snippet)) {
|
|
15340
16011
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
15341
16012
|
}
|
|
@@ -15347,18 +16018,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
15347
16018
|
);
|
|
15348
16019
|
}
|
|
15349
16020
|
const primaryFile = primary.file;
|
|
15350
|
-
const primaryPath =
|
|
16021
|
+
const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
|
|
15351
16022
|
const filesTouched = [];
|
|
15352
16023
|
const depsAdded = [];
|
|
15353
|
-
const pkgPath =
|
|
16024
|
+
const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
|
|
15354
16025
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15355
16026
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
15356
16027
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
15357
|
-
await
|
|
16028
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15358
16029
|
filesTouched.push("package.json");
|
|
15359
16030
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
15360
16031
|
}
|
|
15361
|
-
await
|
|
16032
|
+
await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
15362
16033
|
filesTouched.push(primaryFile);
|
|
15363
16034
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
15364
16035
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -15389,7 +16060,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
15389
16060
|
};
|
|
15390
16061
|
}
|
|
15391
16062
|
for (const file of hookFiles) {
|
|
15392
|
-
const content = await
|
|
16063
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
|
|
15393
16064
|
if (content.includes(args.registration_snippet)) {
|
|
15394
16065
|
return {
|
|
15395
16066
|
library: args.library,
|
|
@@ -15424,28 +16095,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
15424
16095
|
if (!await fileExists2(logPath)) {
|
|
15425
16096
|
return { undone: false, message: "no apply found for library" };
|
|
15426
16097
|
}
|
|
15427
|
-
const raw = await
|
|
16098
|
+
const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
|
|
15428
16099
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
15429
16100
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
15430
16101
|
if (!match) {
|
|
15431
16102
|
return { undone: false, message: "no apply found for library" };
|
|
15432
16103
|
}
|
|
15433
|
-
const pkgPath =
|
|
16104
|
+
const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
|
|
15434
16105
|
if (await fileExists2(pkgPath)) {
|
|
15435
16106
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15436
16107
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
15437
16108
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
15438
16109
|
pkg.dependencies = rest;
|
|
15439
|
-
await
|
|
16110
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15440
16111
|
}
|
|
15441
16112
|
}
|
|
15442
16113
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15443
16114
|
for (const file of hookFiles) {
|
|
15444
|
-
const filePath =
|
|
15445
|
-
const content = await
|
|
16115
|
+
const filePath = import_node_path70.default.join(ctx.scanPath, file);
|
|
16116
|
+
const content = await import_node_fs35.promises.readFile(filePath, "utf8");
|
|
15446
16117
|
if (content.includes(match.registration_snippet)) {
|
|
15447
16118
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
15448
|
-
await
|
|
16119
|
+
await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
|
|
15449
16120
|
break;
|
|
15450
16121
|
}
|
|
15451
16122
|
}
|
|
@@ -15457,39 +16128,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
15457
16128
|
|
|
15458
16129
|
// src/divergences.ts
|
|
15459
16130
|
init_cjs_shims();
|
|
15460
|
-
var
|
|
16131
|
+
var import_types58 = require("@neat.is/types");
|
|
15461
16132
|
function bucketKey(source, target, type) {
|
|
15462
16133
|
return `${type}|${source}|${target}`;
|
|
15463
16134
|
}
|
|
15464
16135
|
function bucketSourceFor(graph, edge) {
|
|
15465
|
-
if (edge.type !==
|
|
15466
|
-
const parsed = (0,
|
|
16136
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
|
|
16137
|
+
const parsed = (0, import_types58.parseFileId)(edge.source);
|
|
15467
16138
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
15468
16139
|
const target = graph.getNodeAttributes(edge.target);
|
|
15469
|
-
if (target.type !==
|
|
15470
|
-
return (0,
|
|
16140
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
|
|
16141
|
+
return (0, import_types58.serviceId)(parsed.service);
|
|
15471
16142
|
}
|
|
15472
16143
|
function bucketEdges(graph) {
|
|
15473
16144
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
15474
16145
|
graph.forEachEdge((id, attrs) => {
|
|
15475
16146
|
const e = attrs;
|
|
15476
|
-
const parsed = (0,
|
|
16147
|
+
const parsed = (0, import_types58.parseEdgeId)(id);
|
|
15477
16148
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
15478
16149
|
const source = bucketSourceFor(graph, e);
|
|
15479
16150
|
const key = bucketKey(source, e.target, e.type);
|
|
15480
16151
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
15481
16152
|
switch (provenance) {
|
|
15482
|
-
case
|
|
16153
|
+
case import_types58.Provenance.EXTRACTED:
|
|
15483
16154
|
cur.extracted = e;
|
|
15484
16155
|
break;
|
|
15485
|
-
case
|
|
16156
|
+
case import_types58.Provenance.OBSERVED:
|
|
15486
16157
|
cur.observed = e;
|
|
15487
16158
|
break;
|
|
15488
|
-
case
|
|
16159
|
+
case import_types58.Provenance.INFERRED:
|
|
15489
16160
|
cur.inferred = e;
|
|
15490
16161
|
break;
|
|
15491
16162
|
default:
|
|
15492
|
-
if (e.provenance ===
|
|
16163
|
+
if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
|
|
15493
16164
|
}
|
|
15494
16165
|
buckets2.set(key, cur);
|
|
15495
16166
|
});
|
|
@@ -15498,22 +16169,22 @@ function bucketEdges(graph) {
|
|
|
15498
16169
|
function nodeIsFrontier(graph, nodeId) {
|
|
15499
16170
|
if (!graph.hasNode(nodeId)) return false;
|
|
15500
16171
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15501
|
-
return attrs.type ===
|
|
16172
|
+
return attrs.type === import_types58.NodeType.FrontierNode;
|
|
15502
16173
|
}
|
|
15503
16174
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
15504
16175
|
if (!graph.hasNode(nodeId)) return false;
|
|
15505
16176
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15506
|
-
return attrs.type ===
|
|
16177
|
+
return attrs.type === import_types58.NodeType.WebSocketChannelNode;
|
|
15507
16178
|
}
|
|
15508
16179
|
function nodeIsServerAction(graph, nodeId) {
|
|
15509
16180
|
if (!graph.hasNode(nodeId)) return false;
|
|
15510
16181
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15511
|
-
return attrs.type ===
|
|
16182
|
+
return attrs.type === import_types58.NodeType.ServerActionNode;
|
|
15512
16183
|
}
|
|
15513
16184
|
function nodeIsSymbol(graph, nodeId) {
|
|
15514
16185
|
if (!graph.hasNode(nodeId)) return false;
|
|
15515
16186
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15516
|
-
return attrs.type ===
|
|
16187
|
+
return attrs.type === import_types58.NodeType.SymbolNode;
|
|
15517
16188
|
}
|
|
15518
16189
|
function clampConfidence(n) {
|
|
15519
16190
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -15533,14 +16204,14 @@ function gradedConfidence(edge) {
|
|
|
15533
16204
|
return clampConfidence(confidenceForEdge(edge));
|
|
15534
16205
|
}
|
|
15535
16206
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
16207
|
+
import_types58.EdgeType.CALLS,
|
|
16208
|
+
import_types58.EdgeType.CONNECTS_TO,
|
|
16209
|
+
import_types58.EdgeType.PUBLISHES_TO,
|
|
16210
|
+
import_types58.EdgeType.CONSUMES_FROM
|
|
15540
16211
|
]);
|
|
15541
16212
|
function detectMissingDivergences(graph, bucket) {
|
|
15542
16213
|
const out = [];
|
|
15543
|
-
if (bucket.type ===
|
|
16214
|
+
if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
|
|
15544
16215
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
15545
16216
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
15546
16217
|
if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
|
|
@@ -15582,7 +16253,7 @@ function declaredHostFor(svc) {
|
|
|
15582
16253
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
15583
16254
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15584
16255
|
const e = graph.getEdgeAttributes(edgeId);
|
|
15585
|
-
if (e.type ===
|
|
16256
|
+
if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
|
|
15586
16257
|
return true;
|
|
15587
16258
|
}
|
|
15588
16259
|
}
|
|
@@ -15595,10 +16266,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
15595
16266
|
const out = [];
|
|
15596
16267
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15597
16268
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15598
|
-
if (edge.type !==
|
|
15599
|
-
if (edge.provenance !==
|
|
16269
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16270
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15600
16271
|
const target = graph.getNodeAttributes(edge.target);
|
|
15601
|
-
if (target.type !==
|
|
16272
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15602
16273
|
const observedHost = target.host?.trim();
|
|
15603
16274
|
if (!observedHost) continue;
|
|
15604
16275
|
if (observedHost === declaredHost) continue;
|
|
@@ -15620,10 +16291,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
15620
16291
|
const deps = svc.dependencies ?? {};
|
|
15621
16292
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15622
16293
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15623
|
-
if (edge.type !==
|
|
15624
|
-
if (edge.provenance !==
|
|
16294
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16295
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15625
16296
|
const target = graph.getNodeAttributes(edge.target);
|
|
15626
|
-
if (target.type !==
|
|
16297
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15627
16298
|
for (const pair of compatPairs()) {
|
|
15628
16299
|
if (pair.engine !== target.engine) continue;
|
|
15629
16300
|
const declared = deps[pair.driver];
|
|
@@ -15720,7 +16391,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
15720
16391
|
for (const d of all) {
|
|
15721
16392
|
if (d.type !== "host-mismatch") continue;
|
|
15722
16393
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
15723
|
-
declaredHalf.add((0,
|
|
16394
|
+
declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
|
|
15724
16395
|
}
|
|
15725
16396
|
if (observedHalf.size === 0) return all;
|
|
15726
16397
|
return all.filter((d) => {
|
|
@@ -15739,13 +16410,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15739
16410
|
}
|
|
15740
16411
|
graph.forEachNode((nodeId, attrs) => {
|
|
15741
16412
|
const n = attrs;
|
|
15742
|
-
if (n.type ===
|
|
16413
|
+
if (n.type === import_types58.NodeType.ServiceNode) {
|
|
15743
16414
|
const svc = n;
|
|
15744
16415
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
15745
16416
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
15746
16417
|
return;
|
|
15747
16418
|
}
|
|
15748
|
-
if (n.type ===
|
|
16419
|
+
if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
|
|
15749
16420
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
15750
16421
|
}
|
|
15751
16422
|
});
|
|
@@ -15781,7 +16452,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15781
16452
|
const bc = "column" in b && b.column ? b.column : "";
|
|
15782
16453
|
return ac.localeCompare(bc);
|
|
15783
16454
|
});
|
|
15784
|
-
return
|
|
16455
|
+
return import_types58.DivergenceResultSchema.parse({
|
|
15785
16456
|
divergences: filtered,
|
|
15786
16457
|
totalAffected: filtered.length,
|
|
15787
16458
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15835,7 +16506,7 @@ function queryLogEntries(opts) {
|
|
|
15835
16506
|
|
|
15836
16507
|
// src/diff.ts
|
|
15837
16508
|
init_cjs_shims();
|
|
15838
|
-
var
|
|
16509
|
+
var import_node_fs36 = require("fs");
|
|
15839
16510
|
async function loadSnapshotForDiff(target) {
|
|
15840
16511
|
if (/^https?:\/\//i.test(target)) {
|
|
15841
16512
|
const res = await fetch(target);
|
|
@@ -15844,7 +16515,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
15844
16515
|
}
|
|
15845
16516
|
return await res.json();
|
|
15846
16517
|
}
|
|
15847
|
-
const raw = await
|
|
16518
|
+
const raw = await import_node_fs36.promises.readFile(target, "utf8");
|
|
15848
16519
|
return JSON.parse(raw);
|
|
15849
16520
|
}
|
|
15850
16521
|
function indexEntries(entries) {
|
|
@@ -15912,23 +16583,23 @@ function canonicalJson(value) {
|
|
|
15912
16583
|
|
|
15913
16584
|
// src/projects.ts
|
|
15914
16585
|
init_cjs_shims();
|
|
15915
|
-
var
|
|
16586
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
15916
16587
|
function pathsForProject(project, baseDir) {
|
|
15917
16588
|
if (project === DEFAULT_PROJECT) {
|
|
15918
16589
|
return {
|
|
15919
|
-
snapshotPath:
|
|
15920
|
-
errorsPath:
|
|
15921
|
-
staleEventsPath:
|
|
15922
|
-
embeddingsCachePath:
|
|
15923
|
-
policyViolationsPath:
|
|
16590
|
+
snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
|
|
16591
|
+
errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
|
|
16592
|
+
staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
|
|
16593
|
+
embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
|
|
16594
|
+
policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
|
|
15924
16595
|
};
|
|
15925
16596
|
}
|
|
15926
16597
|
return {
|
|
15927
|
-
snapshotPath:
|
|
15928
|
-
errorsPath:
|
|
15929
|
-
staleEventsPath:
|
|
15930
|
-
embeddingsCachePath:
|
|
15931
|
-
policyViolationsPath:
|
|
16598
|
+
snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
|
|
16599
|
+
errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
|
|
16600
|
+
staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
16601
|
+
embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
|
|
16602
|
+
policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
15932
16603
|
};
|
|
15933
16604
|
}
|
|
15934
16605
|
var Projects = class {
|
|
@@ -15964,28 +16635,28 @@ var Projects = class {
|
|
|
15964
16635
|
|
|
15965
16636
|
// src/registry.ts
|
|
15966
16637
|
init_cjs_shims();
|
|
15967
|
-
var
|
|
16638
|
+
var import_node_fs37 = require("fs");
|
|
15968
16639
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
15969
|
-
var
|
|
15970
|
-
var
|
|
16640
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
16641
|
+
var import_types59 = require("@neat.is/types");
|
|
15971
16642
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
15972
16643
|
var LOCK_RETRY_MS = 50;
|
|
15973
16644
|
function neatHome() {
|
|
15974
16645
|
const override = process.env.NEAT_HOME;
|
|
15975
|
-
if (override && override.length > 0) return
|
|
15976
|
-
return
|
|
16646
|
+
if (override && override.length > 0) return import_node_path72.default.resolve(override);
|
|
16647
|
+
return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
|
|
15977
16648
|
}
|
|
15978
16649
|
function registryPath() {
|
|
15979
|
-
return
|
|
16650
|
+
return import_node_path72.default.join(neatHome(), "projects.json");
|
|
15980
16651
|
}
|
|
15981
16652
|
function registryLockPath() {
|
|
15982
|
-
return
|
|
16653
|
+
return import_node_path72.default.join(neatHome(), "projects.json.lock");
|
|
15983
16654
|
}
|
|
15984
16655
|
function daemonPidPath() {
|
|
15985
|
-
return
|
|
16656
|
+
return import_node_path72.default.join(neatHome(), "neatd.pid");
|
|
15986
16657
|
}
|
|
15987
16658
|
function daemonsDir() {
|
|
15988
|
-
return
|
|
16659
|
+
return import_node_path72.default.join(neatHome(), "daemons");
|
|
15989
16660
|
}
|
|
15990
16661
|
function isFiniteInt(v) {
|
|
15991
16662
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -16018,7 +16689,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
16018
16689
|
const dir = daemonsDir();
|
|
16019
16690
|
let names;
|
|
16020
16691
|
try {
|
|
16021
|
-
names = await
|
|
16692
|
+
names = await import_node_fs37.promises.readdir(dir);
|
|
16022
16693
|
} catch (err) {
|
|
16023
16694
|
if (err.code === "ENOENT") return [];
|
|
16024
16695
|
throw err;
|
|
@@ -16026,10 +16697,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
16026
16697
|
const out = [];
|
|
16027
16698
|
for (const name of names) {
|
|
16028
16699
|
if (!name.endsWith(".json")) continue;
|
|
16029
|
-
const file =
|
|
16700
|
+
const file = import_node_path72.default.join(dir, name);
|
|
16030
16701
|
let raw;
|
|
16031
16702
|
try {
|
|
16032
|
-
raw = await
|
|
16703
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16033
16704
|
} catch {
|
|
16034
16705
|
continue;
|
|
16035
16706
|
}
|
|
@@ -16055,7 +16726,7 @@ function isPidAliveDefault(pid) {
|
|
|
16055
16726
|
}
|
|
16056
16727
|
async function readPidFile(file) {
|
|
16057
16728
|
try {
|
|
16058
|
-
const raw = await
|
|
16729
|
+
const raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16059
16730
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
16060
16731
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
16061
16732
|
} catch {
|
|
@@ -16103,32 +16774,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
16103
16774
|
}
|
|
16104
16775
|
}
|
|
16105
16776
|
async function normalizeProjectPath(input) {
|
|
16106
|
-
const resolved =
|
|
16777
|
+
const resolved = import_node_path72.default.resolve(input);
|
|
16107
16778
|
try {
|
|
16108
|
-
return await
|
|
16779
|
+
return await import_node_fs37.promises.realpath(resolved);
|
|
16109
16780
|
} catch {
|
|
16110
16781
|
return resolved;
|
|
16111
16782
|
}
|
|
16112
16783
|
}
|
|
16113
16784
|
async function writeAtomically(target, contents) {
|
|
16114
|
-
await
|
|
16785
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
|
|
16115
16786
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
16116
|
-
const fd = await
|
|
16787
|
+
const fd = await import_node_fs37.promises.open(tmp, "w");
|
|
16117
16788
|
try {
|
|
16118
16789
|
await fd.writeFile(contents, "utf8");
|
|
16119
16790
|
await fd.sync();
|
|
16120
16791
|
} finally {
|
|
16121
16792
|
await fd.close();
|
|
16122
16793
|
}
|
|
16123
|
-
await
|
|
16794
|
+
await import_node_fs37.promises.rename(tmp, target);
|
|
16124
16795
|
}
|
|
16125
16796
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
16126
16797
|
const deadline = Date.now() + timeoutMs;
|
|
16127
|
-
await
|
|
16798
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
|
|
16128
16799
|
let probedHolder = false;
|
|
16129
16800
|
while (true) {
|
|
16130
16801
|
try {
|
|
16131
|
-
const fd = await
|
|
16802
|
+
const fd = await import_node_fs37.promises.open(lockPath, "wx");
|
|
16132
16803
|
try {
|
|
16133
16804
|
await fd.writeFile(`${process.pid}
|
|
16134
16805
|
`, "utf8");
|
|
@@ -16153,7 +16824,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
16153
16824
|
}
|
|
16154
16825
|
}
|
|
16155
16826
|
async function releaseLock(lockPath) {
|
|
16156
|
-
await
|
|
16827
|
+
await import_node_fs37.promises.unlink(lockPath).catch(() => {
|
|
16157
16828
|
});
|
|
16158
16829
|
}
|
|
16159
16830
|
async function withLock(fn) {
|
|
@@ -16169,7 +16840,7 @@ async function readRegistry() {
|
|
|
16169
16840
|
const file = registryPath();
|
|
16170
16841
|
let raw;
|
|
16171
16842
|
try {
|
|
16172
|
-
raw = await
|
|
16843
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16173
16844
|
} catch (err) {
|
|
16174
16845
|
if (err.code === "ENOENT") {
|
|
16175
16846
|
return { version: 1, projects: [] };
|
|
@@ -16177,10 +16848,10 @@ async function readRegistry() {
|
|
|
16177
16848
|
throw err;
|
|
16178
16849
|
}
|
|
16179
16850
|
const parsed = JSON.parse(raw);
|
|
16180
|
-
return
|
|
16851
|
+
return import_types59.RegistryFileSchema.parse(parsed);
|
|
16181
16852
|
}
|
|
16182
16853
|
async function writeRegistry(reg) {
|
|
16183
|
-
const validated =
|
|
16854
|
+
const validated = import_types59.RegistryFileSchema.parse(reg);
|
|
16184
16855
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16185
16856
|
}
|
|
16186
16857
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -16274,7 +16945,7 @@ function pruneTtlMs() {
|
|
|
16274
16945
|
}
|
|
16275
16946
|
async function statPathStatus(p) {
|
|
16276
16947
|
try {
|
|
16277
|
-
const stat = await
|
|
16948
|
+
const stat = await import_node_fs37.promises.stat(p);
|
|
16278
16949
|
return stat.isDirectory() ? "present" : "unknown";
|
|
16279
16950
|
} catch (err) {
|
|
16280
16951
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -16375,8 +17046,8 @@ init_auth();
|
|
|
16375
17046
|
// src/connectors-config.ts
|
|
16376
17047
|
init_cjs_shims();
|
|
16377
17048
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
16378
|
-
var
|
|
16379
|
-
var
|
|
17049
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
17050
|
+
var import_node_fs38 = require("fs");
|
|
16380
17051
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
16381
17052
|
var EnvRefUnsetError = class extends Error {
|
|
16382
17053
|
ref;
|
|
@@ -16390,17 +17061,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
16390
17061
|
};
|
|
16391
17062
|
function neatHome2() {
|
|
16392
17063
|
const override = process.env.NEAT_HOME;
|
|
16393
|
-
if (override && override.length > 0) return
|
|
16394
|
-
return
|
|
17064
|
+
if (override && override.length > 0) return import_node_path73.default.resolve(override);
|
|
17065
|
+
return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
|
|
16395
17066
|
}
|
|
16396
17067
|
function connectorsConfigPath(home = neatHome2()) {
|
|
16397
|
-
return
|
|
17068
|
+
return import_node_path73.default.join(home, "connectors.json");
|
|
16398
17069
|
}
|
|
16399
17070
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
16400
17071
|
async function warnIfModeLooserThan0600(file) {
|
|
16401
17072
|
if (process.platform === "win32") return;
|
|
16402
17073
|
try {
|
|
16403
|
-
const stat = await
|
|
17074
|
+
const stat = await import_node_fs38.promises.stat(file);
|
|
16404
17075
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
16405
17076
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
16406
17077
|
console.warn(
|
|
@@ -16414,7 +17085,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
16414
17085
|
const file = connectorsConfigPath(home);
|
|
16415
17086
|
let raw;
|
|
16416
17087
|
try {
|
|
16417
|
-
raw = await
|
|
17088
|
+
raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
16418
17089
|
} catch (err) {
|
|
16419
17090
|
if (err.code === "ENOENT") {
|
|
16420
17091
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -16581,15 +17252,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
16581
17252
|
|
|
16582
17253
|
// src/connectors/index.ts
|
|
16583
17254
|
init_cjs_shims();
|
|
16584
|
-
var
|
|
17255
|
+
var import_types60 = require("@neat.is/types");
|
|
16585
17256
|
var NO_ENV = "unknown";
|
|
16586
17257
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
16587
17258
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16588
17259
|
const sites = [];
|
|
16589
17260
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
16590
17261
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16591
|
-
if (edge.provenance !==
|
|
16592
|
-
const parsed = (0,
|
|
17262
|
+
if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
|
|
17263
|
+
const parsed = (0, import_types60.parseFileId)(edge.source);
|
|
16593
17264
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
16594
17265
|
const site = { relPath: edge.evidence.file };
|
|
16595
17266
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -16600,7 +17271,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
16600
17271
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
16601
17272
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16602
17273
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
16603
|
-
if (attrs.type !==
|
|
17274
|
+
if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
|
|
16604
17275
|
const site = { relPath: attrs.path };
|
|
16605
17276
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
16606
17277
|
return site;
|
|
@@ -17098,10 +17769,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
17098
17769
|
// src/connectors/supabase/map.ts
|
|
17099
17770
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
17100
17771
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
17101
|
-
function targetFromRestPath(
|
|
17102
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
17772
|
+
function targetFromRestPath(path76) {
|
|
17773
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path76);
|
|
17103
17774
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
17104
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
17775
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path76);
|
|
17105
17776
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
17106
17777
|
return null;
|
|
17107
17778
|
}
|
|
@@ -17212,23 +17883,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17212
17883
|
|
|
17213
17884
|
// src/connectors/supabase/resolve.ts
|
|
17214
17885
|
init_cjs_shims();
|
|
17215
|
-
var
|
|
17886
|
+
var import_types62 = require("@neat.is/types");
|
|
17216
17887
|
function createSupabaseResolveTarget(graph, config) {
|
|
17217
17888
|
return (signal, _ctx) => {
|
|
17218
17889
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17219
17890
|
return null;
|
|
17220
17891
|
}
|
|
17221
|
-
const subResourceId = (0,
|
|
17892
|
+
const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17222
17893
|
if (graph.hasNode(subResourceId)) {
|
|
17223
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
17894
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17224
17895
|
}
|
|
17225
|
-
const bareResourceId = (0,
|
|
17896
|
+
const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
|
|
17226
17897
|
if (graph.hasNode(bareResourceId)) {
|
|
17227
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
17898
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17228
17899
|
}
|
|
17229
|
-
const projectLevelId = (0,
|
|
17900
|
+
const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
|
|
17230
17901
|
if (graph.hasNode(projectLevelId)) {
|
|
17231
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
17902
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17232
17903
|
}
|
|
17233
17904
|
return null;
|
|
17234
17905
|
};
|
|
@@ -17321,7 +17992,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
17321
17992
|
|
|
17322
17993
|
// src/connectors/railway/index.ts
|
|
17323
17994
|
init_cjs_shims();
|
|
17324
|
-
var
|
|
17995
|
+
var import_types66 = require("@neat.is/types");
|
|
17325
17996
|
|
|
17326
17997
|
// src/connectors/railway/client.ts
|
|
17327
17998
|
init_cjs_shims();
|
|
@@ -17472,7 +18143,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
17472
18143
|
const out = [];
|
|
17473
18144
|
graph.forEachNode((_id, attrs) => {
|
|
17474
18145
|
const node = attrs;
|
|
17475
|
-
if (node.type !==
|
|
18146
|
+
if (node.type !== import_types66.NodeType.RouteNode) return;
|
|
17476
18147
|
const route = attrs;
|
|
17477
18148
|
if (route.service !== serviceName) return;
|
|
17478
18149
|
out.push({
|
|
@@ -17576,12 +18247,12 @@ function createRailwayResolveTarget(config) {
|
|
|
17576
18247
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
17577
18248
|
if (!serviceName) return null;
|
|
17578
18249
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
17579
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18250
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
17580
18251
|
}
|
|
17581
18252
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
17582
18253
|
const peerName = config.serviceNameById[signal.targetName];
|
|
17583
18254
|
if (!peerName) return null;
|
|
17584
|
-
return { targetNodeId: (0,
|
|
18255
|
+
return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
|
|
17585
18256
|
}
|
|
17586
18257
|
return null;
|
|
17587
18258
|
};
|
|
@@ -17705,9 +18376,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
17705
18376
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
17706
18377
|
if (secondSep === -1) return null;
|
|
17707
18378
|
const method = rest.slice(0, secondSep);
|
|
17708
|
-
const
|
|
17709
|
-
if (!resourceName || !method || !
|
|
17710
|
-
return { resourceName, method, path:
|
|
18379
|
+
const path76 = rest.slice(secondSep + 1);
|
|
18380
|
+
if (!resourceName || !method || !path76) return null;
|
|
18381
|
+
return { resourceName, method, path: path76 };
|
|
17711
18382
|
}
|
|
17712
18383
|
function resourceNameFor(type, labels) {
|
|
17713
18384
|
if (!labels) return null;
|
|
@@ -17745,14 +18416,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
17745
18416
|
if (!req) return null;
|
|
17746
18417
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
17747
18418
|
const method = req.requestMethod.toUpperCase();
|
|
17748
|
-
const
|
|
17749
|
-
if (
|
|
18419
|
+
const path76 = pathFromRequestUrl(req.requestUrl);
|
|
18420
|
+
if (path76 === null) return null;
|
|
17750
18421
|
const timestamp = entry.timestamp;
|
|
17751
18422
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
17752
18423
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
17753
18424
|
return {
|
|
17754
18425
|
targetKind: resourceType,
|
|
17755
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
18426
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
|
|
17756
18427
|
callCount: 1,
|
|
17757
18428
|
errorCount: isError ? 1 : 0,
|
|
17758
18429
|
lastObservedIso: timestamp
|
|
@@ -17769,7 +18440,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
17769
18440
|
|
|
17770
18441
|
// src/connectors/firebase/resolve.ts
|
|
17771
18442
|
init_cjs_shims();
|
|
17772
|
-
var
|
|
18443
|
+
var import_types67 = require("@neat.is/types");
|
|
17773
18444
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
17774
18445
|
switch (resourceType) {
|
|
17775
18446
|
case "cloud_function":
|
|
@@ -17784,7 +18455,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
17784
18455
|
const entries = [];
|
|
17785
18456
|
graph.forEachNode((_id, attrs) => {
|
|
17786
18457
|
const node = attrs;
|
|
17787
|
-
if (node.type !==
|
|
18458
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
17788
18459
|
const route = attrs;
|
|
17789
18460
|
if (route.service !== serviceName) return;
|
|
17790
18461
|
entries.push({
|
|
@@ -17816,7 +18487,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
17816
18487
|
return {
|
|
17817
18488
|
targetNodeId: match.routeNodeId,
|
|
17818
18489
|
serviceName,
|
|
17819
|
-
edgeType:
|
|
18490
|
+
edgeType: import_types67.EdgeType.CALLS
|
|
17820
18491
|
};
|
|
17821
18492
|
};
|
|
17822
18493
|
}
|
|
@@ -17843,7 +18514,7 @@ init_cjs_shims();
|
|
|
17843
18514
|
|
|
17844
18515
|
// src/connectors/cloudflare/connector.ts
|
|
17845
18516
|
init_cjs_shims();
|
|
17846
|
-
var
|
|
18517
|
+
var import_types69 = require("@neat.is/types");
|
|
17847
18518
|
|
|
17848
18519
|
// src/connectors/cloudflare/client.ts
|
|
17849
18520
|
init_cjs_shims();
|
|
@@ -17959,7 +18630,7 @@ function mapEventToSignal(event) {
|
|
|
17959
18630
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
17960
18631
|
const statusCode = metadata?.statusCode;
|
|
17961
18632
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
17962
|
-
const
|
|
18633
|
+
const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
17963
18634
|
return {
|
|
17964
18635
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
17965
18636
|
targetName: scriptName,
|
|
@@ -17967,7 +18638,7 @@ function mapEventToSignal(event) {
|
|
|
17967
18638
|
errorCount: isError ? 1 : 0,
|
|
17968
18639
|
lastObservedIso: observedAt.toISOString(),
|
|
17969
18640
|
method,
|
|
17970
|
-
...
|
|
18641
|
+
...path76 ? { path: path76 } : {},
|
|
17971
18642
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
17972
18643
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
17973
18644
|
};
|
|
@@ -18007,19 +18678,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
18007
18678
|
graph.forEachNode((id, attrs) => {
|
|
18008
18679
|
if (found) return;
|
|
18009
18680
|
const a = attrs;
|
|
18010
|
-
if (a.type ===
|
|
18681
|
+
if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
18011
18682
|
found = id;
|
|
18012
18683
|
}
|
|
18013
18684
|
});
|
|
18014
18685
|
return found;
|
|
18015
18686
|
}
|
|
18016
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
18017
|
-
const normalizedPath = normalizePathTemplate(
|
|
18687
|
+
function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
18688
|
+
const normalizedPath = normalizePathTemplate(path76);
|
|
18018
18689
|
let found = null;
|
|
18019
18690
|
graph.forEachNode((id, attrs) => {
|
|
18020
18691
|
if (found) return;
|
|
18021
18692
|
const a = attrs;
|
|
18022
|
-
if (a.type !==
|
|
18693
|
+
if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
|
|
18023
18694
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
18024
18695
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
18025
18696
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -18031,18 +18702,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18031
18702
|
return (signal) => {
|
|
18032
18703
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
18033
18704
|
const scriptName = signal.targetName;
|
|
18034
|
-
const { method, path:
|
|
18705
|
+
const { method, path: path76 } = signal;
|
|
18035
18706
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
18036
|
-
if (!method || !
|
|
18037
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
18707
|
+
if (!method || !path76) return wholeFileId;
|
|
18708
|
+
return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
|
|
18038
18709
|
};
|
|
18039
18710
|
const mapping = config.workers?.[scriptName];
|
|
18040
18711
|
if (mapping) {
|
|
18041
|
-
const wholeFileId = (0,
|
|
18712
|
+
const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
|
|
18042
18713
|
return {
|
|
18043
18714
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
18044
18715
|
serviceName: mapping.service,
|
|
18045
|
-
edgeType:
|
|
18716
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
18046
18717
|
};
|
|
18047
18718
|
}
|
|
18048
18719
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -18051,13 +18722,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18051
18722
|
return {
|
|
18052
18723
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
18053
18724
|
serviceName: fileNode.service,
|
|
18054
|
-
edgeType:
|
|
18725
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
18055
18726
|
};
|
|
18056
18727
|
}
|
|
18057
18728
|
return {
|
|
18058
|
-
targetNodeId: (0,
|
|
18729
|
+
targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
|
|
18059
18730
|
serviceName: scriptName,
|
|
18060
|
-
edgeType:
|
|
18731
|
+
edgeType: import_types69.EdgeType.CALLS,
|
|
18061
18732
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
18062
18733
|
};
|
|
18063
18734
|
};
|
|
@@ -18253,14 +18924,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18253
18924
|
|
|
18254
18925
|
// src/connectors/neon/resolve.ts
|
|
18255
18926
|
init_cjs_shims();
|
|
18256
|
-
var
|
|
18927
|
+
var import_types73 = require("@neat.is/types");
|
|
18257
18928
|
function createNeonResolveTarget(config) {
|
|
18258
18929
|
return (signal) => {
|
|
18259
18930
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18260
18931
|
return {
|
|
18261
|
-
targetNodeId: (0,
|
|
18932
|
+
targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
|
|
18262
18933
|
serviceName: config.serviceName,
|
|
18263
|
-
edgeType:
|
|
18934
|
+
edgeType: import_types73.EdgeType.CALLS,
|
|
18264
18935
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18265
18936
|
};
|
|
18266
18937
|
};
|
|
@@ -18386,9 +19057,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
18386
19057
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
18387
19058
|
if (secondSep === -1) return null;
|
|
18388
19059
|
const method = rest.slice(0, secondSep);
|
|
18389
|
-
const
|
|
18390
|
-
if (!serviceName || !method || !
|
|
18391
|
-
return { serviceName, method, path:
|
|
19060
|
+
const path76 = rest.slice(secondSep + 1);
|
|
19061
|
+
if (!serviceName || !method || !path76) return null;
|
|
19062
|
+
return { serviceName, method, path: path76 };
|
|
18392
19063
|
}
|
|
18393
19064
|
|
|
18394
19065
|
// src/connectors/cloud-run/map.ts
|
|
@@ -18417,14 +19088,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
18417
19088
|
if (!req) return null;
|
|
18418
19089
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
18419
19090
|
const method = req.requestMethod.toUpperCase();
|
|
18420
|
-
const
|
|
18421
|
-
if (
|
|
19091
|
+
const path76 = pathFromRequestUrl2(req.requestUrl);
|
|
19092
|
+
if (path76 === null) return null;
|
|
18422
19093
|
const timestamp = entry.timestamp;
|
|
18423
19094
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
18424
19095
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
18425
19096
|
return {
|
|
18426
19097
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
18427
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
19098
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
|
|
18428
19099
|
callCount: 1,
|
|
18429
19100
|
errorCount: isError ? 1 : 0,
|
|
18430
19101
|
lastObservedIso: timestamp
|
|
@@ -18441,14 +19112,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
18441
19112
|
|
|
18442
19113
|
// src/connectors/cloud-run/resolve.ts
|
|
18443
19114
|
init_cjs_shims();
|
|
18444
|
-
var
|
|
19115
|
+
var import_types77 = require("@neat.is/types");
|
|
18445
19116
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
18446
19117
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
18447
19118
|
let found = null;
|
|
18448
19119
|
graph.forEachNode((_id, attrs) => {
|
|
18449
19120
|
if (found) return;
|
|
18450
19121
|
const node = attrs;
|
|
18451
|
-
if (node.type !==
|
|
19122
|
+
if (node.type !== import_types77.NodeType.RouteNode) return;
|
|
18452
19123
|
const route = attrs;
|
|
18453
19124
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
18454
19125
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -18463,23 +19134,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
18463
19134
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
18464
19135
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
18465
19136
|
if (!identity) return null;
|
|
18466
|
-
const { serviceName: gcpServiceName, method, path:
|
|
19137
|
+
const { serviceName: gcpServiceName, method, path: path76 } = identity;
|
|
18467
19138
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
18468
19139
|
if (mappedService) {
|
|
18469
19140
|
const routeNodeId = findMatchingRouteNode2(
|
|
18470
19141
|
graph,
|
|
18471
19142
|
mappedService,
|
|
18472
19143
|
method,
|
|
18473
|
-
normalizePathTemplate(
|
|
19144
|
+
normalizePathTemplate(path76)
|
|
18474
19145
|
);
|
|
18475
19146
|
if (routeNodeId) {
|
|
18476
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19147
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
|
|
18477
19148
|
}
|
|
18478
19149
|
}
|
|
18479
19150
|
return {
|
|
18480
|
-
targetNodeId: (0,
|
|
19151
|
+
targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
18481
19152
|
serviceName: mappedService ?? gcpServiceName,
|
|
18482
|
-
edgeType:
|
|
19153
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
18483
19154
|
ensureInfraNode: {
|
|
18484
19155
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
18485
19156
|
name: gcpServiceName,
|
|
@@ -18520,7 +19191,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
18520
19191
|
|
|
18521
19192
|
// src/connectors/render/index.ts
|
|
18522
19193
|
init_cjs_shims();
|
|
18523
|
-
var
|
|
19194
|
+
var import_types80 = require("@neat.is/types");
|
|
18524
19195
|
|
|
18525
19196
|
// src/connectors/render/types.ts
|
|
18526
19197
|
init_cjs_shims();
|
|
@@ -18598,7 +19269,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
18598
19269
|
const out = [];
|
|
18599
19270
|
graph.forEachNode((_id, attrs) => {
|
|
18600
19271
|
const node = attrs;
|
|
18601
|
-
if (node.type !==
|
|
19272
|
+
if (node.type !== import_types80.NodeType.RouteNode) return;
|
|
18602
19273
|
const route = attrs;
|
|
18603
19274
|
if (route.service !== serviceName) return;
|
|
18604
19275
|
out.push({
|
|
@@ -18683,7 +19354,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
18683
19354
|
function createRenderResolveTarget(config) {
|
|
18684
19355
|
return (signal) => {
|
|
18685
19356
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
18686
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
19357
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
|
|
18687
19358
|
}
|
|
18688
19359
|
return null;
|
|
18689
19360
|
};
|
|
@@ -18821,21 +19492,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
18821
19492
|
|
|
18822
19493
|
// src/connectors/planetscale/resolve.ts
|
|
18823
19494
|
init_cjs_shims();
|
|
18824
|
-
var
|
|
19495
|
+
var import_types84 = require("@neat.is/types");
|
|
18825
19496
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
18826
19497
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
18827
19498
|
const databaseName = `${config.organization}/${config.database}`;
|
|
18828
19499
|
return (signal, _ctx) => {
|
|
18829
19500
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18830
|
-
const tableId = (0,
|
|
19501
|
+
const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
|
|
18831
19502
|
if (graph.hasNode(tableId)) {
|
|
18832
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
19503
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
|
|
18833
19504
|
}
|
|
18834
|
-
const providerId = (0,
|
|
19505
|
+
const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
18835
19506
|
return {
|
|
18836
19507
|
targetNodeId: providerId,
|
|
18837
19508
|
serviceName: config.serviceName,
|
|
18838
|
-
edgeType:
|
|
19509
|
+
edgeType: import_types84.EdgeType.CALLS,
|
|
18839
19510
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
18840
19511
|
};
|
|
18841
19512
|
};
|
|
@@ -19100,7 +19771,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19100
19771
|
|
|
19101
19772
|
// src/connectors/eas/resolve.ts
|
|
19102
19773
|
init_cjs_shims();
|
|
19103
|
-
var
|
|
19774
|
+
var import_types89 = require("@neat.is/types");
|
|
19104
19775
|
var NO_ENV2 = "unknown";
|
|
19105
19776
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19106
19777
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19116,8 +19787,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19116
19787
|
function configNodeService(graph, configNodeId) {
|
|
19117
19788
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19118
19789
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19119
|
-
if (edge.type !==
|
|
19120
|
-
const parsed = (0,
|
|
19790
|
+
if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
|
|
19791
|
+
const parsed = (0, import_types89.parseFileId)(edge.source);
|
|
19121
19792
|
if (parsed) return parsed.service;
|
|
19122
19793
|
}
|
|
19123
19794
|
return null;
|
|
@@ -19128,7 +19799,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19128
19799
|
graph.forEachNode((id, attrs) => {
|
|
19129
19800
|
if (scoped) return;
|
|
19130
19801
|
const node = attrs;
|
|
19131
|
-
if (node.type !==
|
|
19802
|
+
if (node.type !== import_types89.NodeType.ConfigNode) return;
|
|
19132
19803
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19133
19804
|
if (anyMatch === null) anyMatch = id;
|
|
19134
19805
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19145,13 +19816,13 @@ function createEasResolveTarget(graph) {
|
|
|
19145
19816
|
if (basenames.length > 0) {
|
|
19146
19817
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19147
19818
|
if (configNodeId) {
|
|
19148
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
19819
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
|
|
19149
19820
|
}
|
|
19150
19821
|
}
|
|
19151
19822
|
return {
|
|
19152
19823
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19153
19824
|
serviceName,
|
|
19154
|
-
edgeType:
|
|
19825
|
+
edgeType: import_types89.EdgeType.CALLS
|
|
19155
19826
|
};
|
|
19156
19827
|
};
|
|
19157
19828
|
}
|
|
@@ -19876,11 +20547,11 @@ function registerRoutes(scope, ctx) {
|
|
|
19876
20547
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
19877
20548
|
const parsed = [];
|
|
19878
20549
|
for (const c of candidates) {
|
|
19879
|
-
const r =
|
|
20550
|
+
const r = import_types92.DivergenceTypeSchema.safeParse(c);
|
|
19880
20551
|
if (!r.success) {
|
|
19881
20552
|
return reply.code(400).send({
|
|
19882
20553
|
error: `unknown divergence type "${c}"`,
|
|
19883
|
-
allowed:
|
|
20554
|
+
allowed: import_types92.DivergenceTypeSchema.options
|
|
19884
20555
|
});
|
|
19885
20556
|
}
|
|
19886
20557
|
parsed.push(r.data);
|
|
@@ -20222,7 +20893,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20222
20893
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20223
20894
|
let violations = await log.readAll();
|
|
20224
20895
|
if (req.query.severity) {
|
|
20225
|
-
const sev =
|
|
20896
|
+
const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
|
|
20226
20897
|
if (!sev.success) {
|
|
20227
20898
|
return reply.code(400).send({
|
|
20228
20899
|
error: "invalid severity",
|
|
@@ -20261,7 +20932,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20261
20932
|
scope.post("/policies/check", async (req, reply) => {
|
|
20262
20933
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
20263
20934
|
if (!proj) return;
|
|
20264
|
-
const parsed =
|
|
20935
|
+
const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
20265
20936
|
if (!parsed.success) {
|
|
20266
20937
|
return reply.code(400).send({
|
|
20267
20938
|
error: "invalid /policies/check body",
|
|
@@ -20582,16 +21253,16 @@ init_otel_grpc();
|
|
|
20582
21253
|
|
|
20583
21254
|
// src/daemon.ts
|
|
20584
21255
|
init_cjs_shims();
|
|
20585
|
-
var
|
|
20586
|
-
var
|
|
21256
|
+
var import_node_fs40 = require("fs");
|
|
21257
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
20587
21258
|
var import_node_module = require("module");
|
|
20588
21259
|
init_otel();
|
|
20589
21260
|
init_auth();
|
|
20590
21261
|
|
|
20591
21262
|
// src/unrouted.ts
|
|
20592
21263
|
init_cjs_shims();
|
|
20593
|
-
var
|
|
20594
|
-
var
|
|
21264
|
+
var import_node_fs39 = require("fs");
|
|
21265
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
20595
21266
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
20596
21267
|
return {
|
|
20597
21268
|
timestamp: now.toISOString(),
|
|
@@ -20601,34 +21272,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
20601
21272
|
};
|
|
20602
21273
|
}
|
|
20603
21274
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
20604
|
-
const target =
|
|
20605
|
-
await
|
|
20606
|
-
await
|
|
21275
|
+
const target = import_node_path74.default.join(neatHome3, "errors.ndjson");
|
|
21276
|
+
await import_node_fs39.promises.mkdir(neatHome3, { recursive: true });
|
|
21277
|
+
await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
20607
21278
|
}
|
|
20608
21279
|
function unroutedErrorsPath(neatHome3) {
|
|
20609
|
-
return
|
|
21280
|
+
return import_node_path74.default.join(neatHome3, "errors.ndjson");
|
|
20610
21281
|
}
|
|
20611
21282
|
|
|
20612
21283
|
// src/daemon.ts
|
|
20613
|
-
var
|
|
21284
|
+
var import_types93 = require("@neat.is/types");
|
|
20614
21285
|
function daemonJsonPath(scanPath) {
|
|
20615
|
-
return
|
|
21286
|
+
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
20616
21287
|
}
|
|
20617
21288
|
function daemonsDiscoveryDir(home) {
|
|
20618
21289
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
20619
|
-
return
|
|
21290
|
+
return import_node_path75.default.join(base, "daemons");
|
|
20620
21291
|
}
|
|
20621
21292
|
function daemonDiscoveryPath(project, home) {
|
|
20622
|
-
return
|
|
21293
|
+
return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
20623
21294
|
}
|
|
20624
21295
|
function sanitizeDiscoveryName(project) {
|
|
20625
21296
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
20626
21297
|
}
|
|
20627
21298
|
function neatHomeFromEnv() {
|
|
20628
21299
|
const env = process.env.NEAT_HOME;
|
|
20629
|
-
if (env && env.length > 0) return
|
|
21300
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20630
21301
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20631
|
-
return
|
|
21302
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20632
21303
|
}
|
|
20633
21304
|
function resolveNeatVersion() {
|
|
20634
21305
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -20660,7 +21331,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
20660
21331
|
} catch {
|
|
20661
21332
|
}
|
|
20662
21333
|
try {
|
|
20663
|
-
await
|
|
21334
|
+
await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
20664
21335
|
} catch {
|
|
20665
21336
|
}
|
|
20666
21337
|
}
|
|
@@ -20683,11 +21354,11 @@ function teardownSlot(slot) {
|
|
|
20683
21354
|
}
|
|
20684
21355
|
}
|
|
20685
21356
|
function neatHomeFor(opts) {
|
|
20686
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
21357
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
|
|
20687
21358
|
const env = process.env.NEAT_HOME;
|
|
20688
|
-
if (env && env.length > 0) return
|
|
21359
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20689
21360
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20690
|
-
return
|
|
21361
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20691
21362
|
}
|
|
20692
21363
|
function routeSpanToProject(serviceName, projects) {
|
|
20693
21364
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -20735,13 +21406,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
20735
21406
|
if (!serviceName) return true;
|
|
20736
21407
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
20737
21408
|
return graph.someNode(
|
|
20738
|
-
(_id, attrs) => attrs.type ===
|
|
21409
|
+
(_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
|
|
20739
21410
|
);
|
|
20740
21411
|
}
|
|
20741
21412
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
20742
|
-
const paths = pathsForProject(entry.name,
|
|
21413
|
+
const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
|
|
20743
21414
|
try {
|
|
20744
|
-
const stat = await
|
|
21415
|
+
const stat = await import_node_fs40.promises.stat(entry.path);
|
|
20745
21416
|
if (!stat.isDirectory()) {
|
|
20746
21417
|
throw new Error(`registered path ${entry.path} is not a directory`);
|
|
20747
21418
|
}
|
|
@@ -20859,7 +21530,7 @@ async function startDaemon(opts = {}) {
|
|
|
20859
21530
|
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;
|
|
20860
21531
|
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;
|
|
20861
21532
|
const singleProject = projectArg;
|
|
20862
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
21533
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
|
|
20863
21534
|
if (singleProject && !singleProjectPath) {
|
|
20864
21535
|
throw new Error(
|
|
20865
21536
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -20867,14 +21538,14 @@ async function startDaemon(opts = {}) {
|
|
|
20867
21538
|
}
|
|
20868
21539
|
if (!singleProject) {
|
|
20869
21540
|
try {
|
|
20870
|
-
await
|
|
21541
|
+
await import_node_fs40.promises.access(regPath);
|
|
20871
21542
|
} catch {
|
|
20872
21543
|
throw new Error(
|
|
20873
21544
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
20874
21545
|
);
|
|
20875
21546
|
}
|
|
20876
21547
|
}
|
|
20877
|
-
const pidPath =
|
|
21548
|
+
const pidPath = import_node_path75.default.join(home, "neatd.pid");
|
|
20878
21549
|
await writeAtomically(pidPath, `${process.pid}
|
|
20879
21550
|
`);
|
|
20880
21551
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -21086,7 +21757,7 @@ async function startDaemon(opts = {}) {
|
|
|
21086
21757
|
}
|
|
21087
21758
|
if (restApp) await restApp.close().catch(() => {
|
|
21088
21759
|
});
|
|
21089
|
-
await
|
|
21760
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21090
21761
|
});
|
|
21091
21762
|
throw new Error(
|
|
21092
21763
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -21254,7 +21925,7 @@ async function startDaemon(opts = {}) {
|
|
|
21254
21925
|
});
|
|
21255
21926
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21256
21927
|
});
|
|
21257
|
-
await
|
|
21928
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21258
21929
|
});
|
|
21259
21930
|
throw new Error(
|
|
21260
21931
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -21289,7 +21960,7 @@ async function startDaemon(opts = {}) {
|
|
|
21289
21960
|
});
|
|
21290
21961
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21291
21962
|
});
|
|
21292
|
-
await
|
|
21963
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21293
21964
|
});
|
|
21294
21965
|
throw new Error(
|
|
21295
21966
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -21336,9 +22007,9 @@ async function startDaemon(opts = {}) {
|
|
|
21336
22007
|
let registryWatcher = null;
|
|
21337
22008
|
let reloadTimer = null;
|
|
21338
22009
|
if (!singleProject) try {
|
|
21339
|
-
const regDir =
|
|
21340
|
-
const regBase =
|
|
21341
|
-
registryWatcher = (0,
|
|
22010
|
+
const regDir = import_node_path75.default.dirname(regPath);
|
|
22011
|
+
const regBase = import_node_path75.default.basename(regPath);
|
|
22012
|
+
registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
|
|
21342
22013
|
if (filename !== null && filename !== regBase) return;
|
|
21343
22014
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
21344
22015
|
reloadTimer = setTimeout(() => {
|
|
@@ -21387,7 +22058,7 @@ async function startDaemon(opts = {}) {
|
|
|
21387
22058
|
if (daemonRecord) {
|
|
21388
22059
|
await clearDaemonRecord(daemonRecord, home);
|
|
21389
22060
|
}
|
|
21390
|
-
await
|
|
22061
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21391
22062
|
});
|
|
21392
22063
|
};
|
|
21393
22064
|
return {
|