@neat.is/core 0.9.0 → 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-RDLDSIEY.js → chunk-UN7VFA4H.js} +1112 -382
- package/dist/chunk-UN7VFA4H.js.map +1 -0
- package/dist/{chunk-WMYVJXUF.js → chunk-YKQB622D.js} +2 -2
- package/dist/cli.cjs +1648 -916
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1311 -579
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +1327 -595
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +1214 -482
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-RDLDSIEY.js.map +0 -1
- /package/dist/{chunk-WMYVJXUF.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);
|
|
1571
|
+
edges.push(hop.edge);
|
|
1572
|
+
visited.add(hop.nextService);
|
|
1573
|
+
current = hop.nextService;
|
|
1574
|
+
}
|
|
1575
|
+
if (edges.length === 0) return null;
|
|
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);
|
|
1567
1620
|
edges.push(hop.edge);
|
|
1568
1621
|
visited.add(hop.nextService);
|
|
1569
1622
|
current = hop.nextService;
|
|
1570
1623
|
}
|
|
1571
1624
|
if (edges.length === 0) return null;
|
|
1572
|
-
return { path:
|
|
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,10 +10779,15 @@ 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;
|
|
10786
|
+
var SARAMA_IMPORT_RE = /"[^"]*\/sarama"/;
|
|
10787
|
+
var GO_PRODUCER_RE = /ProducerMessage\s*\{[\s\S]{0,300}?\bTopic\s*:\s*(?:"([^"]+)"|`([^`]+)`|([A-Za-z_]\w*))/g;
|
|
10788
|
+
var GO_CONSUMER_RE = /\.Consume\s*\([\s\S]{0,120}?\[\]string\s*\{([^}]*)\}/g;
|
|
10789
|
+
var GO_STRING_LITERAL_RE = /"([^"]+)"|`([^`]+)`/g;
|
|
10790
|
+
var GO_IDENT_RE = /[A-Za-z_]\w*/g;
|
|
10236
10791
|
function findAll(re, text) {
|
|
10237
10792
|
re.lastIndex = 0;
|
|
10238
10793
|
const out = [];
|
|
@@ -10242,38 +10797,94 @@ function findAll(re, text) {
|
|
|
10242
10797
|
}
|
|
10243
10798
|
return out;
|
|
10244
10799
|
}
|
|
10800
|
+
function resolveGoLiteral(ident, text) {
|
|
10801
|
+
const esc = ident.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10802
|
+
const re = new RegExp(
|
|
10803
|
+
`\\b${esc}(?:\\s+\\w[\\w.\\[\\]*]*)?\\s*(?::=|=)\\s*(?:"([^"]+)"|\`([^\`]+)\`)`
|
|
10804
|
+
);
|
|
10805
|
+
const m = re.exec(text);
|
|
10806
|
+
if (!m) return void 0;
|
|
10807
|
+
return m[1] ?? m[2];
|
|
10808
|
+
}
|
|
10809
|
+
function producerTopicAnchor(match) {
|
|
10810
|
+
const [full] = match;
|
|
10811
|
+
const tokenLen = match[1] ? match[1].length + 2 : match[2] ? match[2].length + 2 : match[3]?.length ?? 0;
|
|
10812
|
+
return match.index + full.length - tokenLen;
|
|
10813
|
+
}
|
|
10814
|
+
function goSaramaEndpoints(text, emit) {
|
|
10815
|
+
if (!SARAMA_IMPORT_RE.test(text)) return;
|
|
10816
|
+
GO_PRODUCER_RE.lastIndex = 0;
|
|
10817
|
+
let m;
|
|
10818
|
+
while ((m = GO_PRODUCER_RE.exec(text)) !== null) {
|
|
10819
|
+
const literal = m[1] ?? m[2];
|
|
10820
|
+
const anchor = producerTopicAnchor(m);
|
|
10821
|
+
if (literal) {
|
|
10822
|
+
emit(literal, "PUBLISHES_TO", anchor);
|
|
10823
|
+
} else if (m[3]) {
|
|
10824
|
+
const resolved = resolveGoLiteral(m[3], text);
|
|
10825
|
+
if (resolved) emit(resolved, "PUBLISHES_TO", anchor);
|
|
10826
|
+
}
|
|
10827
|
+
}
|
|
10828
|
+
GO_CONSUMER_RE.lastIndex = 0;
|
|
10829
|
+
while ((m = GO_CONSUMER_RE.exec(text)) !== null) {
|
|
10830
|
+
const inside = m[1] ?? "";
|
|
10831
|
+
const anchor = m.index + Math.max(0, m[0].indexOf("[]string"));
|
|
10832
|
+
let sawLiteral = false;
|
|
10833
|
+
GO_STRING_LITERAL_RE.lastIndex = 0;
|
|
10834
|
+
let s;
|
|
10835
|
+
while ((s = GO_STRING_LITERAL_RE.exec(inside)) !== null) {
|
|
10836
|
+
const topic = s[1] ?? s[2];
|
|
10837
|
+
if (topic) {
|
|
10838
|
+
emit(topic, "CONSUMES_FROM", anchor);
|
|
10839
|
+
sawLiteral = true;
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
if (sawLiteral) continue;
|
|
10843
|
+
GO_IDENT_RE.lastIndex = 0;
|
|
10844
|
+
let id;
|
|
10845
|
+
while ((id = GO_IDENT_RE.exec(inside)) !== null) {
|
|
10846
|
+
const resolved = resolveGoLiteral(id[0], text);
|
|
10847
|
+
if (resolved) emit(resolved, "CONSUMES_FROM", anchor);
|
|
10848
|
+
}
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10245
10851
|
function kafkaEndpointsFromFile(file, serviceDir) {
|
|
10246
10852
|
const out = [];
|
|
10247
10853
|
const seen = /* @__PURE__ */ new Set();
|
|
10248
|
-
const make = (topic, edgeType) => {
|
|
10854
|
+
const make = (topic, edgeType, anchor) => {
|
|
10249
10855
|
const key = `${edgeType}|${topic}`;
|
|
10250
10856
|
if (seen.has(key)) return;
|
|
10251
10857
|
seen.add(key);
|
|
10252
|
-
const line = lineOf(file.content, topic);
|
|
10858
|
+
const line = anchor !== void 0 ? file.content.slice(0, anchor).split("\n").length : lineOf(file.content, topic);
|
|
10253
10859
|
out.push({
|
|
10254
10860
|
infraId: (0, import_types28.infraId)("kafka-topic", topic),
|
|
10255
10861
|
name: topic,
|
|
10256
10862
|
kind: "kafka-topic",
|
|
10257
10863
|
edgeType,
|
|
10258
|
-
// `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
|
|
10259
|
-
//
|
|
10260
|
-
//
|
|
10864
|
+
// JS: `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
|
|
10865
|
+
// (kafkajs / node-rdkafka). Go: `sarama.ProducerMessage{Topic: 'x'}` /
|
|
10866
|
+
// `consumerGroup.Consume(ctx, []string{'x'}, …)`. Both are framework-aware
|
|
10867
|
+
// call sites — verified-call-site tier (ADR-066).
|
|
10261
10868
|
confidenceKind: "verified-call-site",
|
|
10262
10869
|
evidence: {
|
|
10263
|
-
file:
|
|
10870
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
10264
10871
|
line,
|
|
10265
10872
|
snippet: snippet(file.content, line)
|
|
10266
10873
|
}
|
|
10267
10874
|
});
|
|
10268
10875
|
};
|
|
10269
|
-
|
|
10270
|
-
|
|
10876
|
+
if (import_node_path39.default.extname(file.path) === ".go") {
|
|
10877
|
+
goSaramaEndpoints(file.content, make);
|
|
10878
|
+
} else {
|
|
10879
|
+
for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
|
|
10880
|
+
for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
|
|
10881
|
+
}
|
|
10271
10882
|
return out;
|
|
10272
10883
|
}
|
|
10273
10884
|
|
|
10274
10885
|
// src/extract/calls/redis.ts
|
|
10275
10886
|
init_cjs_shims();
|
|
10276
|
-
var
|
|
10887
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
10277
10888
|
var import_types29 = require("@neat.is/types");
|
|
10278
10889
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
10279
10890
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -10296,7 +10907,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10296
10907
|
// support tier (ADR-066).
|
|
10297
10908
|
confidenceKind: "url-with-structural-support",
|
|
10298
10909
|
evidence: {
|
|
10299
|
-
file:
|
|
10910
|
+
file: import_node_path40.default.relative(serviceDir, file.path),
|
|
10300
10911
|
line,
|
|
10301
10912
|
snippet: snippet(file.content, line)
|
|
10302
10913
|
}
|
|
@@ -10307,7 +10918,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10307
10918
|
|
|
10308
10919
|
// src/extract/calls/aws.ts
|
|
10309
10920
|
init_cjs_shims();
|
|
10310
|
-
var
|
|
10921
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
10311
10922
|
var import_types30 = require("@neat.is/types");
|
|
10312
10923
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10313
10924
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -10341,7 +10952,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10341
10952
|
// (ADR-066).
|
|
10342
10953
|
confidenceKind: "verified-call-site",
|
|
10343
10954
|
evidence: {
|
|
10344
|
-
file:
|
|
10955
|
+
file: import_node_path41.default.relative(serviceDir, file.path),
|
|
10345
10956
|
line,
|
|
10346
10957
|
snippet: snippet(file.content, line)
|
|
10347
10958
|
}
|
|
@@ -10366,7 +10977,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10366
10977
|
|
|
10367
10978
|
// src/extract/calls/grpc.ts
|
|
10368
10979
|
init_cjs_shims();
|
|
10369
|
-
var
|
|
10980
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
10370
10981
|
var import_types31 = require("@neat.is/types");
|
|
10371
10982
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
10372
10983
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -10425,7 +11036,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10425
11036
|
// tier (ADR-066).
|
|
10426
11037
|
confidenceKind: "verified-call-site",
|
|
10427
11038
|
evidence: {
|
|
10428
|
-
file:
|
|
11039
|
+
file: import_node_path42.default.relative(serviceDir, file.path),
|
|
10429
11040
|
line,
|
|
10430
11041
|
snippet: snippet(file.content, line)
|
|
10431
11042
|
}
|
|
@@ -10436,7 +11047,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10436
11047
|
|
|
10437
11048
|
// src/extract/calls/supabase.ts
|
|
10438
11049
|
init_cjs_shims();
|
|
10439
|
-
var
|
|
11050
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
10440
11051
|
var import_types32 = require("@neat.is/types");
|
|
10441
11052
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
10442
11053
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -10495,7 +11106,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10495
11106
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
10496
11107
|
confidenceKind: "verified-call-site",
|
|
10497
11108
|
evidence: {
|
|
10498
|
-
file:
|
|
11109
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10499
11110
|
line,
|
|
10500
11111
|
snippet: snippet(file.content, line)
|
|
10501
11112
|
}
|
|
@@ -10522,7 +11133,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10522
11133
|
edgeType: "CALLS",
|
|
10523
11134
|
confidenceKind: "verified-call-site",
|
|
10524
11135
|
evidence: {
|
|
10525
|
-
file:
|
|
11136
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10526
11137
|
line,
|
|
10527
11138
|
snippet: snippet(file.content, line)
|
|
10528
11139
|
}
|
|
@@ -10534,7 +11145,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10534
11145
|
|
|
10535
11146
|
// src/extract/calls/firestore.ts
|
|
10536
11147
|
init_cjs_shims();
|
|
10537
|
-
var
|
|
11148
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
10538
11149
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
10539
11150
|
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
10540
11151
|
var import_types33 = require("@neat.is/types");
|
|
@@ -10573,7 +11184,7 @@ function isFirestoreClientFactory(node) {
|
|
|
10573
11184
|
}
|
|
10574
11185
|
function firestoreClientVars(root) {
|
|
10575
11186
|
const vars = /* @__PURE__ */ new Set();
|
|
10576
|
-
const
|
|
11187
|
+
const walk10 = (node) => {
|
|
10577
11188
|
if (node.type === "variable_declarator") {
|
|
10578
11189
|
const name = node.childForFieldName("name");
|
|
10579
11190
|
let value = node.childForFieldName("value");
|
|
@@ -10582,9 +11193,9 @@ function firestoreClientVars(root) {
|
|
|
10582
11193
|
vars.add(name.text);
|
|
10583
11194
|
}
|
|
10584
11195
|
}
|
|
10585
|
-
for (const c of namedChildren(node))
|
|
11196
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10586
11197
|
};
|
|
10587
|
-
|
|
11198
|
+
walk10(root);
|
|
10588
11199
|
return vars;
|
|
10589
11200
|
}
|
|
10590
11201
|
function isClientExpr(node, clientVars) {
|
|
@@ -10705,7 +11316,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10705
11316
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
10706
11317
|
if (!hasClient && !hasAdmin) return [];
|
|
10707
11318
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
10708
|
-
const tree = parseSource3(parserForExt2(
|
|
11319
|
+
const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
|
|
10709
11320
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
10710
11321
|
const collLine = /* @__PURE__ */ new Map();
|
|
10711
11322
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -10739,7 +11350,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10739
11350
|
}
|
|
10740
11351
|
s.add(field);
|
|
10741
11352
|
};
|
|
10742
|
-
const
|
|
11353
|
+
const walk10 = (node) => {
|
|
10743
11354
|
if (node.type === "call_expression") {
|
|
10744
11355
|
const fn = node.childForFieldName("function");
|
|
10745
11356
|
const line = node.startPosition.row + 1;
|
|
@@ -10779,9 +11390,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10779
11390
|
}
|
|
10780
11391
|
}
|
|
10781
11392
|
}
|
|
10782
|
-
for (const c of namedChildren(node))
|
|
11393
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10783
11394
|
};
|
|
10784
|
-
|
|
11395
|
+
walk10(tree.rootNode);
|
|
10785
11396
|
const out = [];
|
|
10786
11397
|
for (const [collPath, line] of collLine) {
|
|
10787
11398
|
const byField = writes.get(collPath);
|
|
@@ -10809,7 +11420,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10809
11420
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
10810
11421
|
...sdkWrites ? { sdkWrites } : {},
|
|
10811
11422
|
evidence: {
|
|
10812
|
-
file:
|
|
11423
|
+
file: import_node_path44.default.relative(serviceDir, file.path),
|
|
10813
11424
|
line,
|
|
10814
11425
|
snippet: snippet(file.content, line)
|
|
10815
11426
|
}
|
|
@@ -10820,7 +11431,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10820
11431
|
|
|
10821
11432
|
// src/extract/calls/mongoose.ts
|
|
10822
11433
|
init_cjs_shims();
|
|
10823
|
-
var
|
|
11434
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
10824
11435
|
var import_types34 = require("@neat.is/types");
|
|
10825
11436
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
10826
11437
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -10971,7 +11582,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
10971
11582
|
kind: r.kind,
|
|
10972
11583
|
edgeType: "CALLS",
|
|
10973
11584
|
confidenceKind: "verified-call-site",
|
|
10974
|
-
evidence: { file:
|
|
11585
|
+
evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
10975
11586
|
};
|
|
10976
11587
|
}
|
|
10977
11588
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -11061,7 +11672,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11061
11672
|
const registry = /* @__PURE__ */ new Map();
|
|
11062
11673
|
for (const f of mongooseFiles) {
|
|
11063
11674
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
11064
|
-
if (fx) registry.set(toPosix(
|
|
11675
|
+
if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
|
|
11065
11676
|
}
|
|
11066
11677
|
if (registry.size === 0) return [];
|
|
11067
11678
|
const out = [];
|
|
@@ -11072,7 +11683,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11072
11683
|
const directColl = /* @__PURE__ */ new Map();
|
|
11073
11684
|
const nsExports = /* @__PURE__ */ new Map();
|
|
11074
11685
|
for (const b of bindings) {
|
|
11075
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
11686
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
|
|
11076
11687
|
if (!resolvedRel) continue;
|
|
11077
11688
|
const fx = registry.get(resolvedRel);
|
|
11078
11689
|
if (!fx) continue;
|
|
@@ -11116,7 +11727,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11116
11727
|
|
|
11117
11728
|
// src/extract/calls/sqlalchemy.ts
|
|
11118
11729
|
init_cjs_shims();
|
|
11119
|
-
var
|
|
11730
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
11120
11731
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
11121
11732
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
11122
11733
|
var import_types35 = require("@neat.is/types");
|
|
@@ -11255,7 +11866,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
11255
11866
|
childTable,
|
|
11256
11867
|
parentTable,
|
|
11257
11868
|
evidence: {
|
|
11258
|
-
file:
|
|
11869
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11259
11870
|
line,
|
|
11260
11871
|
snippet: snippet(file.content, line)
|
|
11261
11872
|
}
|
|
@@ -11280,7 +11891,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
11280
11891
|
confidenceKind: "verified-call-site",
|
|
11281
11892
|
...columns && columns.length > 0 ? { columns } : {},
|
|
11282
11893
|
evidence: {
|
|
11283
|
-
file:
|
|
11894
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11284
11895
|
line,
|
|
11285
11896
|
snippet: snippet(file.content, line)
|
|
11286
11897
|
}
|
|
@@ -11387,7 +11998,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11387
11998
|
edgeType: "CALLS",
|
|
11388
11999
|
confidenceKind: "verified-call-site",
|
|
11389
12000
|
evidence: {
|
|
11390
|
-
file:
|
|
12001
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11391
12002
|
line,
|
|
11392
12003
|
snippet: snippet(file.content, line)
|
|
11393
12004
|
}
|
|
@@ -11399,7 +12010,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11399
12010
|
|
|
11400
12011
|
// src/extract/calls/django-orm.ts
|
|
11401
12012
|
init_cjs_shims();
|
|
11402
|
-
var
|
|
12013
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
11403
12014
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
11404
12015
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
11405
12016
|
var import_types36 = require("@neat.is/types");
|
|
@@ -11469,7 +12080,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11469
12080
|
const tree = parseSource7(makePyParser4(), file.content);
|
|
11470
12081
|
const out = [];
|
|
11471
12082
|
const seen = /* @__PURE__ */ new Set();
|
|
11472
|
-
const defaultAppLabel =
|
|
12083
|
+
const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
|
|
11473
12084
|
walk4(tree.rootNode, (node) => {
|
|
11474
12085
|
if (node.type !== "class_definition") return;
|
|
11475
12086
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -11487,7 +12098,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11487
12098
|
kind: "sql-table",
|
|
11488
12099
|
edgeType: "CALLS",
|
|
11489
12100
|
confidenceKind: "verified-call-site",
|
|
11490
|
-
evidence: { file:
|
|
12101
|
+
evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11491
12102
|
});
|
|
11492
12103
|
});
|
|
11493
12104
|
return out;
|
|
@@ -11495,7 +12106,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11495
12106
|
|
|
11496
12107
|
// src/extract/calls/drizzle.ts
|
|
11497
12108
|
init_cjs_shims();
|
|
11498
|
-
var
|
|
12109
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
11499
12110
|
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
11500
12111
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
11501
12112
|
var import_types37 = require("@neat.is/types");
|
|
@@ -11573,10 +12184,10 @@ function columnsFromObject(obj) {
|
|
|
11573
12184
|
}
|
|
11574
12185
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
11575
12186
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11576
|
-
const tree = parseSource3(parserForExt3(
|
|
12187
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11577
12188
|
const out = [];
|
|
11578
12189
|
const seen = /* @__PURE__ */ new Set();
|
|
11579
|
-
const
|
|
12190
|
+
const walk10 = (node) => {
|
|
11580
12191
|
if (node.type === "call_expression") {
|
|
11581
12192
|
const fn = node.childForFieldName("function");
|
|
11582
12193
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11596,7 +12207,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11596
12207
|
confidenceKind: "structural",
|
|
11597
12208
|
columns,
|
|
11598
12209
|
evidence: {
|
|
11599
|
-
file:
|
|
12210
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11600
12211
|
line,
|
|
11601
12212
|
snippet: snippet(file.content, line)
|
|
11602
12213
|
}
|
|
@@ -11604,9 +12215,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11604
12215
|
}
|
|
11605
12216
|
}
|
|
11606
12217
|
}
|
|
11607
|
-
for (const c of namedChildren4(node))
|
|
12218
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11608
12219
|
};
|
|
11609
|
-
|
|
12220
|
+
walk10(tree.rootNode);
|
|
11610
12221
|
return out;
|
|
11611
12222
|
}
|
|
11612
12223
|
function enclosingVarName(call) {
|
|
@@ -11628,7 +12239,7 @@ function enclosingVarName(call) {
|
|
|
11628
12239
|
function collectDrizzleTables(root) {
|
|
11629
12240
|
const tables = [];
|
|
11630
12241
|
const varToTable = /* @__PURE__ */ new Map();
|
|
11631
|
-
const
|
|
12242
|
+
const walk10 = (node) => {
|
|
11632
12243
|
if (node.type === "call_expression") {
|
|
11633
12244
|
const fn = node.childForFieldName("function");
|
|
11634
12245
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11643,9 +12254,9 @@ function collectDrizzleTables(root) {
|
|
|
11643
12254
|
}
|
|
11644
12255
|
}
|
|
11645
12256
|
}
|
|
11646
|
-
for (const c of namedChildren4(node))
|
|
12257
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11647
12258
|
};
|
|
11648
|
-
|
|
12259
|
+
walk10(root);
|
|
11649
12260
|
return { tables, varToTable };
|
|
11650
12261
|
}
|
|
11651
12262
|
function referencesTargetVar(call) {
|
|
@@ -11662,13 +12273,13 @@ function referencesTargetVar(call) {
|
|
|
11662
12273
|
}
|
|
11663
12274
|
function drizzleForeignKeys(file, serviceDir) {
|
|
11664
12275
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11665
|
-
const tree = parseSource3(parserForExt3(
|
|
12276
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11666
12277
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
11667
12278
|
const out = [];
|
|
11668
12279
|
const seen = /* @__PURE__ */ new Set();
|
|
11669
12280
|
for (const table of tables) {
|
|
11670
12281
|
if (!table.object) continue;
|
|
11671
|
-
const
|
|
12282
|
+
const walk10 = (node) => {
|
|
11672
12283
|
if (node.type === "call_expression") {
|
|
11673
12284
|
const targetVar = referencesTargetVar(node);
|
|
11674
12285
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -11681,7 +12292,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11681
12292
|
childTable: table.tableName,
|
|
11682
12293
|
parentTable,
|
|
11683
12294
|
evidence: {
|
|
11684
|
-
file:
|
|
12295
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11685
12296
|
line,
|
|
11686
12297
|
snippet: snippet(file.content, line)
|
|
11687
12298
|
}
|
|
@@ -11689,16 +12300,16 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11689
12300
|
}
|
|
11690
12301
|
}
|
|
11691
12302
|
}
|
|
11692
|
-
for (const c of namedChildren4(node))
|
|
12303
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11693
12304
|
};
|
|
11694
|
-
|
|
12305
|
+
walk10(table.object);
|
|
11695
12306
|
}
|
|
11696
12307
|
return out;
|
|
11697
12308
|
}
|
|
11698
12309
|
|
|
11699
12310
|
// src/extract/calls/prisma.ts
|
|
11700
12311
|
init_cjs_shims();
|
|
11701
|
-
var
|
|
12312
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
11702
12313
|
var import_types38 = require("@neat.is/types");
|
|
11703
12314
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
11704
12315
|
"Int",
|
|
@@ -11754,7 +12365,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11754
12365
|
confidenceKind: "structural",
|
|
11755
12366
|
columns: b.columns,
|
|
11756
12367
|
evidence: {
|
|
11757
|
-
file:
|
|
12368
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11758
12369
|
line: b.startLine,
|
|
11759
12370
|
snippet: snippet(content, b.startLine)
|
|
11760
12371
|
}
|
|
@@ -11815,7 +12426,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11815
12426
|
}
|
|
11816
12427
|
async function prismaColumnEndpoints(serviceDir) {
|
|
11817
12428
|
const schemaPath = await findFirst(serviceDir, [
|
|
11818
|
-
|
|
12429
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11819
12430
|
"schema.prisma"
|
|
11820
12431
|
]);
|
|
11821
12432
|
if (!schemaPath) return [];
|
|
@@ -11890,7 +12501,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11890
12501
|
childTable: current.table,
|
|
11891
12502
|
parentTable,
|
|
11892
12503
|
evidence: {
|
|
11893
|
-
file:
|
|
12504
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11894
12505
|
line: lineNo,
|
|
11895
12506
|
snippet: snippet(content, lineNo)
|
|
11896
12507
|
}
|
|
@@ -11905,7 +12516,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11905
12516
|
}
|
|
11906
12517
|
async function prismaForeignKeys(serviceDir) {
|
|
11907
12518
|
const schemaPath = await findFirst(serviceDir, [
|
|
11908
|
-
|
|
12519
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11909
12520
|
"schema.prisma"
|
|
11910
12521
|
]);
|
|
11911
12522
|
if (!schemaPath) return [];
|
|
@@ -11916,7 +12527,7 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
11916
12527
|
|
|
11917
12528
|
// src/extract/calls/activerecord.ts
|
|
11918
12529
|
init_cjs_shims();
|
|
11919
|
-
var
|
|
12530
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11920
12531
|
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
11921
12532
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
11922
12533
|
var import_types39 = require("@neat.is/types");
|
|
@@ -12124,7 +12735,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
12124
12735
|
confidenceKind: "structural",
|
|
12125
12736
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
12126
12737
|
evidence: {
|
|
12127
|
-
file:
|
|
12738
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12128
12739
|
line: table.line,
|
|
12129
12740
|
snippet: snippet(file.content, table.line)
|
|
12130
12741
|
}
|
|
@@ -12146,7 +12757,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
12146
12757
|
childTable,
|
|
12147
12758
|
parentTable,
|
|
12148
12759
|
evidence: {
|
|
12149
|
-
file:
|
|
12760
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12150
12761
|
line,
|
|
12151
12762
|
snippet: snippet(file.content, line)
|
|
12152
12763
|
}
|
|
@@ -12229,7 +12840,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
12229
12840
|
edgeType: "CALLS",
|
|
12230
12841
|
confidenceKind: "verified-call-site",
|
|
12231
12842
|
evidence: {
|
|
12232
|
-
file:
|
|
12843
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12233
12844
|
line,
|
|
12234
12845
|
snippet: snippet(file.content, line)
|
|
12235
12846
|
}
|
|
@@ -12266,7 +12877,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12266
12877
|
childTable,
|
|
12267
12878
|
parentTable,
|
|
12268
12879
|
evidence: {
|
|
12269
|
-
file:
|
|
12880
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12270
12881
|
line,
|
|
12271
12882
|
snippet: snippet(file.content, line)
|
|
12272
12883
|
}
|
|
@@ -12278,7 +12889,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12278
12889
|
|
|
12279
12890
|
// src/extract/calls/eloquent.ts
|
|
12280
12891
|
init_cjs_shims();
|
|
12281
|
-
var
|
|
12892
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12282
12893
|
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
12283
12894
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
12284
12895
|
var import_types40 = require("@neat.is/types");
|
|
@@ -12535,7 +13146,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
12535
13146
|
confidenceKind: "structural",
|
|
12536
13147
|
...columns.length > 0 ? { columns } : {},
|
|
12537
13148
|
evidence: {
|
|
12538
|
-
file:
|
|
13149
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12539
13150
|
line: bp.line,
|
|
12540
13151
|
snippet: snippet(file.content, bp.line)
|
|
12541
13152
|
}
|
|
@@ -12557,7 +13168,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
12557
13168
|
childTable,
|
|
12558
13169
|
parentTable,
|
|
12559
13170
|
evidence: {
|
|
12560
|
-
file:
|
|
13171
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12561
13172
|
line,
|
|
12562
13173
|
snippet: snippet(file.content, line)
|
|
12563
13174
|
}
|
|
@@ -12677,7 +13288,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
12677
13288
|
edgeType: "CALLS",
|
|
12678
13289
|
confidenceKind: "verified-call-site",
|
|
12679
13290
|
evidence: {
|
|
12680
|
-
file:
|
|
13291
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12681
13292
|
line,
|
|
12682
13293
|
snippet: snippet(file.content, line)
|
|
12683
13294
|
}
|
|
@@ -12713,7 +13324,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12713
13324
|
childTable,
|
|
12714
13325
|
parentTable,
|
|
12715
13326
|
evidence: {
|
|
12716
|
-
file:
|
|
13327
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12717
13328
|
line,
|
|
12718
13329
|
snippet: snippet(file.content, line)
|
|
12719
13330
|
}
|
|
@@ -12725,7 +13336,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12725
13336
|
|
|
12726
13337
|
// src/extract/calls/go.ts
|
|
12727
13338
|
init_cjs_shims();
|
|
12728
|
-
var
|
|
13339
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12729
13340
|
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
12730
13341
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
12731
13342
|
var import_types41 = require("@neat.is/types");
|
|
@@ -12800,7 +13411,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
12800
13411
|
return null;
|
|
12801
13412
|
}
|
|
12802
13413
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
12803
|
-
if (
|
|
13414
|
+
if (import_node_path54.default.extname(file.path) !== ".go") return [];
|
|
12804
13415
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
12805
13416
|
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12806
13417
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -12829,7 +13440,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12829
13440
|
confidenceKind: "verified-call-site",
|
|
12830
13441
|
...columns.length > 0 ? { columns } : {},
|
|
12831
13442
|
evidence: {
|
|
12832
|
-
file: toPosix(
|
|
13443
|
+
file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
|
|
12833
13444
|
line,
|
|
12834
13445
|
snippet: snippet(file.content, line)
|
|
12835
13446
|
}
|
|
@@ -12840,7 +13451,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12840
13451
|
|
|
12841
13452
|
// src/extract/calls/gorm.ts
|
|
12842
13453
|
init_cjs_shims();
|
|
12843
|
-
var
|
|
13454
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12844
13455
|
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
12845
13456
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
12846
13457
|
var import_types42 = require("@neat.is/types");
|
|
@@ -13273,7 +13884,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
13273
13884
|
seen.delete(struct.name);
|
|
13274
13885
|
}
|
|
13275
13886
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
13276
|
-
if (
|
|
13887
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13277
13888
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13278
13889
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13279
13890
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13295,7 +13906,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13295
13906
|
confidenceKind: "structural",
|
|
13296
13907
|
...columns.length > 0 ? { columns } : {},
|
|
13297
13908
|
evidence: {
|
|
13298
|
-
file: toPosix(
|
|
13909
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13299
13910
|
line: struct.line,
|
|
13300
13911
|
snippet: snippet(file.content, struct.line)
|
|
13301
13912
|
}
|
|
@@ -13304,7 +13915,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13304
13915
|
return out;
|
|
13305
13916
|
}
|
|
13306
13917
|
function gormForeignKeys(file, serviceDir) {
|
|
13307
|
-
if (
|
|
13918
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13308
13919
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13309
13920
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13310
13921
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13319,7 +13930,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13319
13930
|
childTable,
|
|
13320
13931
|
parentTable,
|
|
13321
13932
|
evidence: {
|
|
13322
|
-
file: toPosix(
|
|
13933
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13323
13934
|
line,
|
|
13324
13935
|
snippet: snippet(file.content, line)
|
|
13325
13936
|
}
|
|
@@ -13354,15 +13965,131 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13354
13965
|
return out;
|
|
13355
13966
|
}
|
|
13356
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
|
+
|
|
13357
14084
|
// src/extract/calls/index.ts
|
|
13358
14085
|
function edgeTypeFromEndpoint(ep) {
|
|
13359
14086
|
switch (ep.edgeType) {
|
|
13360
14087
|
case "PUBLISHES_TO":
|
|
13361
|
-
return
|
|
14088
|
+
return import_types44.EdgeType.PUBLISHES_TO;
|
|
13362
14089
|
case "CONSUMES_FROM":
|
|
13363
|
-
return
|
|
14090
|
+
return import_types44.EdgeType.CONSUMES_FROM;
|
|
13364
14091
|
default:
|
|
13365
|
-
return
|
|
14092
|
+
return import_types44.EdgeType.CALLS;
|
|
13366
14093
|
}
|
|
13367
14094
|
}
|
|
13368
14095
|
function isAwsKind(kind) {
|
|
@@ -13412,6 +14139,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13412
14139
|
} catch (err) {
|
|
13413
14140
|
recordExtractionError("laravel eloquent extraction", file.path, err);
|
|
13414
14141
|
}
|
|
14142
|
+
try {
|
|
14143
|
+
endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
|
|
14144
|
+
} catch (err) {
|
|
14145
|
+
recordExtractionError("efcore data-axis extraction", file.path, err);
|
|
14146
|
+
}
|
|
13415
14147
|
}
|
|
13416
14148
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
13417
14149
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
@@ -13422,7 +14154,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13422
14154
|
if (!graph.hasNode(ep.infraId)) {
|
|
13423
14155
|
const node = {
|
|
13424
14156
|
id: ep.infraId,
|
|
13425
|
-
type:
|
|
14157
|
+
type: import_types44.NodeType.InfraNode,
|
|
13426
14158
|
name: ep.name,
|
|
13427
14159
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
13428
14160
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -13435,21 +14167,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13435
14167
|
}
|
|
13436
14168
|
if (ep.columns && ep.columns.length > 0) {
|
|
13437
14169
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13438
|
-
if (node.type ===
|
|
14170
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13439
14171
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13440
14172
|
...node,
|
|
13441
14173
|
columns: foldColumns(
|
|
13442
14174
|
node.columns,
|
|
13443
14175
|
ep.columns,
|
|
13444
|
-
|
|
13445
|
-
(0,
|
|
14176
|
+
import_types44.Provenance.EXTRACTED,
|
|
14177
|
+
(0, import_types44.confidenceForExtracted)(ep.confidenceKind)
|
|
13446
14178
|
)
|
|
13447
14179
|
});
|
|
13448
14180
|
}
|
|
13449
14181
|
}
|
|
13450
14182
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
13451
14183
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13452
|
-
if (node.type ===
|
|
14184
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13453
14185
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13454
14186
|
...node,
|
|
13455
14187
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -13457,7 +14189,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13457
14189
|
}
|
|
13458
14190
|
}
|
|
13459
14191
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
13460
|
-
const confidence = (0,
|
|
14192
|
+
const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
|
|
13461
14193
|
const relFile = toPosix(ep.evidence.file);
|
|
13462
14194
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
13463
14195
|
graph,
|
|
@@ -13467,7 +14199,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13467
14199
|
);
|
|
13468
14200
|
nodesAdded += n;
|
|
13469
14201
|
edgesAdded += e;
|
|
13470
|
-
if (!(0,
|
|
14202
|
+
if (!(0, import_types44.passesExtractedFloor)(confidence)) {
|
|
13471
14203
|
noteExtractedDropped({
|
|
13472
14204
|
source: fileNodeId,
|
|
13473
14205
|
target: ep.infraId,
|
|
@@ -13487,7 +14219,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13487
14219
|
source: fileNodeId,
|
|
13488
14220
|
target: ep.infraId,
|
|
13489
14221
|
type: edgeType,
|
|
13490
|
-
provenance:
|
|
14222
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
13491
14223
|
confidence,
|
|
13492
14224
|
evidence: ep.evidence
|
|
13493
14225
|
};
|
|
@@ -13510,7 +14242,7 @@ async function addCallEdges(graph, services) {
|
|
|
13510
14242
|
|
|
13511
14243
|
// src/extract/table-edges.ts
|
|
13512
14244
|
init_cjs_shims();
|
|
13513
|
-
var
|
|
14245
|
+
var import_types45 = require("@neat.is/types");
|
|
13514
14246
|
async function addTableEdges(graph, services) {
|
|
13515
14247
|
let nodesAdded = 0;
|
|
13516
14248
|
let edgesAdded = 0;
|
|
@@ -13538,20 +14270,20 @@ async function addTableEdges(graph, services) {
|
|
|
13538
14270
|
}
|
|
13539
14271
|
refs.push(...modelRefs);
|
|
13540
14272
|
for (const ref of refs) {
|
|
13541
|
-
const childId = (0,
|
|
13542
|
-
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);
|
|
13543
14275
|
if (childId === parentId) continue;
|
|
13544
14276
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
13545
14277
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
13546
|
-
const edgeId = (0,
|
|
14278
|
+
const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
|
|
13547
14279
|
if (graph.hasEdge(edgeId)) continue;
|
|
13548
14280
|
const edge = {
|
|
13549
14281
|
id: edgeId,
|
|
13550
14282
|
source: childId,
|
|
13551
14283
|
target: parentId,
|
|
13552
|
-
type:
|
|
13553
|
-
provenance:
|
|
13554
|
-
confidence: (0,
|
|
14284
|
+
type: import_types45.EdgeType.REFERENCES,
|
|
14285
|
+
provenance: import_types45.Provenance.EXTRACTED,
|
|
14286
|
+
confidence: (0, import_types45.confidenceForExtracted)("structural"),
|
|
13555
14287
|
evidence: ref.evidence
|
|
13556
14288
|
};
|
|
13557
14289
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -13564,7 +14296,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
13564
14296
|
if (graph.hasNode(id)) return 0;
|
|
13565
14297
|
const node = {
|
|
13566
14298
|
id,
|
|
13567
|
-
type:
|
|
14299
|
+
type: import_types45.NodeType.InfraNode,
|
|
13568
14300
|
name,
|
|
13569
14301
|
provider: "self",
|
|
13570
14302
|
kind: "sql-table"
|
|
@@ -13578,16 +14310,16 @@ init_cjs_shims();
|
|
|
13578
14310
|
|
|
13579
14311
|
// src/extract/infra/docker-compose.ts
|
|
13580
14312
|
init_cjs_shims();
|
|
13581
|
-
var
|
|
13582
|
-
var
|
|
14313
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
14314
|
+
var import_types47 = require("@neat.is/types");
|
|
13583
14315
|
|
|
13584
14316
|
// src/extract/infra/shared.ts
|
|
13585
14317
|
init_cjs_shims();
|
|
13586
|
-
var
|
|
14318
|
+
var import_types46 = require("@neat.is/types");
|
|
13587
14319
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
13588
14320
|
return {
|
|
13589
|
-
id: (0,
|
|
13590
|
-
type:
|
|
14321
|
+
id: (0, import_types46.infraId)(kind, name),
|
|
14322
|
+
type: import_types46.NodeType.InfraNode,
|
|
13591
14323
|
name,
|
|
13592
14324
|
provider,
|
|
13593
14325
|
kind,
|
|
@@ -13631,8 +14363,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
13631
14363
|
source: anchorId,
|
|
13632
14364
|
target: node.id,
|
|
13633
14365
|
type: edgeType,
|
|
13634
|
-
provenance:
|
|
13635
|
-
confidence: (0,
|
|
14366
|
+
provenance: import_types46.Provenance.EXTRACTED,
|
|
14367
|
+
confidence: (0, import_types46.confidenceForExtracted)("structural"),
|
|
13636
14368
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
13637
14369
|
};
|
|
13638
14370
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13649,7 +14381,7 @@ function dependsOnList(value) {
|
|
|
13649
14381
|
}
|
|
13650
14382
|
function serviceNameToServiceNode(name, services) {
|
|
13651
14383
|
for (const s of services) {
|
|
13652
|
-
if (s.node.name === name ||
|
|
14384
|
+
if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
|
|
13653
14385
|
}
|
|
13654
14386
|
return null;
|
|
13655
14387
|
}
|
|
@@ -13658,7 +14390,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13658
14390
|
let edgesAdded = 0;
|
|
13659
14391
|
let composePath = null;
|
|
13660
14392
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
13661
|
-
const abs =
|
|
14393
|
+
const abs = import_node_path57.default.join(scanPath, name);
|
|
13662
14394
|
if (await exists(abs)) {
|
|
13663
14395
|
composePath = abs;
|
|
13664
14396
|
break;
|
|
@@ -13671,13 +14403,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13671
14403
|
} catch (err) {
|
|
13672
14404
|
recordExtractionError(
|
|
13673
14405
|
"infra docker-compose",
|
|
13674
|
-
|
|
14406
|
+
import_node_path57.default.relative(scanPath, composePath),
|
|
13675
14407
|
err
|
|
13676
14408
|
);
|
|
13677
14409
|
return { nodesAdded, edgesAdded };
|
|
13678
14410
|
}
|
|
13679
14411
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
13680
|
-
const evidenceFile =
|
|
14412
|
+
const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
|
|
13681
14413
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
13682
14414
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
13683
14415
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -13699,15 +14431,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13699
14431
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
13700
14432
|
const targetId = composeNameToNodeId.get(dep);
|
|
13701
14433
|
if (!targetId) continue;
|
|
13702
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
14434
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
|
|
13703
14435
|
if (graph.hasEdge(edgeId)) continue;
|
|
13704
14436
|
const edge = {
|
|
13705
14437
|
id: edgeId,
|
|
13706
14438
|
source: sourceId,
|
|
13707
14439
|
target: targetId,
|
|
13708
|
-
type:
|
|
13709
|
-
provenance:
|
|
13710
|
-
confidence: (0,
|
|
14440
|
+
type: import_types47.EdgeType.DEPENDS_ON,
|
|
14441
|
+
provenance: import_types47.Provenance.EXTRACTED,
|
|
14442
|
+
confidence: (0, import_types47.confidenceForExtracted)("structural"),
|
|
13711
14443
|
evidence: { file: evidenceFile }
|
|
13712
14444
|
};
|
|
13713
14445
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13719,9 +14451,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13719
14451
|
|
|
13720
14452
|
// src/extract/infra/dockerfile.ts
|
|
13721
14453
|
init_cjs_shims();
|
|
13722
|
-
var
|
|
13723
|
-
var
|
|
13724
|
-
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");
|
|
13725
14457
|
function readDockerfile(content) {
|
|
13726
14458
|
let image = null;
|
|
13727
14459
|
const ports = [];
|
|
@@ -13750,15 +14482,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13750
14482
|
let nodesAdded = 0;
|
|
13751
14483
|
let edgesAdded = 0;
|
|
13752
14484
|
for (const service of services) {
|
|
13753
|
-
const dockerfilePath =
|
|
14485
|
+
const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
|
|
13754
14486
|
if (!await exists(dockerfilePath)) continue;
|
|
13755
14487
|
let content;
|
|
13756
14488
|
try {
|
|
13757
|
-
content = await
|
|
14489
|
+
content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
|
|
13758
14490
|
} catch (err) {
|
|
13759
14491
|
recordExtractionError(
|
|
13760
14492
|
"infra dockerfile",
|
|
13761
|
-
|
|
14493
|
+
import_node_path58.default.relative(scanPath, dockerfilePath),
|
|
13762
14494
|
err
|
|
13763
14495
|
);
|
|
13764
14496
|
continue;
|
|
@@ -13770,8 +14502,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13770
14502
|
graph.addNode(node.id, node);
|
|
13771
14503
|
nodesAdded++;
|
|
13772
14504
|
}
|
|
13773
|
-
const relDockerfile = toPosix(
|
|
13774
|
-
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));
|
|
13775
14507
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
13776
14508
|
graph,
|
|
13777
14509
|
service.pkg.name,
|
|
@@ -13780,15 +14512,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13780
14512
|
);
|
|
13781
14513
|
nodesAdded += fn;
|
|
13782
14514
|
edgesAdded += fe;
|
|
13783
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
14515
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
|
|
13784
14516
|
if (!graph.hasEdge(edgeId)) {
|
|
13785
14517
|
const edge = {
|
|
13786
14518
|
id: edgeId,
|
|
13787
14519
|
source: fileNodeId,
|
|
13788
14520
|
target: node.id,
|
|
13789
|
-
type:
|
|
13790
|
-
provenance:
|
|
13791
|
-
confidence: (0,
|
|
14521
|
+
type: import_types48.EdgeType.RUNS_ON,
|
|
14522
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14523
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13792
14524
|
evidence: {
|
|
13793
14525
|
file: evidenceFile,
|
|
13794
14526
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -13803,15 +14535,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13803
14535
|
graph.addNode(portNode.id, portNode);
|
|
13804
14536
|
nodesAdded++;
|
|
13805
14537
|
}
|
|
13806
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
14538
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
|
|
13807
14539
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
13808
14540
|
const portEdge = {
|
|
13809
14541
|
id: portEdgeId,
|
|
13810
14542
|
source: fileNodeId,
|
|
13811
14543
|
target: portNode.id,
|
|
13812
|
-
type:
|
|
13813
|
-
provenance:
|
|
13814
|
-
confidence: (0,
|
|
14544
|
+
type: import_types48.EdgeType.CONNECTS_TO,
|
|
14545
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14546
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13815
14547
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
13816
14548
|
};
|
|
13817
14549
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -13823,23 +14555,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13823
14555
|
|
|
13824
14556
|
// src/extract/infra/terraform.ts
|
|
13825
14557
|
init_cjs_shims();
|
|
13826
|
-
var
|
|
13827
|
-
var
|
|
13828
|
-
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");
|
|
13829
14561
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
13830
14562
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
13831
14563
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
13832
14564
|
if (depth > max) return [];
|
|
13833
14565
|
const out = [];
|
|
13834
|
-
const entries = await
|
|
14566
|
+
const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13835
14567
|
for (const entry of entries) {
|
|
13836
14568
|
if (entry.isDirectory()) {
|
|
13837
14569
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
13838
|
-
const child =
|
|
14570
|
+
const child = import_node_path59.default.join(start, entry.name);
|
|
13839
14571
|
if (await isPythonVenvDir(child)) continue;
|
|
13840
14572
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
13841
14573
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
13842
|
-
out.push(
|
|
14574
|
+
out.push(import_node_path59.default.join(start, entry.name));
|
|
13843
14575
|
}
|
|
13844
14576
|
}
|
|
13845
14577
|
return out;
|
|
@@ -13870,8 +14602,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13870
14602
|
let edgesAdded = 0;
|
|
13871
14603
|
const files = await walkTfFiles(scanPath);
|
|
13872
14604
|
for (const file of files) {
|
|
13873
|
-
const content = await
|
|
13874
|
-
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));
|
|
13875
14607
|
const resources = [];
|
|
13876
14608
|
const byKey = /* @__PURE__ */ new Map();
|
|
13877
14609
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -13906,16 +14638,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13906
14638
|
if (!target) continue;
|
|
13907
14639
|
if (seen.has(target.nodeId)) continue;
|
|
13908
14640
|
seen.add(target.nodeId);
|
|
13909
|
-
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);
|
|
13910
14642
|
if (graph.hasEdge(edgeId)) continue;
|
|
13911
14643
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
13912
14644
|
const edge = {
|
|
13913
14645
|
id: edgeId,
|
|
13914
14646
|
source: resource.nodeId,
|
|
13915
14647
|
target: target.nodeId,
|
|
13916
|
-
type:
|
|
13917
|
-
provenance:
|
|
13918
|
-
confidence: (0,
|
|
14648
|
+
type: import_types49.EdgeType.DEPENDS_ON,
|
|
14649
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
14650
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
13919
14651
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
13920
14652
|
};
|
|
13921
14653
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13928,8 +14660,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13928
14660
|
|
|
13929
14661
|
// src/extract/infra/k8s.ts
|
|
13930
14662
|
init_cjs_shims();
|
|
13931
|
-
var
|
|
13932
|
-
var
|
|
14663
|
+
var import_node_fs27 = require("fs");
|
|
14664
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
13933
14665
|
var import_yaml3 = require("yaml");
|
|
13934
14666
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
13935
14667
|
Service: "k8s-service",
|
|
@@ -13943,15 +14675,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
13943
14675
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
13944
14676
|
if (depth > max) return [];
|
|
13945
14677
|
const out = [];
|
|
13946
|
-
const entries = await
|
|
14678
|
+
const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13947
14679
|
for (const entry of entries) {
|
|
13948
14680
|
if (entry.isDirectory()) {
|
|
13949
14681
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
13950
|
-
const child =
|
|
14682
|
+
const child = import_node_path60.default.join(start, entry.name);
|
|
13951
14683
|
if (await isPythonVenvDir(child)) continue;
|
|
13952
14684
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
13953
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
13954
|
-
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));
|
|
13955
14687
|
}
|
|
13956
14688
|
}
|
|
13957
14689
|
return out;
|
|
@@ -13960,7 +14692,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
13960
14692
|
let nodesAdded = 0;
|
|
13961
14693
|
const files = await walkYamlFiles2(scanPath);
|
|
13962
14694
|
for (const file of files) {
|
|
13963
|
-
const content = await
|
|
14695
|
+
const content = await import_node_fs27.promises.readFile(file, "utf8");
|
|
13964
14696
|
let docs;
|
|
13965
14697
|
try {
|
|
13966
14698
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -13984,16 +14716,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
13984
14716
|
|
|
13985
14717
|
// src/extract/infra/cloudflare.ts
|
|
13986
14718
|
init_cjs_shims();
|
|
13987
|
-
var
|
|
13988
|
-
var
|
|
14719
|
+
var import_node_fs28 = require("fs");
|
|
14720
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
13989
14721
|
var import_smol_toml3 = require("smol-toml");
|
|
13990
|
-
var
|
|
14722
|
+
var import_types50 = require("@neat.is/types");
|
|
13991
14723
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
13992
14724
|
async function readWranglerConfig(dir) {
|
|
13993
14725
|
for (const filename of WRANGLER_FILENAMES) {
|
|
13994
|
-
const abs =
|
|
14726
|
+
const abs = import_node_path61.default.join(dir, filename);
|
|
13995
14727
|
if (!await exists(abs)) continue;
|
|
13996
|
-
const raw = await
|
|
14728
|
+
const raw = await import_node_fs28.promises.readFile(abs, "utf8");
|
|
13997
14729
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
13998
14730
|
return { config, relFile: filename, raw };
|
|
13999
14731
|
}
|
|
@@ -14035,8 +14767,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
14035
14767
|
source: anchorId,
|
|
14036
14768
|
target: node.id,
|
|
14037
14769
|
type: edgeType,
|
|
14038
|
-
provenance:
|
|
14039
|
-
confidence: (0,
|
|
14770
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14771
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14040
14772
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
14041
14773
|
};
|
|
14042
14774
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14054,11 +14786,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14054
14786
|
try {
|
|
14055
14787
|
read = await readWranglerConfig(service.dir);
|
|
14056
14788
|
} catch (err) {
|
|
14057
|
-
recordExtractionError("infra cloudflare",
|
|
14789
|
+
recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
|
|
14058
14790
|
continue;
|
|
14059
14791
|
}
|
|
14060
14792
|
if (!read || !read.config.name) continue;
|
|
14061
|
-
const evidenceFile = toPosix(
|
|
14793
|
+
const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
|
|
14062
14794
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
14063
14795
|
}
|
|
14064
14796
|
for (const worker of discovered) {
|
|
@@ -14070,7 +14802,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14070
14802
|
}
|
|
14071
14803
|
let anchorId = service.node.id;
|
|
14072
14804
|
if (config.main) {
|
|
14073
|
-
const entryRelPath = toPosix(
|
|
14805
|
+
const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
|
|
14074
14806
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
14075
14807
|
graph,
|
|
14076
14808
|
service.pkg.name,
|
|
@@ -14097,15 +14829,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14097
14829
|
nodesAdded++;
|
|
14098
14830
|
}
|
|
14099
14831
|
if (runtimeNode.id !== anchorId) {
|
|
14100
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
14832
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
|
|
14101
14833
|
if (!graph.hasEdge(runsOnId)) {
|
|
14102
14834
|
const edge = {
|
|
14103
14835
|
id: runsOnId,
|
|
14104
14836
|
source: anchorId,
|
|
14105
14837
|
target: runtimeNode.id,
|
|
14106
|
-
type:
|
|
14107
|
-
provenance:
|
|
14108
|
-
confidence: (0,
|
|
14838
|
+
type: import_types50.EdgeType.RUNS_ON,
|
|
14839
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14840
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14109
14841
|
evidence: {
|
|
14110
14842
|
file: evidenceFile,
|
|
14111
14843
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -14119,7 +14851,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14119
14851
|
const result = addResourceEdge(
|
|
14120
14852
|
graph,
|
|
14121
14853
|
anchorId,
|
|
14122
|
-
|
|
14854
|
+
import_types50.EdgeType.CONNECTS_TO,
|
|
14123
14855
|
"cloudflare-route",
|
|
14124
14856
|
route,
|
|
14125
14857
|
evidenceFile,
|
|
@@ -14143,7 +14875,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14143
14875
|
const result = addResourceEdge(
|
|
14144
14876
|
graph,
|
|
14145
14877
|
anchorId,
|
|
14146
|
-
|
|
14878
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14147
14879
|
group.kind,
|
|
14148
14880
|
name,
|
|
14149
14881
|
evidenceFile,
|
|
@@ -14157,7 +14889,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14157
14889
|
const result = addResourceEdge(
|
|
14158
14890
|
graph,
|
|
14159
14891
|
anchorId,
|
|
14160
|
-
|
|
14892
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14161
14893
|
"cloudflare-cron",
|
|
14162
14894
|
cron,
|
|
14163
14895
|
evidenceFile,
|
|
@@ -14170,7 +14902,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14170
14902
|
const result = addResourceEdge(
|
|
14171
14903
|
graph,
|
|
14172
14904
|
anchorId,
|
|
14173
|
-
|
|
14905
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14174
14906
|
"cloudflare-env-var",
|
|
14175
14907
|
varName,
|
|
14176
14908
|
evidenceFile,
|
|
@@ -14183,15 +14915,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14183
14915
|
if (!svc.service) continue;
|
|
14184
14916
|
const target = workerIndex.get(svc.service);
|
|
14185
14917
|
if (target && target.anchorId !== anchorId) {
|
|
14186
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
14918
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
|
|
14187
14919
|
if (!graph.hasEdge(edgeId)) {
|
|
14188
14920
|
const edge = {
|
|
14189
14921
|
id: edgeId,
|
|
14190
14922
|
source: anchorId,
|
|
14191
14923
|
target: target.anchorId,
|
|
14192
|
-
type:
|
|
14193
|
-
provenance:
|
|
14194
|
-
confidence: (0,
|
|
14924
|
+
type: import_types50.EdgeType.CALLS,
|
|
14925
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14926
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14195
14927
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
14196
14928
|
};
|
|
14197
14929
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14202,7 +14934,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14202
14934
|
const result = addResourceEdge(
|
|
14203
14935
|
graph,
|
|
14204
14936
|
anchorId,
|
|
14205
|
-
|
|
14937
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14206
14938
|
"cloudflare-service-binding",
|
|
14207
14939
|
svc.service,
|
|
14208
14940
|
evidenceFile,
|
|
@@ -14217,24 +14949,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14217
14949
|
|
|
14218
14950
|
// src/extract/infra/vercel.ts
|
|
14219
14951
|
init_cjs_shims();
|
|
14220
|
-
var
|
|
14221
|
-
var
|
|
14222
|
-
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");
|
|
14223
14955
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
14224
14956
|
async function readVercelConfig(dir) {
|
|
14225
14957
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
14226
|
-
const abs =
|
|
14958
|
+
const abs = import_node_path62.default.join(dir, filename);
|
|
14227
14959
|
if (!await exists(abs)) continue;
|
|
14228
|
-
const raw = await
|
|
14960
|
+
const raw = await import_node_fs29.promises.readFile(abs, "utf8");
|
|
14229
14961
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
14230
14962
|
return { config, relFile: filename, raw };
|
|
14231
14963
|
}
|
|
14232
14964
|
return null;
|
|
14233
14965
|
}
|
|
14234
14966
|
async function readLinkedProjectName(dir) {
|
|
14235
|
-
const abs =
|
|
14967
|
+
const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
|
|
14236
14968
|
if (!await exists(abs)) return void 0;
|
|
14237
|
-
const parsed = JSON.parse(await
|
|
14969
|
+
const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
|
|
14238
14970
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
14239
14971
|
}
|
|
14240
14972
|
function routeSource(route) {
|
|
@@ -14250,7 +14982,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14250
14982
|
read = await readVercelConfig(service.dir);
|
|
14251
14983
|
projectName = await readLinkedProjectName(service.dir);
|
|
14252
14984
|
} catch (err) {
|
|
14253
|
-
recordExtractionError("infra vercel",
|
|
14985
|
+
recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
|
|
14254
14986
|
continue;
|
|
14255
14987
|
}
|
|
14256
14988
|
if (!read && !projectName) continue;
|
|
@@ -14266,7 +14998,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14266
14998
|
const anchorId = service.node.id;
|
|
14267
14999
|
if (!read) continue;
|
|
14268
15000
|
const { config, relFile, raw } = read;
|
|
14269
|
-
const evidenceFile = toPosix(
|
|
15001
|
+
const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
|
|
14270
15002
|
const add = (edgeType, kind, name) => {
|
|
14271
15003
|
if (!name) return;
|
|
14272
15004
|
const result = emitPlatformResourceEdge(
|
|
@@ -14282,12 +15014,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14282
15014
|
nodesAdded += result.nodesAdded;
|
|
14283
15015
|
edgesAdded += result.edgesAdded;
|
|
14284
15016
|
};
|
|
14285
|
-
add(
|
|
14286
|
-
for (const cron of config.crons ?? []) add(
|
|
14287
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
14288
|
-
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);
|
|
14289
15021
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
14290
|
-
add(
|
|
15022
|
+
add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
14291
15023
|
}
|
|
14292
15024
|
}
|
|
14293
15025
|
return { nodesAdded, edgesAdded };
|
|
@@ -14295,16 +15027,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14295
15027
|
|
|
14296
15028
|
// src/extract/infra/railway.ts
|
|
14297
15029
|
init_cjs_shims();
|
|
14298
|
-
var
|
|
14299
|
-
var
|
|
15030
|
+
var import_node_fs30 = require("fs");
|
|
15031
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
14300
15032
|
var import_smol_toml4 = require("smol-toml");
|
|
14301
|
-
var
|
|
15033
|
+
var import_types52 = require("@neat.is/types");
|
|
14302
15034
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
14303
15035
|
async function readRailwayConfig(dir) {
|
|
14304
15036
|
for (const filename of RAILWAY_FILENAMES) {
|
|
14305
|
-
const abs =
|
|
15037
|
+
const abs = import_node_path63.default.join(dir, filename);
|
|
14306
15038
|
if (!await exists(abs)) continue;
|
|
14307
|
-
const raw = await
|
|
15039
|
+
const raw = await import_node_fs30.promises.readFile(abs, "utf8");
|
|
14308
15040
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14309
15041
|
return { config, relFile: filename, raw };
|
|
14310
15042
|
}
|
|
@@ -14318,7 +15050,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14318
15050
|
try {
|
|
14319
15051
|
read = await readRailwayConfig(service.dir);
|
|
14320
15052
|
} catch (err) {
|
|
14321
|
-
recordExtractionError("infra railway",
|
|
15053
|
+
recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
|
|
14322
15054
|
continue;
|
|
14323
15055
|
}
|
|
14324
15056
|
if (!read) continue;
|
|
@@ -14328,7 +15060,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14328
15060
|
}
|
|
14329
15061
|
const anchorId = service.node.id;
|
|
14330
15062
|
const { config, relFile, raw } = read;
|
|
14331
|
-
const evidenceFile = toPosix(
|
|
15063
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
|
|
14332
15064
|
const add = (edgeType, kind, name) => {
|
|
14333
15065
|
if (!name) return;
|
|
14334
15066
|
const result = emitPlatformResourceEdge(
|
|
@@ -14344,24 +15076,24 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14344
15076
|
nodesAdded += result.nodesAdded;
|
|
14345
15077
|
edgesAdded += result.edgesAdded;
|
|
14346
15078
|
};
|
|
14347
|
-
add(
|
|
14348
|
-
add(
|
|
14349
|
-
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);
|
|
14350
15082
|
}
|
|
14351
15083
|
return { nodesAdded, edgesAdded };
|
|
14352
15084
|
}
|
|
14353
15085
|
|
|
14354
15086
|
// src/extract/infra/supabase.ts
|
|
14355
15087
|
init_cjs_shims();
|
|
14356
|
-
var
|
|
14357
|
-
var
|
|
15088
|
+
var import_node_fs31 = require("fs");
|
|
15089
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
14358
15090
|
var import_smol_toml5 = require("smol-toml");
|
|
14359
|
-
var
|
|
15091
|
+
var import_types53 = require("@neat.is/types");
|
|
14360
15092
|
async function readSupabaseConfig(dir) {
|
|
14361
|
-
const relFile =
|
|
14362
|
-
const abs =
|
|
15093
|
+
const relFile = import_node_path64.default.join("supabase", "config.toml");
|
|
15094
|
+
const abs = import_node_path64.default.join(dir, relFile);
|
|
14363
15095
|
if (!await exists(abs)) return null;
|
|
14364
|
-
const raw = await
|
|
15096
|
+
const raw = await import_node_fs31.promises.readFile(abs, "utf8");
|
|
14365
15097
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
14366
15098
|
return { config, relFile, raw };
|
|
14367
15099
|
}
|
|
@@ -14373,7 +15105,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14373
15105
|
try {
|
|
14374
15106
|
read = await readSupabaseConfig(service.dir);
|
|
14375
15107
|
} catch (err) {
|
|
14376
|
-
recordExtractionError("infra supabase",
|
|
15108
|
+
recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
|
|
14377
15109
|
continue;
|
|
14378
15110
|
}
|
|
14379
15111
|
if (!read) continue;
|
|
@@ -14388,7 +15120,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14388
15120
|
});
|
|
14389
15121
|
}
|
|
14390
15122
|
const anchorId = service.node.id;
|
|
14391
|
-
const evidenceFile = toPosix(
|
|
15123
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
|
|
14392
15124
|
const add = (edgeType, kind, name) => {
|
|
14393
15125
|
if (!name) return;
|
|
14394
15126
|
const result = emitPlatformResourceEdge(
|
|
@@ -14404,10 +15136,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14404
15136
|
nodesAdded += result.nodesAdded;
|
|
14405
15137
|
edgesAdded += result.edgesAdded;
|
|
14406
15138
|
};
|
|
14407
|
-
add(
|
|
14408
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
14409
|
-
if (config.storage) add(
|
|
14410
|
-
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");
|
|
14411
15143
|
}
|
|
14412
15144
|
return { nodesAdded, edgesAdded };
|
|
14413
15145
|
}
|
|
@@ -14430,14 +15162,14 @@ async function addInfra(graph, scanPath, services) {
|
|
|
14430
15162
|
|
|
14431
15163
|
// src/extract/zod-shapes.ts
|
|
14432
15164
|
init_cjs_shims();
|
|
14433
|
-
var
|
|
14434
|
-
var
|
|
15165
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15166
|
+
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
14435
15167
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
14436
|
-
var
|
|
15168
|
+
var import_types54 = require("@neat.is/types");
|
|
14437
15169
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
14438
15170
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
14439
15171
|
function parserForExt4(ext) {
|
|
14440
|
-
const p = new
|
|
15172
|
+
const p = new import_tree_sitter17.default();
|
|
14441
15173
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
14442
15174
|
return p;
|
|
14443
15175
|
}
|
|
@@ -14525,7 +15257,7 @@ function topLevelSchemas(root) {
|
|
|
14525
15257
|
}
|
|
14526
15258
|
function zodShapesFromFile(file, serviceDir) {
|
|
14527
15259
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
14528
|
-
const tree = parseSource3(parserForExt4(
|
|
15260
|
+
const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
|
|
14529
15261
|
const out = [];
|
|
14530
15262
|
const seen = /* @__PURE__ */ new Set();
|
|
14531
15263
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14539,11 +15271,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
14539
15271
|
seen.add(name);
|
|
14540
15272
|
const line = call.startPosition.row + 1;
|
|
14541
15273
|
out.push({
|
|
14542
|
-
infraId: (0,
|
|
15274
|
+
infraId: (0, import_types54.infraId)("zod-schema", name),
|
|
14543
15275
|
name,
|
|
14544
15276
|
fields,
|
|
14545
15277
|
evidence: {
|
|
14546
|
-
file:
|
|
15278
|
+
file: import_node_path65.default.relative(serviceDir, file.path),
|
|
14547
15279
|
line,
|
|
14548
15280
|
snippet: snippet(file.content, line)
|
|
14549
15281
|
}
|
|
@@ -14574,7 +15306,7 @@ async function addZodShapes(graph, services) {
|
|
|
14574
15306
|
if (!graph.hasNode(shape.infraId)) {
|
|
14575
15307
|
const node = {
|
|
14576
15308
|
id: shape.infraId,
|
|
14577
|
-
type:
|
|
15309
|
+
type: import_types54.NodeType.InfraNode,
|
|
14578
15310
|
name: shape.name,
|
|
14579
15311
|
provider: "self",
|
|
14580
15312
|
kind: "zod-schema"
|
|
@@ -14584,14 +15316,14 @@ async function addZodShapes(graph, services) {
|
|
|
14584
15316
|
}
|
|
14585
15317
|
if (shape.fields.length > 0) {
|
|
14586
15318
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
14587
|
-
if (node.type ===
|
|
15319
|
+
if (node.type === import_types54.NodeType.InfraNode) {
|
|
14588
15320
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
14589
15321
|
...node,
|
|
14590
15322
|
columns: foldColumns(
|
|
14591
15323
|
node.columns,
|
|
14592
15324
|
shape.fields,
|
|
14593
|
-
|
|
14594
|
-
(0,
|
|
15325
|
+
import_types54.Provenance.EXTRACTED,
|
|
15326
|
+
(0, import_types54.confidenceForExtracted)("structural")
|
|
14595
15327
|
)
|
|
14596
15328
|
});
|
|
14597
15329
|
}
|
|
@@ -14605,15 +15337,15 @@ async function addZodShapes(graph, services) {
|
|
|
14605
15337
|
);
|
|
14606
15338
|
nodesAdded += n;
|
|
14607
15339
|
edgesAdded += e;
|
|
14608
|
-
const edgeId = (0,
|
|
15340
|
+
const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
|
|
14609
15341
|
if (!graph.hasEdge(edgeId)) {
|
|
14610
15342
|
const edge = {
|
|
14611
15343
|
id: edgeId,
|
|
14612
15344
|
source: fileNodeId,
|
|
14613
15345
|
target: shape.infraId,
|
|
14614
|
-
type:
|
|
14615
|
-
provenance:
|
|
14616
|
-
confidence: (0,
|
|
15346
|
+
type: import_types54.EdgeType.CONTAINS,
|
|
15347
|
+
provenance: import_types54.Provenance.EXTRACTED,
|
|
15348
|
+
confidence: (0, import_types54.confidenceForExtracted)("structural"),
|
|
14617
15349
|
evidence: shape.evidence
|
|
14618
15350
|
};
|
|
14619
15351
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -14627,7 +15359,7 @@ async function addZodShapes(graph, services) {
|
|
|
14627
15359
|
|
|
14628
15360
|
// src/extract/firestore-rules.ts
|
|
14629
15361
|
init_cjs_shims();
|
|
14630
|
-
var
|
|
15362
|
+
var import_types55 = require("@neat.is/types");
|
|
14631
15363
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
14632
15364
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
14633
15365
|
function stripComments(src) {
|
|
@@ -14767,7 +15499,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
14767
15499
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
14768
15500
|
graph.forEachNode((id, attrs) => {
|
|
14769
15501
|
const node = attrs;
|
|
14770
|
-
if (node.type !==
|
|
15502
|
+
if (node.type !== import_types55.NodeType.InfraNode) return;
|
|
14771
15503
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
14772
15504
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
14773
15505
|
if (!fields || fields.size === 0) return;
|
|
@@ -14780,17 +15512,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
14780
15512
|
}
|
|
14781
15513
|
|
|
14782
15514
|
// src/extract/index.ts
|
|
14783
|
-
var
|
|
15515
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
14784
15516
|
|
|
14785
15517
|
// src/extract/retire.ts
|
|
14786
15518
|
init_cjs_shims();
|
|
14787
|
-
var
|
|
14788
|
-
var
|
|
14789
|
-
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");
|
|
14790
15522
|
function dropOrphanedFileNodes(graph) {
|
|
14791
15523
|
const orphans = [];
|
|
14792
15524
|
graph.forEachNode((id, attrs) => {
|
|
14793
|
-
if (attrs.type !==
|
|
15525
|
+
if (attrs.type !== import_types56.NodeType.FileNode) return;
|
|
14794
15526
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
14795
15527
|
orphans.push(id);
|
|
14796
15528
|
}
|
|
@@ -14803,14 +15535,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
14803
15535
|
const bases = [scanPath, ...serviceDirs];
|
|
14804
15536
|
graph.forEachEdge((id, attrs) => {
|
|
14805
15537
|
const edge = attrs;
|
|
14806
|
-
if (edge.provenance !==
|
|
15538
|
+
if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
|
|
14807
15539
|
const evidenceFile = edge.evidence?.file;
|
|
14808
15540
|
if (!evidenceFile) return;
|
|
14809
|
-
if (
|
|
14810
|
-
if (!(0,
|
|
15541
|
+
if (import_node_path66.default.isAbsolute(evidenceFile)) {
|
|
15542
|
+
if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
|
|
14811
15543
|
return;
|
|
14812
15544
|
}
|
|
14813
|
-
const found = bases.some((base) => (0,
|
|
15545
|
+
const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
|
|
14814
15546
|
if (!found) toDrop.push(id);
|
|
14815
15547
|
});
|
|
14816
15548
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -14867,7 +15599,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14867
15599
|
}
|
|
14868
15600
|
const droppedEntries = drainDroppedExtracted();
|
|
14869
15601
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
14870
|
-
const rejectedPath =
|
|
15602
|
+
const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
14871
15603
|
try {
|
|
14872
15604
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
14873
15605
|
} catch (err) {
|
|
@@ -14901,9 +15633,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14901
15633
|
|
|
14902
15634
|
// src/persist.ts
|
|
14903
15635
|
init_cjs_shims();
|
|
14904
|
-
var
|
|
14905
|
-
var
|
|
14906
|
-
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");
|
|
14907
15639
|
var SCHEMA_VERSION = 7;
|
|
14908
15640
|
function migrateV1ToV2(payload) {
|
|
14909
15641
|
const nodes = payload.graph.nodes;
|
|
@@ -14927,7 +15659,7 @@ function migrateV5ToV6(payload) {
|
|
|
14927
15659
|
if (Array.isArray(nodes)) {
|
|
14928
15660
|
for (const node of nodes) {
|
|
14929
15661
|
const attrs = node.attributes;
|
|
14930
|
-
if (!attrs || attrs.type !==
|
|
15662
|
+
if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
|
|
14931
15663
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
14932
15664
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
14933
15665
|
}
|
|
@@ -14943,12 +15675,12 @@ function migrateV2ToV3(payload) {
|
|
|
14943
15675
|
for (const edge of edges) {
|
|
14944
15676
|
const attrs = edge.attributes;
|
|
14945
15677
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
14946
|
-
attrs.provenance =
|
|
15678
|
+
attrs.provenance = import_types57.Provenance.OBSERVED;
|
|
14947
15679
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
14948
15680
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
14949
15681
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
14950
15682
|
if (type && source && target) {
|
|
14951
|
-
const newId = (0,
|
|
15683
|
+
const newId = (0, import_types57.observedEdgeId)(source, target, type);
|
|
14952
15684
|
attrs.id = newId;
|
|
14953
15685
|
if (edge.key) edge.key = newId;
|
|
14954
15686
|
}
|
|
@@ -14957,7 +15689,7 @@ function migrateV2ToV3(payload) {
|
|
|
14957
15689
|
return { ...payload, schemaVersion: 3 };
|
|
14958
15690
|
}
|
|
14959
15691
|
async function ensureDir(filePath) {
|
|
14960
|
-
await
|
|
15692
|
+
await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
|
|
14961
15693
|
}
|
|
14962
15694
|
async function saveGraphToDisk(graph, outPath) {
|
|
14963
15695
|
await ensureDir(outPath);
|
|
@@ -14967,13 +15699,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
14967
15699
|
graph: graph.export()
|
|
14968
15700
|
};
|
|
14969
15701
|
const tmp = `${outPath}.tmp`;
|
|
14970
|
-
await
|
|
14971
|
-
await
|
|
15702
|
+
await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
15703
|
+
await import_node_fs33.promises.rename(tmp, outPath);
|
|
14972
15704
|
}
|
|
14973
15705
|
async function loadGraphFromDisk(graph, outPath) {
|
|
14974
15706
|
let raw;
|
|
14975
15707
|
try {
|
|
14976
|
-
raw = await
|
|
15708
|
+
raw = await import_node_fs33.promises.readFile(outPath, "utf8");
|
|
14977
15709
|
} catch (err) {
|
|
14978
15710
|
if (err.code === "ENOENT") return;
|
|
14979
15711
|
throw err;
|
|
@@ -15049,19 +15781,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
15049
15781
|
init_cjs_shims();
|
|
15050
15782
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15051
15783
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15052
|
-
var
|
|
15784
|
+
var import_types92 = require("@neat.is/types");
|
|
15053
15785
|
|
|
15054
15786
|
// src/extend/index.ts
|
|
15055
15787
|
init_cjs_shims();
|
|
15056
|
-
var
|
|
15057
|
-
var
|
|
15788
|
+
var import_node_fs35 = require("fs");
|
|
15789
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
15058
15790
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
15059
15791
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
15060
15792
|
|
|
15061
15793
|
// src/installers/package-manager.ts
|
|
15062
15794
|
init_cjs_shims();
|
|
15063
|
-
var
|
|
15064
|
-
var
|
|
15795
|
+
var import_node_fs34 = require("fs");
|
|
15796
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
15065
15797
|
var import_node_child_process = require("child_process");
|
|
15066
15798
|
var LOCKFILE_PRIORITY = [
|
|
15067
15799
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -15076,29 +15808,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
15076
15808
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
15077
15809
|
async function exists2(p) {
|
|
15078
15810
|
try {
|
|
15079
|
-
await
|
|
15811
|
+
await import_node_fs34.promises.access(p);
|
|
15080
15812
|
return true;
|
|
15081
15813
|
} catch {
|
|
15082
15814
|
return false;
|
|
15083
15815
|
}
|
|
15084
15816
|
}
|
|
15085
15817
|
async function detectPackageManager(serviceDir) {
|
|
15086
|
-
let dir =
|
|
15818
|
+
let dir = import_node_path69.default.resolve(serviceDir);
|
|
15087
15819
|
const stops = /* @__PURE__ */ new Set();
|
|
15088
15820
|
for (let i = 0; i < 64; i++) {
|
|
15089
15821
|
if (stops.has(dir)) break;
|
|
15090
15822
|
stops.add(dir);
|
|
15091
15823
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
15092
|
-
const lockPath =
|
|
15824
|
+
const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
|
|
15093
15825
|
if (await exists2(lockPath)) {
|
|
15094
15826
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
15095
15827
|
}
|
|
15096
15828
|
}
|
|
15097
|
-
const parent =
|
|
15829
|
+
const parent = import_node_path69.default.dirname(dir);
|
|
15098
15830
|
if (parent === dir) break;
|
|
15099
15831
|
dir = parent;
|
|
15100
15832
|
}
|
|
15101
|
-
return { pm: "npm", cwd:
|
|
15833
|
+
return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
15102
15834
|
}
|
|
15103
15835
|
async function runPackageManagerInstall(cmd) {
|
|
15104
15836
|
return new Promise((resolve) => {
|
|
@@ -15140,15 +15872,15 @@ ${err.message}`
|
|
|
15140
15872
|
// src/extend/index.ts
|
|
15141
15873
|
async function fileExists2(p) {
|
|
15142
15874
|
try {
|
|
15143
|
-
await
|
|
15875
|
+
await import_node_fs35.promises.access(p);
|
|
15144
15876
|
return true;
|
|
15145
15877
|
} catch {
|
|
15146
15878
|
return false;
|
|
15147
15879
|
}
|
|
15148
15880
|
}
|
|
15149
15881
|
async function readPackageJson(scanPath) {
|
|
15150
|
-
const pkgPath =
|
|
15151
|
-
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");
|
|
15152
15884
|
return JSON.parse(raw);
|
|
15153
15885
|
}
|
|
15154
15886
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -15161,27 +15893,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
15161
15893
|
]);
|
|
15162
15894
|
async function findHookFiles(scanPath) {
|
|
15163
15895
|
const found = [];
|
|
15164
|
-
const
|
|
15165
|
-
const entries = await
|
|
15896
|
+
const walk10 = async (dir) => {
|
|
15897
|
+
const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
15166
15898
|
for (const entry of entries) {
|
|
15167
15899
|
if (entry.isDirectory()) {
|
|
15168
15900
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
15169
|
-
await
|
|
15901
|
+
await walk10(import_node_path70.default.join(dir, entry.name));
|
|
15170
15902
|
} else if (entry.isFile()) {
|
|
15171
15903
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
15172
|
-
const rel =
|
|
15173
|
-
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("/"));
|
|
15174
15906
|
}
|
|
15175
15907
|
}
|
|
15176
15908
|
}
|
|
15177
15909
|
};
|
|
15178
|
-
await
|
|
15910
|
+
await walk10(scanPath);
|
|
15179
15911
|
return found.sort();
|
|
15180
15912
|
}
|
|
15181
15913
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
15182
15914
|
let fallback = null;
|
|
15183
15915
|
for (const file of hookFiles) {
|
|
15184
|
-
const content = await
|
|
15916
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
|
|
15185
15917
|
const patched = splicedContent(content, snippet2);
|
|
15186
15918
|
if (patched !== null) return { file, content, patched };
|
|
15187
15919
|
if (fallback === null) fallback = { file, content };
|
|
@@ -15189,12 +15921,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
15189
15921
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
15190
15922
|
}
|
|
15191
15923
|
function extendLogPath() {
|
|
15192
|
-
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");
|
|
15193
15925
|
}
|
|
15194
15926
|
async function appendExtendLog(entry) {
|
|
15195
15927
|
const logPath = extendLogPath();
|
|
15196
|
-
await
|
|
15197
|
-
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");
|
|
15198
15930
|
}
|
|
15199
15931
|
function splicedContent(fileContent, snippet2) {
|
|
15200
15932
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -15252,7 +15984,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
15252
15984
|
}
|
|
15253
15985
|
async function describeProjectInstrumentation(ctx) {
|
|
15254
15986
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15255
|
-
const envNeat = await fileExists2(
|
|
15987
|
+
const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
|
|
15256
15988
|
const registryInstrPackages = new Set(
|
|
15257
15989
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
15258
15990
|
);
|
|
@@ -15274,7 +16006,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
15274
16006
|
);
|
|
15275
16007
|
}
|
|
15276
16008
|
for (const file of hookFiles) {
|
|
15277
|
-
const content = await
|
|
16009
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
|
|
15278
16010
|
if (content.includes(args.registration_snippet)) {
|
|
15279
16011
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
15280
16012
|
}
|
|
@@ -15286,18 +16018,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
15286
16018
|
);
|
|
15287
16019
|
}
|
|
15288
16020
|
const primaryFile = primary.file;
|
|
15289
|
-
const primaryPath =
|
|
16021
|
+
const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
|
|
15290
16022
|
const filesTouched = [];
|
|
15291
16023
|
const depsAdded = [];
|
|
15292
|
-
const pkgPath =
|
|
16024
|
+
const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
|
|
15293
16025
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15294
16026
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
15295
16027
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
15296
|
-
await
|
|
16028
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15297
16029
|
filesTouched.push("package.json");
|
|
15298
16030
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
15299
16031
|
}
|
|
15300
|
-
await
|
|
16032
|
+
await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
15301
16033
|
filesTouched.push(primaryFile);
|
|
15302
16034
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
15303
16035
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -15328,7 +16060,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
15328
16060
|
};
|
|
15329
16061
|
}
|
|
15330
16062
|
for (const file of hookFiles) {
|
|
15331
|
-
const content = await
|
|
16063
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
|
|
15332
16064
|
if (content.includes(args.registration_snippet)) {
|
|
15333
16065
|
return {
|
|
15334
16066
|
library: args.library,
|
|
@@ -15363,28 +16095,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
15363
16095
|
if (!await fileExists2(logPath)) {
|
|
15364
16096
|
return { undone: false, message: "no apply found for library" };
|
|
15365
16097
|
}
|
|
15366
|
-
const raw = await
|
|
16098
|
+
const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
|
|
15367
16099
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
15368
16100
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
15369
16101
|
if (!match) {
|
|
15370
16102
|
return { undone: false, message: "no apply found for library" };
|
|
15371
16103
|
}
|
|
15372
|
-
const pkgPath =
|
|
16104
|
+
const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
|
|
15373
16105
|
if (await fileExists2(pkgPath)) {
|
|
15374
16106
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15375
16107
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
15376
16108
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
15377
16109
|
pkg.dependencies = rest;
|
|
15378
|
-
await
|
|
16110
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15379
16111
|
}
|
|
15380
16112
|
}
|
|
15381
16113
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15382
16114
|
for (const file of hookFiles) {
|
|
15383
|
-
const filePath =
|
|
15384
|
-
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");
|
|
15385
16117
|
if (content.includes(match.registration_snippet)) {
|
|
15386
16118
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
15387
|
-
await
|
|
16119
|
+
await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
|
|
15388
16120
|
break;
|
|
15389
16121
|
}
|
|
15390
16122
|
}
|
|
@@ -15396,39 +16128,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
15396
16128
|
|
|
15397
16129
|
// src/divergences.ts
|
|
15398
16130
|
init_cjs_shims();
|
|
15399
|
-
var
|
|
16131
|
+
var import_types58 = require("@neat.is/types");
|
|
15400
16132
|
function bucketKey(source, target, type) {
|
|
15401
16133
|
return `${type}|${source}|${target}`;
|
|
15402
16134
|
}
|
|
15403
16135
|
function bucketSourceFor(graph, edge) {
|
|
15404
|
-
if (edge.type !==
|
|
15405
|
-
const parsed = (0,
|
|
16136
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
|
|
16137
|
+
const parsed = (0, import_types58.parseFileId)(edge.source);
|
|
15406
16138
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
15407
16139
|
const target = graph.getNodeAttributes(edge.target);
|
|
15408
|
-
if (target.type !==
|
|
15409
|
-
return (0,
|
|
16140
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
|
|
16141
|
+
return (0, import_types58.serviceId)(parsed.service);
|
|
15410
16142
|
}
|
|
15411
16143
|
function bucketEdges(graph) {
|
|
15412
16144
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
15413
16145
|
graph.forEachEdge((id, attrs) => {
|
|
15414
16146
|
const e = attrs;
|
|
15415
|
-
const parsed = (0,
|
|
16147
|
+
const parsed = (0, import_types58.parseEdgeId)(id);
|
|
15416
16148
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
15417
16149
|
const source = bucketSourceFor(graph, e);
|
|
15418
16150
|
const key = bucketKey(source, e.target, e.type);
|
|
15419
16151
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
15420
16152
|
switch (provenance) {
|
|
15421
|
-
case
|
|
16153
|
+
case import_types58.Provenance.EXTRACTED:
|
|
15422
16154
|
cur.extracted = e;
|
|
15423
16155
|
break;
|
|
15424
|
-
case
|
|
16156
|
+
case import_types58.Provenance.OBSERVED:
|
|
15425
16157
|
cur.observed = e;
|
|
15426
16158
|
break;
|
|
15427
|
-
case
|
|
16159
|
+
case import_types58.Provenance.INFERRED:
|
|
15428
16160
|
cur.inferred = e;
|
|
15429
16161
|
break;
|
|
15430
16162
|
default:
|
|
15431
|
-
if (e.provenance ===
|
|
16163
|
+
if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
|
|
15432
16164
|
}
|
|
15433
16165
|
buckets2.set(key, cur);
|
|
15434
16166
|
});
|
|
@@ -15437,22 +16169,22 @@ function bucketEdges(graph) {
|
|
|
15437
16169
|
function nodeIsFrontier(graph, nodeId) {
|
|
15438
16170
|
if (!graph.hasNode(nodeId)) return false;
|
|
15439
16171
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15440
|
-
return attrs.type ===
|
|
16172
|
+
return attrs.type === import_types58.NodeType.FrontierNode;
|
|
15441
16173
|
}
|
|
15442
16174
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
15443
16175
|
if (!graph.hasNode(nodeId)) return false;
|
|
15444
16176
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15445
|
-
return attrs.type ===
|
|
16177
|
+
return attrs.type === import_types58.NodeType.WebSocketChannelNode;
|
|
15446
16178
|
}
|
|
15447
16179
|
function nodeIsServerAction(graph, nodeId) {
|
|
15448
16180
|
if (!graph.hasNode(nodeId)) return false;
|
|
15449
16181
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15450
|
-
return attrs.type ===
|
|
16182
|
+
return attrs.type === import_types58.NodeType.ServerActionNode;
|
|
15451
16183
|
}
|
|
15452
16184
|
function nodeIsSymbol(graph, nodeId) {
|
|
15453
16185
|
if (!graph.hasNode(nodeId)) return false;
|
|
15454
16186
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15455
|
-
return attrs.type ===
|
|
16187
|
+
return attrs.type === import_types58.NodeType.SymbolNode;
|
|
15456
16188
|
}
|
|
15457
16189
|
function clampConfidence(n) {
|
|
15458
16190
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -15472,14 +16204,14 @@ function gradedConfidence(edge) {
|
|
|
15472
16204
|
return clampConfidence(confidenceForEdge(edge));
|
|
15473
16205
|
}
|
|
15474
16206
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
15475
|
-
|
|
15476
|
-
|
|
15477
|
-
|
|
15478
|
-
|
|
16207
|
+
import_types58.EdgeType.CALLS,
|
|
16208
|
+
import_types58.EdgeType.CONNECTS_TO,
|
|
16209
|
+
import_types58.EdgeType.PUBLISHES_TO,
|
|
16210
|
+
import_types58.EdgeType.CONSUMES_FROM
|
|
15479
16211
|
]);
|
|
15480
16212
|
function detectMissingDivergences(graph, bucket) {
|
|
15481
16213
|
const out = [];
|
|
15482
|
-
if (bucket.type ===
|
|
16214
|
+
if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
|
|
15483
16215
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
15484
16216
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
15485
16217
|
if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
|
|
@@ -15521,7 +16253,7 @@ function declaredHostFor(svc) {
|
|
|
15521
16253
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
15522
16254
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15523
16255
|
const e = graph.getEdgeAttributes(edgeId);
|
|
15524
|
-
if (e.type ===
|
|
16256
|
+
if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
|
|
15525
16257
|
return true;
|
|
15526
16258
|
}
|
|
15527
16259
|
}
|
|
@@ -15534,10 +16266,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
15534
16266
|
const out = [];
|
|
15535
16267
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15536
16268
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15537
|
-
if (edge.type !==
|
|
15538
|
-
if (edge.provenance !==
|
|
16269
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16270
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15539
16271
|
const target = graph.getNodeAttributes(edge.target);
|
|
15540
|
-
if (target.type !==
|
|
16272
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15541
16273
|
const observedHost = target.host?.trim();
|
|
15542
16274
|
if (!observedHost) continue;
|
|
15543
16275
|
if (observedHost === declaredHost) continue;
|
|
@@ -15559,10 +16291,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
15559
16291
|
const deps = svc.dependencies ?? {};
|
|
15560
16292
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15561
16293
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15562
|
-
if (edge.type !==
|
|
15563
|
-
if (edge.provenance !==
|
|
16294
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16295
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15564
16296
|
const target = graph.getNodeAttributes(edge.target);
|
|
15565
|
-
if (target.type !==
|
|
16297
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15566
16298
|
for (const pair of compatPairs()) {
|
|
15567
16299
|
if (pair.engine !== target.engine) continue;
|
|
15568
16300
|
const declared = deps[pair.driver];
|
|
@@ -15659,7 +16391,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
15659
16391
|
for (const d of all) {
|
|
15660
16392
|
if (d.type !== "host-mismatch") continue;
|
|
15661
16393
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
15662
|
-
declaredHalf.add((0,
|
|
16394
|
+
declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
|
|
15663
16395
|
}
|
|
15664
16396
|
if (observedHalf.size === 0) return all;
|
|
15665
16397
|
return all.filter((d) => {
|
|
@@ -15678,13 +16410,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15678
16410
|
}
|
|
15679
16411
|
graph.forEachNode((nodeId, attrs) => {
|
|
15680
16412
|
const n = attrs;
|
|
15681
|
-
if (n.type ===
|
|
16413
|
+
if (n.type === import_types58.NodeType.ServiceNode) {
|
|
15682
16414
|
const svc = n;
|
|
15683
16415
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
15684
16416
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
15685
16417
|
return;
|
|
15686
16418
|
}
|
|
15687
|
-
if (n.type ===
|
|
16419
|
+
if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
|
|
15688
16420
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
15689
16421
|
}
|
|
15690
16422
|
});
|
|
@@ -15720,7 +16452,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15720
16452
|
const bc = "column" in b && b.column ? b.column : "";
|
|
15721
16453
|
return ac.localeCompare(bc);
|
|
15722
16454
|
});
|
|
15723
|
-
return
|
|
16455
|
+
return import_types58.DivergenceResultSchema.parse({
|
|
15724
16456
|
divergences: filtered,
|
|
15725
16457
|
totalAffected: filtered.length,
|
|
15726
16458
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15774,7 +16506,7 @@ function queryLogEntries(opts) {
|
|
|
15774
16506
|
|
|
15775
16507
|
// src/diff.ts
|
|
15776
16508
|
init_cjs_shims();
|
|
15777
|
-
var
|
|
16509
|
+
var import_node_fs36 = require("fs");
|
|
15778
16510
|
async function loadSnapshotForDiff(target) {
|
|
15779
16511
|
if (/^https?:\/\//i.test(target)) {
|
|
15780
16512
|
const res = await fetch(target);
|
|
@@ -15783,7 +16515,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
15783
16515
|
}
|
|
15784
16516
|
return await res.json();
|
|
15785
16517
|
}
|
|
15786
|
-
const raw = await
|
|
16518
|
+
const raw = await import_node_fs36.promises.readFile(target, "utf8");
|
|
15787
16519
|
return JSON.parse(raw);
|
|
15788
16520
|
}
|
|
15789
16521
|
function indexEntries(entries) {
|
|
@@ -15851,23 +16583,23 @@ function canonicalJson(value) {
|
|
|
15851
16583
|
|
|
15852
16584
|
// src/projects.ts
|
|
15853
16585
|
init_cjs_shims();
|
|
15854
|
-
var
|
|
16586
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
15855
16587
|
function pathsForProject(project, baseDir) {
|
|
15856
16588
|
if (project === DEFAULT_PROJECT) {
|
|
15857
16589
|
return {
|
|
15858
|
-
snapshotPath:
|
|
15859
|
-
errorsPath:
|
|
15860
|
-
staleEventsPath:
|
|
15861
|
-
embeddingsCachePath:
|
|
15862
|
-
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")
|
|
15863
16595
|
};
|
|
15864
16596
|
}
|
|
15865
16597
|
return {
|
|
15866
|
-
snapshotPath:
|
|
15867
|
-
errorsPath:
|
|
15868
|
-
staleEventsPath:
|
|
15869
|
-
embeddingsCachePath:
|
|
15870
|
-
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`)
|
|
15871
16603
|
};
|
|
15872
16604
|
}
|
|
15873
16605
|
var Projects = class {
|
|
@@ -15903,28 +16635,28 @@ var Projects = class {
|
|
|
15903
16635
|
|
|
15904
16636
|
// src/registry.ts
|
|
15905
16637
|
init_cjs_shims();
|
|
15906
|
-
var
|
|
16638
|
+
var import_node_fs37 = require("fs");
|
|
15907
16639
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
15908
|
-
var
|
|
15909
|
-
var
|
|
16640
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
16641
|
+
var import_types59 = require("@neat.is/types");
|
|
15910
16642
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
15911
16643
|
var LOCK_RETRY_MS = 50;
|
|
15912
16644
|
function neatHome() {
|
|
15913
16645
|
const override = process.env.NEAT_HOME;
|
|
15914
|
-
if (override && override.length > 0) return
|
|
15915
|
-
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");
|
|
15916
16648
|
}
|
|
15917
16649
|
function registryPath() {
|
|
15918
|
-
return
|
|
16650
|
+
return import_node_path72.default.join(neatHome(), "projects.json");
|
|
15919
16651
|
}
|
|
15920
16652
|
function registryLockPath() {
|
|
15921
|
-
return
|
|
16653
|
+
return import_node_path72.default.join(neatHome(), "projects.json.lock");
|
|
15922
16654
|
}
|
|
15923
16655
|
function daemonPidPath() {
|
|
15924
|
-
return
|
|
16656
|
+
return import_node_path72.default.join(neatHome(), "neatd.pid");
|
|
15925
16657
|
}
|
|
15926
16658
|
function daemonsDir() {
|
|
15927
|
-
return
|
|
16659
|
+
return import_node_path72.default.join(neatHome(), "daemons");
|
|
15928
16660
|
}
|
|
15929
16661
|
function isFiniteInt(v) {
|
|
15930
16662
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -15957,7 +16689,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
15957
16689
|
const dir = daemonsDir();
|
|
15958
16690
|
let names;
|
|
15959
16691
|
try {
|
|
15960
|
-
names = await
|
|
16692
|
+
names = await import_node_fs37.promises.readdir(dir);
|
|
15961
16693
|
} catch (err) {
|
|
15962
16694
|
if (err.code === "ENOENT") return [];
|
|
15963
16695
|
throw err;
|
|
@@ -15965,10 +16697,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
15965
16697
|
const out = [];
|
|
15966
16698
|
for (const name of names) {
|
|
15967
16699
|
if (!name.endsWith(".json")) continue;
|
|
15968
|
-
const file =
|
|
16700
|
+
const file = import_node_path72.default.join(dir, name);
|
|
15969
16701
|
let raw;
|
|
15970
16702
|
try {
|
|
15971
|
-
raw = await
|
|
16703
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
15972
16704
|
} catch {
|
|
15973
16705
|
continue;
|
|
15974
16706
|
}
|
|
@@ -15994,7 +16726,7 @@ function isPidAliveDefault(pid) {
|
|
|
15994
16726
|
}
|
|
15995
16727
|
async function readPidFile(file) {
|
|
15996
16728
|
try {
|
|
15997
|
-
const raw = await
|
|
16729
|
+
const raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
15998
16730
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
15999
16731
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
16000
16732
|
} catch {
|
|
@@ -16042,32 +16774,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
16042
16774
|
}
|
|
16043
16775
|
}
|
|
16044
16776
|
async function normalizeProjectPath(input) {
|
|
16045
|
-
const resolved =
|
|
16777
|
+
const resolved = import_node_path72.default.resolve(input);
|
|
16046
16778
|
try {
|
|
16047
|
-
return await
|
|
16779
|
+
return await import_node_fs37.promises.realpath(resolved);
|
|
16048
16780
|
} catch {
|
|
16049
16781
|
return resolved;
|
|
16050
16782
|
}
|
|
16051
16783
|
}
|
|
16052
16784
|
async function writeAtomically(target, contents) {
|
|
16053
|
-
await
|
|
16785
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
|
|
16054
16786
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
16055
|
-
const fd = await
|
|
16787
|
+
const fd = await import_node_fs37.promises.open(tmp, "w");
|
|
16056
16788
|
try {
|
|
16057
16789
|
await fd.writeFile(contents, "utf8");
|
|
16058
16790
|
await fd.sync();
|
|
16059
16791
|
} finally {
|
|
16060
16792
|
await fd.close();
|
|
16061
16793
|
}
|
|
16062
|
-
await
|
|
16794
|
+
await import_node_fs37.promises.rename(tmp, target);
|
|
16063
16795
|
}
|
|
16064
16796
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
16065
16797
|
const deadline = Date.now() + timeoutMs;
|
|
16066
|
-
await
|
|
16798
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
|
|
16067
16799
|
let probedHolder = false;
|
|
16068
16800
|
while (true) {
|
|
16069
16801
|
try {
|
|
16070
|
-
const fd = await
|
|
16802
|
+
const fd = await import_node_fs37.promises.open(lockPath, "wx");
|
|
16071
16803
|
try {
|
|
16072
16804
|
await fd.writeFile(`${process.pid}
|
|
16073
16805
|
`, "utf8");
|
|
@@ -16092,7 +16824,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
16092
16824
|
}
|
|
16093
16825
|
}
|
|
16094
16826
|
async function releaseLock(lockPath) {
|
|
16095
|
-
await
|
|
16827
|
+
await import_node_fs37.promises.unlink(lockPath).catch(() => {
|
|
16096
16828
|
});
|
|
16097
16829
|
}
|
|
16098
16830
|
async function withLock(fn) {
|
|
@@ -16108,7 +16840,7 @@ async function readRegistry() {
|
|
|
16108
16840
|
const file = registryPath();
|
|
16109
16841
|
let raw;
|
|
16110
16842
|
try {
|
|
16111
|
-
raw = await
|
|
16843
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16112
16844
|
} catch (err) {
|
|
16113
16845
|
if (err.code === "ENOENT") {
|
|
16114
16846
|
return { version: 1, projects: [] };
|
|
@@ -16116,10 +16848,10 @@ async function readRegistry() {
|
|
|
16116
16848
|
throw err;
|
|
16117
16849
|
}
|
|
16118
16850
|
const parsed = JSON.parse(raw);
|
|
16119
|
-
return
|
|
16851
|
+
return import_types59.RegistryFileSchema.parse(parsed);
|
|
16120
16852
|
}
|
|
16121
16853
|
async function writeRegistry(reg) {
|
|
16122
|
-
const validated =
|
|
16854
|
+
const validated = import_types59.RegistryFileSchema.parse(reg);
|
|
16123
16855
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16124
16856
|
}
|
|
16125
16857
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -16213,7 +16945,7 @@ function pruneTtlMs() {
|
|
|
16213
16945
|
}
|
|
16214
16946
|
async function statPathStatus(p) {
|
|
16215
16947
|
try {
|
|
16216
|
-
const stat = await
|
|
16948
|
+
const stat = await import_node_fs37.promises.stat(p);
|
|
16217
16949
|
return stat.isDirectory() ? "present" : "unknown";
|
|
16218
16950
|
} catch (err) {
|
|
16219
16951
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -16314,8 +17046,8 @@ init_auth();
|
|
|
16314
17046
|
// src/connectors-config.ts
|
|
16315
17047
|
init_cjs_shims();
|
|
16316
17048
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
16317
|
-
var
|
|
16318
|
-
var
|
|
17049
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
17050
|
+
var import_node_fs38 = require("fs");
|
|
16319
17051
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
16320
17052
|
var EnvRefUnsetError = class extends Error {
|
|
16321
17053
|
ref;
|
|
@@ -16329,17 +17061,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
16329
17061
|
};
|
|
16330
17062
|
function neatHome2() {
|
|
16331
17063
|
const override = process.env.NEAT_HOME;
|
|
16332
|
-
if (override && override.length > 0) return
|
|
16333
|
-
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");
|
|
16334
17066
|
}
|
|
16335
17067
|
function connectorsConfigPath(home = neatHome2()) {
|
|
16336
|
-
return
|
|
17068
|
+
return import_node_path73.default.join(home, "connectors.json");
|
|
16337
17069
|
}
|
|
16338
17070
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
16339
17071
|
async function warnIfModeLooserThan0600(file) {
|
|
16340
17072
|
if (process.platform === "win32") return;
|
|
16341
17073
|
try {
|
|
16342
|
-
const stat = await
|
|
17074
|
+
const stat = await import_node_fs38.promises.stat(file);
|
|
16343
17075
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
16344
17076
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
16345
17077
|
console.warn(
|
|
@@ -16353,7 +17085,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
16353
17085
|
const file = connectorsConfigPath(home);
|
|
16354
17086
|
let raw;
|
|
16355
17087
|
try {
|
|
16356
|
-
raw = await
|
|
17088
|
+
raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
16357
17089
|
} catch (err) {
|
|
16358
17090
|
if (err.code === "ENOENT") {
|
|
16359
17091
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -16520,15 +17252,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
16520
17252
|
|
|
16521
17253
|
// src/connectors/index.ts
|
|
16522
17254
|
init_cjs_shims();
|
|
16523
|
-
var
|
|
17255
|
+
var import_types60 = require("@neat.is/types");
|
|
16524
17256
|
var NO_ENV = "unknown";
|
|
16525
17257
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
16526
17258
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16527
17259
|
const sites = [];
|
|
16528
17260
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
16529
17261
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16530
|
-
if (edge.provenance !==
|
|
16531
|
-
const parsed = (0,
|
|
17262
|
+
if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
|
|
17263
|
+
const parsed = (0, import_types60.parseFileId)(edge.source);
|
|
16532
17264
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
16533
17265
|
const site = { relPath: edge.evidence.file };
|
|
16534
17266
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -16539,7 +17271,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
16539
17271
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
16540
17272
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16541
17273
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
16542
|
-
if (attrs.type !==
|
|
17274
|
+
if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
|
|
16543
17275
|
const site = { relPath: attrs.path };
|
|
16544
17276
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
16545
17277
|
return site;
|
|
@@ -17037,10 +17769,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
17037
17769
|
// src/connectors/supabase/map.ts
|
|
17038
17770
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
17039
17771
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
17040
|
-
function targetFromRestPath(
|
|
17041
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
17772
|
+
function targetFromRestPath(path76) {
|
|
17773
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path76);
|
|
17042
17774
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
17043
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
17775
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path76);
|
|
17044
17776
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
17045
17777
|
return null;
|
|
17046
17778
|
}
|
|
@@ -17151,23 +17883,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17151
17883
|
|
|
17152
17884
|
// src/connectors/supabase/resolve.ts
|
|
17153
17885
|
init_cjs_shims();
|
|
17154
|
-
var
|
|
17886
|
+
var import_types62 = require("@neat.is/types");
|
|
17155
17887
|
function createSupabaseResolveTarget(graph, config) {
|
|
17156
17888
|
return (signal, _ctx) => {
|
|
17157
17889
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17158
17890
|
return null;
|
|
17159
17891
|
}
|
|
17160
|
-
const subResourceId = (0,
|
|
17892
|
+
const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17161
17893
|
if (graph.hasNode(subResourceId)) {
|
|
17162
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
17894
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17163
17895
|
}
|
|
17164
|
-
const bareResourceId = (0,
|
|
17896
|
+
const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
|
|
17165
17897
|
if (graph.hasNode(bareResourceId)) {
|
|
17166
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
17898
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17167
17899
|
}
|
|
17168
|
-
const projectLevelId = (0,
|
|
17900
|
+
const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
|
|
17169
17901
|
if (graph.hasNode(projectLevelId)) {
|
|
17170
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
17902
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17171
17903
|
}
|
|
17172
17904
|
return null;
|
|
17173
17905
|
};
|
|
@@ -17260,7 +17992,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
17260
17992
|
|
|
17261
17993
|
// src/connectors/railway/index.ts
|
|
17262
17994
|
init_cjs_shims();
|
|
17263
|
-
var
|
|
17995
|
+
var import_types66 = require("@neat.is/types");
|
|
17264
17996
|
|
|
17265
17997
|
// src/connectors/railway/client.ts
|
|
17266
17998
|
init_cjs_shims();
|
|
@@ -17411,7 +18143,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
17411
18143
|
const out = [];
|
|
17412
18144
|
graph.forEachNode((_id, attrs) => {
|
|
17413
18145
|
const node = attrs;
|
|
17414
|
-
if (node.type !==
|
|
18146
|
+
if (node.type !== import_types66.NodeType.RouteNode) return;
|
|
17415
18147
|
const route = attrs;
|
|
17416
18148
|
if (route.service !== serviceName) return;
|
|
17417
18149
|
out.push({
|
|
@@ -17515,12 +18247,12 @@ function createRailwayResolveTarget(config) {
|
|
|
17515
18247
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
17516
18248
|
if (!serviceName) return null;
|
|
17517
18249
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
17518
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18250
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
17519
18251
|
}
|
|
17520
18252
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
17521
18253
|
const peerName = config.serviceNameById[signal.targetName];
|
|
17522
18254
|
if (!peerName) return null;
|
|
17523
|
-
return { targetNodeId: (0,
|
|
18255
|
+
return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
|
|
17524
18256
|
}
|
|
17525
18257
|
return null;
|
|
17526
18258
|
};
|
|
@@ -17644,9 +18376,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
17644
18376
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
17645
18377
|
if (secondSep === -1) return null;
|
|
17646
18378
|
const method = rest.slice(0, secondSep);
|
|
17647
|
-
const
|
|
17648
|
-
if (!resourceName || !method || !
|
|
17649
|
-
return { resourceName, method, path:
|
|
18379
|
+
const path76 = rest.slice(secondSep + 1);
|
|
18380
|
+
if (!resourceName || !method || !path76) return null;
|
|
18381
|
+
return { resourceName, method, path: path76 };
|
|
17650
18382
|
}
|
|
17651
18383
|
function resourceNameFor(type, labels) {
|
|
17652
18384
|
if (!labels) return null;
|
|
@@ -17684,14 +18416,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
17684
18416
|
if (!req) return null;
|
|
17685
18417
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
17686
18418
|
const method = req.requestMethod.toUpperCase();
|
|
17687
|
-
const
|
|
17688
|
-
if (
|
|
18419
|
+
const path76 = pathFromRequestUrl(req.requestUrl);
|
|
18420
|
+
if (path76 === null) return null;
|
|
17689
18421
|
const timestamp = entry.timestamp;
|
|
17690
18422
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
17691
18423
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
17692
18424
|
return {
|
|
17693
18425
|
targetKind: resourceType,
|
|
17694
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
18426
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
|
|
17695
18427
|
callCount: 1,
|
|
17696
18428
|
errorCount: isError ? 1 : 0,
|
|
17697
18429
|
lastObservedIso: timestamp
|
|
@@ -17708,7 +18440,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
17708
18440
|
|
|
17709
18441
|
// src/connectors/firebase/resolve.ts
|
|
17710
18442
|
init_cjs_shims();
|
|
17711
|
-
var
|
|
18443
|
+
var import_types67 = require("@neat.is/types");
|
|
17712
18444
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
17713
18445
|
switch (resourceType) {
|
|
17714
18446
|
case "cloud_function":
|
|
@@ -17723,7 +18455,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
17723
18455
|
const entries = [];
|
|
17724
18456
|
graph.forEachNode((_id, attrs) => {
|
|
17725
18457
|
const node = attrs;
|
|
17726
|
-
if (node.type !==
|
|
18458
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
17727
18459
|
const route = attrs;
|
|
17728
18460
|
if (route.service !== serviceName) return;
|
|
17729
18461
|
entries.push({
|
|
@@ -17755,7 +18487,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
17755
18487
|
return {
|
|
17756
18488
|
targetNodeId: match.routeNodeId,
|
|
17757
18489
|
serviceName,
|
|
17758
|
-
edgeType:
|
|
18490
|
+
edgeType: import_types67.EdgeType.CALLS
|
|
17759
18491
|
};
|
|
17760
18492
|
};
|
|
17761
18493
|
}
|
|
@@ -17782,7 +18514,7 @@ init_cjs_shims();
|
|
|
17782
18514
|
|
|
17783
18515
|
// src/connectors/cloudflare/connector.ts
|
|
17784
18516
|
init_cjs_shims();
|
|
17785
|
-
var
|
|
18517
|
+
var import_types69 = require("@neat.is/types");
|
|
17786
18518
|
|
|
17787
18519
|
// src/connectors/cloudflare/client.ts
|
|
17788
18520
|
init_cjs_shims();
|
|
@@ -17898,7 +18630,7 @@ function mapEventToSignal(event) {
|
|
|
17898
18630
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
17899
18631
|
const statusCode = metadata?.statusCode;
|
|
17900
18632
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
17901
|
-
const
|
|
18633
|
+
const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
17902
18634
|
return {
|
|
17903
18635
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
17904
18636
|
targetName: scriptName,
|
|
@@ -17906,7 +18638,7 @@ function mapEventToSignal(event) {
|
|
|
17906
18638
|
errorCount: isError ? 1 : 0,
|
|
17907
18639
|
lastObservedIso: observedAt.toISOString(),
|
|
17908
18640
|
method,
|
|
17909
|
-
...
|
|
18641
|
+
...path76 ? { path: path76 } : {},
|
|
17910
18642
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
17911
18643
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
17912
18644
|
};
|
|
@@ -17946,19 +18678,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
17946
18678
|
graph.forEachNode((id, attrs) => {
|
|
17947
18679
|
if (found) return;
|
|
17948
18680
|
const a = attrs;
|
|
17949
|
-
if (a.type ===
|
|
18681
|
+
if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
17950
18682
|
found = id;
|
|
17951
18683
|
}
|
|
17952
18684
|
});
|
|
17953
18685
|
return found;
|
|
17954
18686
|
}
|
|
17955
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
17956
|
-
const normalizedPath = normalizePathTemplate(
|
|
18687
|
+
function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
18688
|
+
const normalizedPath = normalizePathTemplate(path76);
|
|
17957
18689
|
let found = null;
|
|
17958
18690
|
graph.forEachNode((id, attrs) => {
|
|
17959
18691
|
if (found) return;
|
|
17960
18692
|
const a = attrs;
|
|
17961
|
-
if (a.type !==
|
|
18693
|
+
if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
|
|
17962
18694
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
17963
18695
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
17964
18696
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -17970,18 +18702,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
17970
18702
|
return (signal) => {
|
|
17971
18703
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
17972
18704
|
const scriptName = signal.targetName;
|
|
17973
|
-
const { method, path:
|
|
18705
|
+
const { method, path: path76 } = signal;
|
|
17974
18706
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
17975
|
-
if (!method || !
|
|
17976
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
18707
|
+
if (!method || !path76) return wholeFileId;
|
|
18708
|
+
return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
|
|
17977
18709
|
};
|
|
17978
18710
|
const mapping = config.workers?.[scriptName];
|
|
17979
18711
|
if (mapping) {
|
|
17980
|
-
const wholeFileId = (0,
|
|
18712
|
+
const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
|
|
17981
18713
|
return {
|
|
17982
18714
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
17983
18715
|
serviceName: mapping.service,
|
|
17984
|
-
edgeType:
|
|
18716
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
17985
18717
|
};
|
|
17986
18718
|
}
|
|
17987
18719
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -17990,13 +18722,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
17990
18722
|
return {
|
|
17991
18723
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
17992
18724
|
serviceName: fileNode.service,
|
|
17993
|
-
edgeType:
|
|
18725
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
17994
18726
|
};
|
|
17995
18727
|
}
|
|
17996
18728
|
return {
|
|
17997
|
-
targetNodeId: (0,
|
|
18729
|
+
targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
|
|
17998
18730
|
serviceName: scriptName,
|
|
17999
|
-
edgeType:
|
|
18731
|
+
edgeType: import_types69.EdgeType.CALLS,
|
|
18000
18732
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
18001
18733
|
};
|
|
18002
18734
|
};
|
|
@@ -18192,14 +18924,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18192
18924
|
|
|
18193
18925
|
// src/connectors/neon/resolve.ts
|
|
18194
18926
|
init_cjs_shims();
|
|
18195
|
-
var
|
|
18927
|
+
var import_types73 = require("@neat.is/types");
|
|
18196
18928
|
function createNeonResolveTarget(config) {
|
|
18197
18929
|
return (signal) => {
|
|
18198
18930
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18199
18931
|
return {
|
|
18200
|
-
targetNodeId: (0,
|
|
18932
|
+
targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
|
|
18201
18933
|
serviceName: config.serviceName,
|
|
18202
|
-
edgeType:
|
|
18934
|
+
edgeType: import_types73.EdgeType.CALLS,
|
|
18203
18935
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18204
18936
|
};
|
|
18205
18937
|
};
|
|
@@ -18325,9 +19057,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
18325
19057
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
18326
19058
|
if (secondSep === -1) return null;
|
|
18327
19059
|
const method = rest.slice(0, secondSep);
|
|
18328
|
-
const
|
|
18329
|
-
if (!serviceName || !method || !
|
|
18330
|
-
return { serviceName, method, path:
|
|
19060
|
+
const path76 = rest.slice(secondSep + 1);
|
|
19061
|
+
if (!serviceName || !method || !path76) return null;
|
|
19062
|
+
return { serviceName, method, path: path76 };
|
|
18331
19063
|
}
|
|
18332
19064
|
|
|
18333
19065
|
// src/connectors/cloud-run/map.ts
|
|
@@ -18356,14 +19088,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
18356
19088
|
if (!req) return null;
|
|
18357
19089
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
18358
19090
|
const method = req.requestMethod.toUpperCase();
|
|
18359
|
-
const
|
|
18360
|
-
if (
|
|
19091
|
+
const path76 = pathFromRequestUrl2(req.requestUrl);
|
|
19092
|
+
if (path76 === null) return null;
|
|
18361
19093
|
const timestamp = entry.timestamp;
|
|
18362
19094
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
18363
19095
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
18364
19096
|
return {
|
|
18365
19097
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
18366
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
19098
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
|
|
18367
19099
|
callCount: 1,
|
|
18368
19100
|
errorCount: isError ? 1 : 0,
|
|
18369
19101
|
lastObservedIso: timestamp
|
|
@@ -18380,14 +19112,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
18380
19112
|
|
|
18381
19113
|
// src/connectors/cloud-run/resolve.ts
|
|
18382
19114
|
init_cjs_shims();
|
|
18383
|
-
var
|
|
19115
|
+
var import_types77 = require("@neat.is/types");
|
|
18384
19116
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
18385
19117
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
18386
19118
|
let found = null;
|
|
18387
19119
|
graph.forEachNode((_id, attrs) => {
|
|
18388
19120
|
if (found) return;
|
|
18389
19121
|
const node = attrs;
|
|
18390
|
-
if (node.type !==
|
|
19122
|
+
if (node.type !== import_types77.NodeType.RouteNode) return;
|
|
18391
19123
|
const route = attrs;
|
|
18392
19124
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
18393
19125
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -18402,23 +19134,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
18402
19134
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
18403
19135
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
18404
19136
|
if (!identity) return null;
|
|
18405
|
-
const { serviceName: gcpServiceName, method, path:
|
|
19137
|
+
const { serviceName: gcpServiceName, method, path: path76 } = identity;
|
|
18406
19138
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
18407
19139
|
if (mappedService) {
|
|
18408
19140
|
const routeNodeId = findMatchingRouteNode2(
|
|
18409
19141
|
graph,
|
|
18410
19142
|
mappedService,
|
|
18411
19143
|
method,
|
|
18412
|
-
normalizePathTemplate(
|
|
19144
|
+
normalizePathTemplate(path76)
|
|
18413
19145
|
);
|
|
18414
19146
|
if (routeNodeId) {
|
|
18415
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19147
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
|
|
18416
19148
|
}
|
|
18417
19149
|
}
|
|
18418
19150
|
return {
|
|
18419
|
-
targetNodeId: (0,
|
|
19151
|
+
targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
18420
19152
|
serviceName: mappedService ?? gcpServiceName,
|
|
18421
|
-
edgeType:
|
|
19153
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
18422
19154
|
ensureInfraNode: {
|
|
18423
19155
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
18424
19156
|
name: gcpServiceName,
|
|
@@ -18459,7 +19191,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
18459
19191
|
|
|
18460
19192
|
// src/connectors/render/index.ts
|
|
18461
19193
|
init_cjs_shims();
|
|
18462
|
-
var
|
|
19194
|
+
var import_types80 = require("@neat.is/types");
|
|
18463
19195
|
|
|
18464
19196
|
// src/connectors/render/types.ts
|
|
18465
19197
|
init_cjs_shims();
|
|
@@ -18537,7 +19269,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
18537
19269
|
const out = [];
|
|
18538
19270
|
graph.forEachNode((_id, attrs) => {
|
|
18539
19271
|
const node = attrs;
|
|
18540
|
-
if (node.type !==
|
|
19272
|
+
if (node.type !== import_types80.NodeType.RouteNode) return;
|
|
18541
19273
|
const route = attrs;
|
|
18542
19274
|
if (route.service !== serviceName) return;
|
|
18543
19275
|
out.push({
|
|
@@ -18622,7 +19354,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
18622
19354
|
function createRenderResolveTarget(config) {
|
|
18623
19355
|
return (signal) => {
|
|
18624
19356
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
18625
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
19357
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
|
|
18626
19358
|
}
|
|
18627
19359
|
return null;
|
|
18628
19360
|
};
|
|
@@ -18760,21 +19492,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
18760
19492
|
|
|
18761
19493
|
// src/connectors/planetscale/resolve.ts
|
|
18762
19494
|
init_cjs_shims();
|
|
18763
|
-
var
|
|
19495
|
+
var import_types84 = require("@neat.is/types");
|
|
18764
19496
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
18765
19497
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
18766
19498
|
const databaseName = `${config.organization}/${config.database}`;
|
|
18767
19499
|
return (signal, _ctx) => {
|
|
18768
19500
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18769
|
-
const tableId = (0,
|
|
19501
|
+
const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
|
|
18770
19502
|
if (graph.hasNode(tableId)) {
|
|
18771
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
19503
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
|
|
18772
19504
|
}
|
|
18773
|
-
const providerId = (0,
|
|
19505
|
+
const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
18774
19506
|
return {
|
|
18775
19507
|
targetNodeId: providerId,
|
|
18776
19508
|
serviceName: config.serviceName,
|
|
18777
|
-
edgeType:
|
|
19509
|
+
edgeType: import_types84.EdgeType.CALLS,
|
|
18778
19510
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
18779
19511
|
};
|
|
18780
19512
|
};
|
|
@@ -19039,7 +19771,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19039
19771
|
|
|
19040
19772
|
// src/connectors/eas/resolve.ts
|
|
19041
19773
|
init_cjs_shims();
|
|
19042
|
-
var
|
|
19774
|
+
var import_types89 = require("@neat.is/types");
|
|
19043
19775
|
var NO_ENV2 = "unknown";
|
|
19044
19776
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19045
19777
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19055,8 +19787,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19055
19787
|
function configNodeService(graph, configNodeId) {
|
|
19056
19788
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19057
19789
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19058
|
-
if (edge.type !==
|
|
19059
|
-
const parsed = (0,
|
|
19790
|
+
if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
|
|
19791
|
+
const parsed = (0, import_types89.parseFileId)(edge.source);
|
|
19060
19792
|
if (parsed) return parsed.service;
|
|
19061
19793
|
}
|
|
19062
19794
|
return null;
|
|
@@ -19067,7 +19799,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19067
19799
|
graph.forEachNode((id, attrs) => {
|
|
19068
19800
|
if (scoped) return;
|
|
19069
19801
|
const node = attrs;
|
|
19070
|
-
if (node.type !==
|
|
19802
|
+
if (node.type !== import_types89.NodeType.ConfigNode) return;
|
|
19071
19803
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19072
19804
|
if (anyMatch === null) anyMatch = id;
|
|
19073
19805
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19084,13 +19816,13 @@ function createEasResolveTarget(graph) {
|
|
|
19084
19816
|
if (basenames.length > 0) {
|
|
19085
19817
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19086
19818
|
if (configNodeId) {
|
|
19087
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
19819
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
|
|
19088
19820
|
}
|
|
19089
19821
|
}
|
|
19090
19822
|
return {
|
|
19091
19823
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19092
19824
|
serviceName,
|
|
19093
|
-
edgeType:
|
|
19825
|
+
edgeType: import_types89.EdgeType.CALLS
|
|
19094
19826
|
};
|
|
19095
19827
|
};
|
|
19096
19828
|
}
|
|
@@ -19815,11 +20547,11 @@ function registerRoutes(scope, ctx) {
|
|
|
19815
20547
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
19816
20548
|
const parsed = [];
|
|
19817
20549
|
for (const c of candidates) {
|
|
19818
|
-
const r =
|
|
20550
|
+
const r = import_types92.DivergenceTypeSchema.safeParse(c);
|
|
19819
20551
|
if (!r.success) {
|
|
19820
20552
|
return reply.code(400).send({
|
|
19821
20553
|
error: `unknown divergence type "${c}"`,
|
|
19822
|
-
allowed:
|
|
20554
|
+
allowed: import_types92.DivergenceTypeSchema.options
|
|
19823
20555
|
});
|
|
19824
20556
|
}
|
|
19825
20557
|
parsed.push(r.data);
|
|
@@ -20161,7 +20893,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20161
20893
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20162
20894
|
let violations = await log.readAll();
|
|
20163
20895
|
if (req.query.severity) {
|
|
20164
|
-
const sev =
|
|
20896
|
+
const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
|
|
20165
20897
|
if (!sev.success) {
|
|
20166
20898
|
return reply.code(400).send({
|
|
20167
20899
|
error: "invalid severity",
|
|
@@ -20200,7 +20932,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20200
20932
|
scope.post("/policies/check", async (req, reply) => {
|
|
20201
20933
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
20202
20934
|
if (!proj) return;
|
|
20203
|
-
const parsed =
|
|
20935
|
+
const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
20204
20936
|
if (!parsed.success) {
|
|
20205
20937
|
return reply.code(400).send({
|
|
20206
20938
|
error: "invalid /policies/check body",
|
|
@@ -20521,16 +21253,16 @@ init_otel_grpc();
|
|
|
20521
21253
|
|
|
20522
21254
|
// src/daemon.ts
|
|
20523
21255
|
init_cjs_shims();
|
|
20524
|
-
var
|
|
20525
|
-
var
|
|
21256
|
+
var import_node_fs40 = require("fs");
|
|
21257
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
20526
21258
|
var import_node_module = require("module");
|
|
20527
21259
|
init_otel();
|
|
20528
21260
|
init_auth();
|
|
20529
21261
|
|
|
20530
21262
|
// src/unrouted.ts
|
|
20531
21263
|
init_cjs_shims();
|
|
20532
|
-
var
|
|
20533
|
-
var
|
|
21264
|
+
var import_node_fs39 = require("fs");
|
|
21265
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
20534
21266
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
20535
21267
|
return {
|
|
20536
21268
|
timestamp: now.toISOString(),
|
|
@@ -20540,34 +21272,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
20540
21272
|
};
|
|
20541
21273
|
}
|
|
20542
21274
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
20543
|
-
const target =
|
|
20544
|
-
await
|
|
20545
|
-
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");
|
|
20546
21278
|
}
|
|
20547
21279
|
function unroutedErrorsPath(neatHome3) {
|
|
20548
|
-
return
|
|
21280
|
+
return import_node_path74.default.join(neatHome3, "errors.ndjson");
|
|
20549
21281
|
}
|
|
20550
21282
|
|
|
20551
21283
|
// src/daemon.ts
|
|
20552
|
-
var
|
|
21284
|
+
var import_types93 = require("@neat.is/types");
|
|
20553
21285
|
function daemonJsonPath(scanPath) {
|
|
20554
|
-
return
|
|
21286
|
+
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
20555
21287
|
}
|
|
20556
21288
|
function daemonsDiscoveryDir(home) {
|
|
20557
21289
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
20558
|
-
return
|
|
21290
|
+
return import_node_path75.default.join(base, "daemons");
|
|
20559
21291
|
}
|
|
20560
21292
|
function daemonDiscoveryPath(project, home) {
|
|
20561
|
-
return
|
|
21293
|
+
return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
20562
21294
|
}
|
|
20563
21295
|
function sanitizeDiscoveryName(project) {
|
|
20564
21296
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
20565
21297
|
}
|
|
20566
21298
|
function neatHomeFromEnv() {
|
|
20567
21299
|
const env = process.env.NEAT_HOME;
|
|
20568
|
-
if (env && env.length > 0) return
|
|
21300
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20569
21301
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20570
|
-
return
|
|
21302
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20571
21303
|
}
|
|
20572
21304
|
function resolveNeatVersion() {
|
|
20573
21305
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -20599,7 +21331,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
20599
21331
|
} catch {
|
|
20600
21332
|
}
|
|
20601
21333
|
try {
|
|
20602
|
-
await
|
|
21334
|
+
await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
20603
21335
|
} catch {
|
|
20604
21336
|
}
|
|
20605
21337
|
}
|
|
@@ -20622,11 +21354,11 @@ function teardownSlot(slot) {
|
|
|
20622
21354
|
}
|
|
20623
21355
|
}
|
|
20624
21356
|
function neatHomeFor(opts) {
|
|
20625
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
21357
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
|
|
20626
21358
|
const env = process.env.NEAT_HOME;
|
|
20627
|
-
if (env && env.length > 0) return
|
|
21359
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20628
21360
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20629
|
-
return
|
|
21361
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20630
21362
|
}
|
|
20631
21363
|
function routeSpanToProject(serviceName, projects) {
|
|
20632
21364
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -20674,13 +21406,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
20674
21406
|
if (!serviceName) return true;
|
|
20675
21407
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
20676
21408
|
return graph.someNode(
|
|
20677
|
-
(_id, attrs) => attrs.type ===
|
|
21409
|
+
(_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
|
|
20678
21410
|
);
|
|
20679
21411
|
}
|
|
20680
21412
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
20681
|
-
const paths = pathsForProject(entry.name,
|
|
21413
|
+
const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
|
|
20682
21414
|
try {
|
|
20683
|
-
const stat = await
|
|
21415
|
+
const stat = await import_node_fs40.promises.stat(entry.path);
|
|
20684
21416
|
if (!stat.isDirectory()) {
|
|
20685
21417
|
throw new Error(`registered path ${entry.path} is not a directory`);
|
|
20686
21418
|
}
|
|
@@ -20798,7 +21530,7 @@ async function startDaemon(opts = {}) {
|
|
|
20798
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;
|
|
20799
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;
|
|
20800
21532
|
const singleProject = projectArg;
|
|
20801
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
21533
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
|
|
20802
21534
|
if (singleProject && !singleProjectPath) {
|
|
20803
21535
|
throw new Error(
|
|
20804
21536
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -20806,14 +21538,14 @@ async function startDaemon(opts = {}) {
|
|
|
20806
21538
|
}
|
|
20807
21539
|
if (!singleProject) {
|
|
20808
21540
|
try {
|
|
20809
|
-
await
|
|
21541
|
+
await import_node_fs40.promises.access(regPath);
|
|
20810
21542
|
} catch {
|
|
20811
21543
|
throw new Error(
|
|
20812
21544
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
20813
21545
|
);
|
|
20814
21546
|
}
|
|
20815
21547
|
}
|
|
20816
|
-
const pidPath =
|
|
21548
|
+
const pidPath = import_node_path75.default.join(home, "neatd.pid");
|
|
20817
21549
|
await writeAtomically(pidPath, `${process.pid}
|
|
20818
21550
|
`);
|
|
20819
21551
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -21025,7 +21757,7 @@ async function startDaemon(opts = {}) {
|
|
|
21025
21757
|
}
|
|
21026
21758
|
if (restApp) await restApp.close().catch(() => {
|
|
21027
21759
|
});
|
|
21028
|
-
await
|
|
21760
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21029
21761
|
});
|
|
21030
21762
|
throw new Error(
|
|
21031
21763
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -21193,7 +21925,7 @@ async function startDaemon(opts = {}) {
|
|
|
21193
21925
|
});
|
|
21194
21926
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21195
21927
|
});
|
|
21196
|
-
await
|
|
21928
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21197
21929
|
});
|
|
21198
21930
|
throw new Error(
|
|
21199
21931
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -21228,7 +21960,7 @@ async function startDaemon(opts = {}) {
|
|
|
21228
21960
|
});
|
|
21229
21961
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21230
21962
|
});
|
|
21231
|
-
await
|
|
21963
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21232
21964
|
});
|
|
21233
21965
|
throw new Error(
|
|
21234
21966
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -21275,9 +22007,9 @@ async function startDaemon(opts = {}) {
|
|
|
21275
22007
|
let registryWatcher = null;
|
|
21276
22008
|
let reloadTimer = null;
|
|
21277
22009
|
if (!singleProject) try {
|
|
21278
|
-
const regDir =
|
|
21279
|
-
const regBase =
|
|
21280
|
-
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) => {
|
|
21281
22013
|
if (filename !== null && filename !== regBase) return;
|
|
21282
22014
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
21283
22015
|
reloadTimer = setTimeout(() => {
|
|
@@ -21326,7 +22058,7 @@ async function startDaemon(opts = {}) {
|
|
|
21326
22058
|
if (daemonRecord) {
|
|
21327
22059
|
await clearDaemonRecord(daemonRecord, home);
|
|
21328
22060
|
}
|
|
21329
|
-
await
|
|
22061
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21330
22062
|
});
|
|
21331
22063
|
};
|
|
21332
22064
|
return {
|