@neat.is/core 0.9.1-dev.20260819 → 0.9.2-dev.20260821
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-FCO5Z3RW.js → chunk-ERE47MCR.js} +2 -2
- package/dist/{chunk-UUYCTH2E.js → chunk-GDGUY4T6.js} +34 -2
- package/dist/chunk-GDGUY4T6.js.map +1 -0
- package/dist/{chunk-ICO37D3H.js → chunk-L4SZIIER.js} +3 -3
- package/dist/{chunk-JUVJBH23.js → chunk-RQQUI3NQ.js} +1073 -378
- package/dist/chunk-RQQUI3NQ.js.map +1 -0
- package/dist/cli.cjs +1640 -911
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +1303 -574
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +1319 -590
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-CO47JRIN.js → otel-grpc-Y5K4WGWO.js} +3 -3
- package/dist/server.cjs +1206 -477
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-JUVJBH23.js.map +0 -1
- package/dist/chunk-UUYCTH2E.js.map +0 -1
- /package/dist/{chunk-FCO5Z3RW.js.map → chunk-ERE47MCR.js.map} +0 -0
- /package/dist/{chunk-ICO37D3H.js.map → chunk-L4SZIIER.js.map} +0 -0
- /package/dist/{otel-grpc-CO47JRIN.js.map → otel-grpc-Y5K4WGWO.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 }
|
|
@@ -525,11 +525,42 @@ async function decodeProtobufBody(buf) {
|
|
|
525
525
|
const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
|
|
526
526
|
return reshapeGrpcRequest2(decoded);
|
|
527
527
|
}
|
|
528
|
+
function decompressorForEncoding(encoding) {
|
|
529
|
+
switch (encoding) {
|
|
530
|
+
case "gzip":
|
|
531
|
+
case "x-gzip":
|
|
532
|
+
return import_node_zlib.default.createGunzip();
|
|
533
|
+
case "deflate":
|
|
534
|
+
return import_node_zlib.default.createInflate();
|
|
535
|
+
default:
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
528
539
|
async function buildOtelReceiver(opts) {
|
|
529
540
|
const app = (0, import_fastify.default)({
|
|
530
541
|
logger: false,
|
|
531
542
|
bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
|
|
532
543
|
});
|
|
544
|
+
app.addHook("preParsing", (req, _reply, payload, done) => {
|
|
545
|
+
const encoding = (req.headers["content-encoding"] ?? "").toString().trim().toLowerCase();
|
|
546
|
+
if (encoding === "" || encoding === "identity") {
|
|
547
|
+
done(null, payload);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const decompressor = decompressorForEncoding(encoding);
|
|
551
|
+
if (!decompressor) {
|
|
552
|
+
done(null, payload);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const tracked = decompressor;
|
|
556
|
+
tracked.receivedEncodedLength = 0;
|
|
557
|
+
payload.on("data", (chunk) => {
|
|
558
|
+
tracked.receivedEncodedLength = (tracked.receivedEncodedLength ?? 0) + chunk.length;
|
|
559
|
+
});
|
|
560
|
+
payload.on("error", (err) => decompressor.destroy(err));
|
|
561
|
+
payload.pipe(decompressor);
|
|
562
|
+
done(null, decompressor);
|
|
563
|
+
});
|
|
533
564
|
const REJECT_WARN_INTERVAL_MS = 6e4;
|
|
534
565
|
let lastRejectWarnAt = 0;
|
|
535
566
|
const warnRejectedOtlp = () => {
|
|
@@ -735,13 +766,14 @@ function logSpanHandler(span) {
|
|
|
735
766
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
736
767
|
);
|
|
737
768
|
}
|
|
738
|
-
var
|
|
769
|
+
var import_node_path53, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
739
770
|
var init_otel = __esm({
|
|
740
771
|
"src/otel.ts"() {
|
|
741
772
|
"use strict";
|
|
742
773
|
init_cjs_shims();
|
|
743
|
-
|
|
774
|
+
import_node_path53 = __toESM(require("path"), 1);
|
|
744
775
|
import_node_url2 = require("url");
|
|
776
|
+
import_node_zlib = __toESM(require("zlib"), 1);
|
|
745
777
|
import_fastify = __toESM(require("fastify"), 1);
|
|
746
778
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
747
779
|
init_auth();
|
|
@@ -1333,19 +1365,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1333
1365
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1334
1366
|
let best = { path: [start], edges: [] };
|
|
1335
1367
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1336
|
-
function step(node,
|
|
1337
|
-
if (
|
|
1338
|
-
best = { path: [...
|
|
1368
|
+
function step(node, path76, edges) {
|
|
1369
|
+
if (path76.length > best.path.length) {
|
|
1370
|
+
best = { path: [...path76], edges: [...edges] };
|
|
1339
1371
|
}
|
|
1340
|
-
if (
|
|
1372
|
+
if (path76.length - 1 >= maxDepth) return;
|
|
1341
1373
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1342
1374
|
for (const [srcId, edge] of incoming) {
|
|
1343
1375
|
if (visited.has(srcId)) continue;
|
|
1344
1376
|
visited.add(srcId);
|
|
1345
|
-
|
|
1377
|
+
path76.push(srcId);
|
|
1346
1378
|
edges.push(edge);
|
|
1347
|
-
step(srcId,
|
|
1348
|
-
|
|
1379
|
+
step(srcId, path76, edges);
|
|
1380
|
+
path76.pop();
|
|
1349
1381
|
edges.pop();
|
|
1350
1382
|
visited.delete(srcId);
|
|
1351
1383
|
}
|
|
@@ -1353,11 +1385,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1353
1385
|
step(start, [start], []);
|
|
1354
1386
|
return best;
|
|
1355
1387
|
}
|
|
1356
|
-
function databaseRootCauseShape(graph, origin,
|
|
1388
|
+
function databaseRootCauseShape(graph, origin, walk10) {
|
|
1357
1389
|
const targetDb = origin;
|
|
1358
1390
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1359
1391
|
if (candidatePairs.length === 0) return null;
|
|
1360
|
-
for (const id of
|
|
1392
|
+
for (const id of walk10.path) {
|
|
1361
1393
|
const owner = resolveOwningService(graph, id);
|
|
1362
1394
|
if (!owner) continue;
|
|
1363
1395
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1384,8 +1416,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
|
|
|
1384
1416
|
}
|
|
1385
1417
|
return null;
|
|
1386
1418
|
}
|
|
1387
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1388
|
-
for (const id of
|
|
1419
|
+
function serviceRootCauseShape(graph, _origin, walk10) {
|
|
1420
|
+
for (const id of walk10.path) {
|
|
1389
1421
|
const owner = resolveOwningService(graph, id);
|
|
1390
1422
|
if (!owner) continue;
|
|
1391
1423
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1421,15 +1453,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
1421
1453
|
}
|
|
1422
1454
|
return null;
|
|
1423
1455
|
}
|
|
1424
|
-
function fileRootCauseShape(graph, origin,
|
|
1456
|
+
function fileRootCauseShape(graph, origin, walk10) {
|
|
1425
1457
|
const owner = resolveOwningService(graph, origin.id);
|
|
1426
1458
|
if (!owner) return null;
|
|
1427
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1459
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1428
1460
|
}
|
|
1429
|
-
function symbolRootCauseShape(graph, origin,
|
|
1461
|
+
function symbolRootCauseShape(graph, origin, walk10) {
|
|
1430
1462
|
const owner = resolveOwningService(graph, origin.id);
|
|
1431
1463
|
if (!owner) return null;
|
|
1432
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1464
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1433
1465
|
}
|
|
1434
1466
|
var rootCauseShapes = {
|
|
1435
1467
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1442,25 +1474,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1442
1474
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1443
1475
|
const shape = rootCauseShapes[origin.type];
|
|
1444
1476
|
if (shape) {
|
|
1445
|
-
const
|
|
1446
|
-
const match = shape(graph, origin,
|
|
1477
|
+
const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1478
|
+
const match = shape(graph, origin, walk10);
|
|
1447
1479
|
if (match) {
|
|
1448
1480
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1449
|
-
return
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1481
|
+
return {
|
|
1482
|
+
source: "compat",
|
|
1483
|
+
result: import_types.RootCauseResultSchema.parse({
|
|
1484
|
+
rootCauseNode: match.rootCauseNode,
|
|
1485
|
+
rootCauseReason: reason,
|
|
1486
|
+
traversalPath: walk10.path,
|
|
1487
|
+
edgeProvenances: walk10.edges.map((e) => e.provenance),
|
|
1488
|
+
confidence: confidenceFromMix(walk10.edges),
|
|
1489
|
+
fixRecommendation: match.fixRecommendation
|
|
1490
|
+
})
|
|
1491
|
+
};
|
|
1457
1492
|
}
|
|
1458
1493
|
}
|
|
1459
1494
|
if (origin.type === import_types.NodeType.ServiceNode) {
|
|
1460
1495
|
const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
|
|
1461
|
-
if (crossService) return crossService;
|
|
1496
|
+
if (crossService) return { result: crossService, source: "cross-service" };
|
|
1462
1497
|
}
|
|
1463
|
-
|
|
1498
|
+
const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
|
|
1499
|
+
return incident ? { result: incident, source: "incident" } : null;
|
|
1464
1500
|
}
|
|
1465
1501
|
var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
|
|
1466
1502
|
function incidentMatchesNode(ev, nodeId) {
|
|
@@ -1556,26 +1592,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
|
|
|
1556
1592
|
return best;
|
|
1557
1593
|
}
|
|
1558
1594
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1559
|
-
const
|
|
1595
|
+
const path76 = [originServiceId];
|
|
1560
1596
|
const edges = [];
|
|
1561
1597
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1562
1598
|
let current = originServiceId;
|
|
1563
1599
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1564
1600
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1565
1601
|
if (!hop) break;
|
|
1566
|
-
|
|
1602
|
+
path76.push(hop.nextService);
|
|
1603
|
+
edges.push(hop.edge);
|
|
1604
|
+
visited.add(hop.nextService);
|
|
1605
|
+
current = hop.nextService;
|
|
1606
|
+
}
|
|
1607
|
+
if (edges.length === 0) return null;
|
|
1608
|
+
return { path: path76, edges, culprit: current };
|
|
1609
|
+
}
|
|
1610
|
+
function isStaleCallEdge(e) {
|
|
1611
|
+
return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
|
|
1612
|
+
}
|
|
1613
|
+
function staleCallDominates(e, id, curEdge, curId) {
|
|
1614
|
+
const ev = e.signal?.spanCount ?? e.callCount ?? 0;
|
|
1615
|
+
const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
|
|
1616
|
+
if (ev !== cv) return ev > cv;
|
|
1617
|
+
return id < curId;
|
|
1618
|
+
}
|
|
1619
|
+
function dominantStaleCall(graph, serviceId15, visited) {
|
|
1620
|
+
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1621
|
+
for (const src of callSourcesForService(graph, serviceId15)) {
|
|
1622
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
1623
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
1624
|
+
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
1625
|
+
if (isFrontierNode(graph, e.target)) continue;
|
|
1626
|
+
const owner = resolveOwningService(graph, e.target);
|
|
1627
|
+
if (!owner || visited.has(owner.id)) continue;
|
|
1628
|
+
const cur = bestByCallee.get(owner.id);
|
|
1629
|
+
if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
|
|
1630
|
+
bestByCallee.set(owner.id, e);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
let best = null;
|
|
1635
|
+
for (const [id, edge] of bestByCallee) {
|
|
1636
|
+
if (!isStaleCallEdge(edge)) continue;
|
|
1637
|
+
if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
|
|
1638
|
+
best = { nextService: id, edge };
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return best;
|
|
1642
|
+
}
|
|
1643
|
+
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
1644
|
+
const path76 = [originServiceId];
|
|
1645
|
+
const edges = [];
|
|
1646
|
+
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1647
|
+
let current = originServiceId;
|
|
1648
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1649
|
+
const hop = dominantStaleCall(graph, current, visited);
|
|
1650
|
+
if (!hop) break;
|
|
1651
|
+
path76.push(hop.nextService);
|
|
1567
1652
|
edges.push(hop.edge);
|
|
1568
1653
|
visited.add(hop.nextService);
|
|
1569
1654
|
current = hop.nextService;
|
|
1570
1655
|
}
|
|
1571
1656
|
if (edges.length === 0) return null;
|
|
1572
|
-
return { path:
|
|
1657
|
+
return { path: path76, edges, culprit: current };
|
|
1573
1658
|
}
|
|
1574
1659
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1575
1660
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1576
1661
|
if (!chain) return null;
|
|
1577
1662
|
const culprit = chain.culprit;
|
|
1578
|
-
const
|
|
1663
|
+
const path76 = [...chain.path];
|
|
1579
1664
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1580
1665
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1581
1666
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1583,14 +1668,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1583
1668
|
if (loc) {
|
|
1584
1669
|
let rootCauseNode = culprit;
|
|
1585
1670
|
if (loc.fileNode) {
|
|
1586
|
-
|
|
1671
|
+
path76.push(loc.fileNode);
|
|
1587
1672
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1588
1673
|
rootCauseNode = loc.fileNode;
|
|
1589
1674
|
}
|
|
1590
1675
|
return import_types.RootCauseResultSchema.parse({
|
|
1591
1676
|
rootCauseNode,
|
|
1592
1677
|
rootCauseReason: loc.rootCauseReason,
|
|
1593
|
-
traversalPath:
|
|
1678
|
+
traversalPath: path76,
|
|
1594
1679
|
edgeProvenances,
|
|
1595
1680
|
confidence,
|
|
1596
1681
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1602,7 +1687,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1602
1687
|
return import_types.RootCauseResultSchema.parse({
|
|
1603
1688
|
rootCauseNode: culprit,
|
|
1604
1689
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1605
|
-
traversalPath:
|
|
1690
|
+
traversalPath: path76,
|
|
1606
1691
|
edgeProvenances,
|
|
1607
1692
|
confidence,
|
|
1608
1693
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -1989,17 +2074,20 @@ function displayNameOf(nodeId) {
|
|
|
1989
2074
|
return nodeId.replace(/^[a-z]+:/, "");
|
|
1990
2075
|
}
|
|
1991
2076
|
function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
|
|
1992
|
-
const
|
|
1993
|
-
if (!
|
|
2077
|
+
const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
|
|
2078
|
+
if (!tagged) return null;
|
|
1994
2079
|
const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
|
|
1995
|
-
if (!navigation) return
|
|
1996
|
-
return enrichWithNavigation(graph, errorNodeId,
|
|
2080
|
+
if (!navigation) return tagged.result;
|
|
2081
|
+
return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
|
|
1997
2082
|
}
|
|
1998
|
-
function enrichWithNavigation(graph, errorNodeId,
|
|
2083
|
+
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
2084
|
+
const legacy = tagged.result;
|
|
1999
2085
|
const seedNode = legacy.rootCauseNode;
|
|
2000
2086
|
const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
|
|
2001
2087
|
const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
|
|
2002
2088
|
const candidates = [];
|
|
2089
|
+
const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
|
|
2090
|
+
const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
|
|
2003
2091
|
if (seedCtx && isVictimSeed(seedCtx)) {
|
|
2004
2092
|
const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
|
|
2005
2093
|
const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
|
|
@@ -2024,6 +2112,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
2024
2112
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
2025
2113
|
...lastProv ? { provenance: lastProv } : {}
|
|
2026
2114
|
});
|
|
2115
|
+
} else if (staleChain) {
|
|
2116
|
+
const culprit = staleChain.culprit;
|
|
2117
|
+
const culpritName = displayNameOf(culprit);
|
|
2118
|
+
const seedName = displayNameOf(seedNode);
|
|
2119
|
+
const staleConfidence = confidenceFromMix(staleChain.edges, now);
|
|
2120
|
+
candidates.push({
|
|
2121
|
+
node: culprit,
|
|
2122
|
+
classification: "primary-failure",
|
|
2123
|
+
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.`,
|
|
2124
|
+
context: nodeContext(graph, culprit, incidents, now),
|
|
2125
|
+
confidence: staleConfidence,
|
|
2126
|
+
provenance: import_types.Provenance.STALE
|
|
2127
|
+
});
|
|
2128
|
+
candidates.push({
|
|
2129
|
+
node: seedNode,
|
|
2130
|
+
classification: "symptom-only",
|
|
2131
|
+
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.`,
|
|
2132
|
+
context: seedCtx ?? EMPTY_CONTEXT,
|
|
2133
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
|
|
2134
|
+
...lastProv ? { provenance: lastProv } : {}
|
|
2135
|
+
});
|
|
2027
2136
|
} else {
|
|
2028
2137
|
candidates.push({
|
|
2029
2138
|
node: seedNode,
|
|
@@ -2037,11 +2146,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
2037
2146
|
const top = candidates[0];
|
|
2038
2147
|
let traversalPath = legacy.traversalPath;
|
|
2039
2148
|
let edgeProvenances = legacy.edgeProvenances;
|
|
2040
|
-
if (top.node
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2149
|
+
if (staleChain && top.node === staleChain.culprit) {
|
|
2150
|
+
traversalPath = staleChain.path;
|
|
2151
|
+
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
2152
|
+
} else if (top.node !== seedNode) {
|
|
2153
|
+
const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
2154
|
+
if (path76) {
|
|
2155
|
+
traversalPath = path76.nodes;
|
|
2156
|
+
edgeProvenances = path76.edges.map((e) => e.provenance);
|
|
2045
2157
|
} else {
|
|
2046
2158
|
traversalPath = [errorNodeId, top.node];
|
|
2047
2159
|
edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
|
|
@@ -2063,6 +2175,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
|
|
|
2063
2175
|
return legacy.fixRecommendation;
|
|
2064
2176
|
}
|
|
2065
2177
|
const name = top.node.replace(/^service:/, "");
|
|
2178
|
+
if (top.provenance === import_types.Provenance.STALE) {
|
|
2179
|
+
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}.`;
|
|
2180
|
+
}
|
|
2066
2181
|
if (top.classification === "primary-failure") {
|
|
2067
2182
|
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
2183
|
}
|
|
@@ -2581,6 +2696,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
|
2581
2696
|
var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
|
|
2582
2697
|
var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
|
|
2583
2698
|
var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
|
|
2699
|
+
var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
|
|
2584
2700
|
var import_types6 = require("@neat.is/types");
|
|
2585
2701
|
|
|
2586
2702
|
// src/extract/shared.ts
|
|
@@ -2861,7 +2977,7 @@ function buildServiceHostIndex(services) {
|
|
|
2861
2977
|
async function walkSourceFiles(dir, excludeDirs = []) {
|
|
2862
2978
|
const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
|
|
2863
2979
|
const out = [];
|
|
2864
|
-
async function
|
|
2980
|
+
async function walk10(current) {
|
|
2865
2981
|
const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2866
2982
|
for (const entry of entries) {
|
|
2867
2983
|
const full = import_node_path5.default.join(current, entry.name);
|
|
@@ -2869,7 +2985,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2869
2985
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2870
2986
|
if (excluded.has(import_node_path5.default.resolve(full))) continue;
|
|
2871
2987
|
if (await isPythonVenvDir(full)) continue;
|
|
2872
|
-
await
|
|
2988
|
+
await walk10(full);
|
|
2873
2989
|
} 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
2990
|
// would attribute our instrumentation imports to the user's service.
|
|
2875
2991
|
!isNeatAuthoredSourceFile(entry.name)) {
|
|
@@ -2877,7 +2993,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2877
2993
|
}
|
|
2878
2994
|
}
|
|
2879
2995
|
}
|
|
2880
|
-
await
|
|
2996
|
+
await walk10(dir);
|
|
2881
2997
|
return out;
|
|
2882
2998
|
}
|
|
2883
2999
|
async function loadSourceFiles(dir, excludeDirs = []) {
|
|
@@ -3397,6 +3513,11 @@ function makePhpParser() {
|
|
|
3397
3513
|
p.setLanguage(import_tree_sitter_php.default.php_only);
|
|
3398
3514
|
return p;
|
|
3399
3515
|
}
|
|
3516
|
+
function makeRustParser() {
|
|
3517
|
+
const p = new import_tree_sitter2.default();
|
|
3518
|
+
p.setLanguage(import_tree_sitter_rust.default);
|
|
3519
|
+
return p;
|
|
3520
|
+
}
|
|
3400
3521
|
var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
3401
3522
|
"get",
|
|
3402
3523
|
"post",
|
|
@@ -3468,8 +3589,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3468
3589
|
chiWalk(tree.rootNode, "", out);
|
|
3469
3590
|
return out;
|
|
3470
3591
|
}
|
|
3471
|
-
function stripChiRegex(
|
|
3472
|
-
return
|
|
3592
|
+
function stripChiRegex(path76) {
|
|
3593
|
+
return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3473
3594
|
}
|
|
3474
3595
|
function chiWalk(node, prefix, out) {
|
|
3475
3596
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -4141,9 +4262,9 @@ function rubyRocketRoute(args) {
|
|
|
4141
4262
|
if (!pair || pair.type !== "pair") continue;
|
|
4142
4263
|
const k = pair.childForFieldName("key");
|
|
4143
4264
|
if (k?.type !== "string") continue;
|
|
4144
|
-
const
|
|
4145
|
-
if (
|
|
4146
|
-
return { path:
|
|
4265
|
+
const path76 = rubyLiteral(k);
|
|
4266
|
+
if (path76 === null) continue;
|
|
4267
|
+
return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
4147
4268
|
}
|
|
4148
4269
|
return null;
|
|
4149
4270
|
}
|
|
@@ -4313,6 +4434,48 @@ function railsRoutesFromSource(source, parser) {
|
|
|
4313
4434
|
});
|
|
4314
4435
|
return out;
|
|
4315
4436
|
}
|
|
4437
|
+
var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4438
|
+
function sinatraRoutesFromSource(source, parser) {
|
|
4439
|
+
const tree = parseSource2(parser, source);
|
|
4440
|
+
if (!fileReferencesSinatra(tree.rootNode)) return [];
|
|
4441
|
+
const out = [];
|
|
4442
|
+
walk(tree.rootNode, (node) => {
|
|
4443
|
+
if (node.type !== "call") return;
|
|
4444
|
+
if (node.childForFieldName("receiver")) return;
|
|
4445
|
+
const method = node.childForFieldName("method")?.text;
|
|
4446
|
+
if (!method || !SINATRA_VERBS.has(method)) return;
|
|
4447
|
+
if (!node.childForFieldName("block")) return;
|
|
4448
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
4449
|
+
if (first?.type !== "string") return;
|
|
4450
|
+
const p = rubyLiteral(first);
|
|
4451
|
+
if (p === null || !p.startsWith("/")) return;
|
|
4452
|
+
out.push({
|
|
4453
|
+
method: method.toUpperCase(),
|
|
4454
|
+
pathTemplate: canonicalizeTemplate(p),
|
|
4455
|
+
line: node.startPosition.row + 1,
|
|
4456
|
+
framework: "sinatra"
|
|
4457
|
+
});
|
|
4458
|
+
});
|
|
4459
|
+
return out;
|
|
4460
|
+
}
|
|
4461
|
+
function fileReferencesSinatra(root) {
|
|
4462
|
+
let found = false;
|
|
4463
|
+
walk(root, (node) => {
|
|
4464
|
+
if (found) return;
|
|
4465
|
+
if (node.type === "call") {
|
|
4466
|
+
const m = node.childForFieldName("method")?.text;
|
|
4467
|
+
if (m === "require" || m === "require_relative") {
|
|
4468
|
+
const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
4469
|
+
if (s !== null && /^sinatra\b/.test(s)) found = true;
|
|
4470
|
+
}
|
|
4471
|
+
return;
|
|
4472
|
+
}
|
|
4473
|
+
if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
|
|
4474
|
+
found = true;
|
|
4475
|
+
}
|
|
4476
|
+
});
|
|
4477
|
+
return found;
|
|
4478
|
+
}
|
|
4316
4479
|
var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
|
|
4317
4480
|
var LARAVEL_RESOURCE_ROWS = [
|
|
4318
4481
|
{ action: "index", methods: ["GET"], suffix: "" },
|
|
@@ -4488,6 +4651,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
|
|
|
4488
4651
|
}
|
|
4489
4652
|
return out;
|
|
4490
4653
|
}
|
|
4654
|
+
var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4655
|
+
function isSlimAppCtor(node) {
|
|
4656
|
+
if (!node) return false;
|
|
4657
|
+
if (node.type === "scoped_call_expression") {
|
|
4658
|
+
const scope = node.childForFieldName("scope")?.text ?? "";
|
|
4659
|
+
const name = node.childForFieldName("name")?.text;
|
|
4660
|
+
return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
|
|
4661
|
+
}
|
|
4662
|
+
if (node.type === "object_creation_expression") {
|
|
4663
|
+
const cls = node.namedChild(0)?.text ?? "";
|
|
4664
|
+
return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
|
|
4665
|
+
}
|
|
4666
|
+
return false;
|
|
4667
|
+
}
|
|
4668
|
+
function isSlimAppType(node) {
|
|
4669
|
+
if (!node || node.type !== "named_type") return false;
|
|
4670
|
+
const text = node.text;
|
|
4671
|
+
return text === "App" || text.endsWith("\\App");
|
|
4672
|
+
}
|
|
4673
|
+
function collectSlimAppVars(root) {
|
|
4674
|
+
const vars = /* @__PURE__ */ new Set();
|
|
4675
|
+
walk(root, (node) => {
|
|
4676
|
+
if (node.type === "assignment_expression") {
|
|
4677
|
+
const left = node.childForFieldName("left");
|
|
4678
|
+
if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
|
|
4679
|
+
vars.add(left.text);
|
|
4680
|
+
}
|
|
4681
|
+
return;
|
|
4682
|
+
}
|
|
4683
|
+
if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
|
|
4684
|
+
const name = node.childForFieldName("name");
|
|
4685
|
+
if (name?.type === "variable_name") vars.add(name.text);
|
|
4686
|
+
}
|
|
4687
|
+
});
|
|
4688
|
+
return vars;
|
|
4689
|
+
}
|
|
4690
|
+
function slimClosureParamVars(closure) {
|
|
4691
|
+
const out = ["$this"];
|
|
4692
|
+
for (let i = 0; i < closure.namedChildCount; i++) {
|
|
4693
|
+
const params = closure.namedChild(i);
|
|
4694
|
+
if (params?.type !== "formal_parameters") continue;
|
|
4695
|
+
for (let j = 0; j < params.namedChildCount; j++) {
|
|
4696
|
+
const param = params.namedChild(j);
|
|
4697
|
+
if (param?.type !== "simple_parameter") continue;
|
|
4698
|
+
for (let k = 0; k < param.namedChildCount; k++) {
|
|
4699
|
+
const v = param.namedChild(k);
|
|
4700
|
+
if (v?.type === "variable_name") {
|
|
4701
|
+
out.push(v.text);
|
|
4702
|
+
break;
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
return out;
|
|
4708
|
+
}
|
|
4709
|
+
function phpStringArray(node) {
|
|
4710
|
+
const out = [];
|
|
4711
|
+
if (node?.type !== "array_creation_expression") return out;
|
|
4712
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4713
|
+
const el = node.namedChild(i);
|
|
4714
|
+
if (el?.type !== "array_element_initializer") continue;
|
|
4715
|
+
const s = phpStaticString(el.namedChild(0));
|
|
4716
|
+
if (s !== null) out.push(s);
|
|
4717
|
+
}
|
|
4718
|
+
return out;
|
|
4719
|
+
}
|
|
4720
|
+
function slimRoutesFromSource(source, parser) {
|
|
4721
|
+
const tree = parseSource2(parser, source);
|
|
4722
|
+
const appVars = collectSlimAppVars(tree.rootNode);
|
|
4723
|
+
if (appVars.size === 0) return [];
|
|
4724
|
+
const out = [];
|
|
4725
|
+
slimWalk(tree.rootNode, "", appVars, out);
|
|
4726
|
+
return out;
|
|
4727
|
+
}
|
|
4728
|
+
function slimWalk(node, prefix, appVars, out) {
|
|
4729
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4730
|
+
const child = node.namedChild(i);
|
|
4731
|
+
if (child) slimHandle(child, prefix, appVars, out);
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
function slimHandle(node, prefix, appVars, out) {
|
|
4735
|
+
if (node.type === "member_call_expression") {
|
|
4736
|
+
const obj = node.childForFieldName("object");
|
|
4737
|
+
const method = node.childForFieldName("name")?.text;
|
|
4738
|
+
const args = node.childForFieldName("arguments");
|
|
4739
|
+
if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
|
|
4740
|
+
const line = node.startPosition.row + 1;
|
|
4741
|
+
if (method === "group") {
|
|
4742
|
+
const groupPrefix = phpFirstString(args);
|
|
4743
|
+
const closure = laravelGroupClosure(args);
|
|
4744
|
+
if (groupPrefix !== null && closure) {
|
|
4745
|
+
const inner = new Set(appVars);
|
|
4746
|
+
for (const v of slimClosureParamVars(closure)) inner.add(v);
|
|
4747
|
+
const body = closure.childForFieldName("body");
|
|
4748
|
+
if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
|
|
4749
|
+
}
|
|
4750
|
+
return;
|
|
4751
|
+
}
|
|
4752
|
+
if (SLIM_VERBS.has(method)) {
|
|
4753
|
+
const p = phpFirstString(args);
|
|
4754
|
+
if (p !== null) {
|
|
4755
|
+
out.push({
|
|
4756
|
+
method: method.toUpperCase(),
|
|
4757
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4758
|
+
line,
|
|
4759
|
+
framework: "slim"
|
|
4760
|
+
});
|
|
4761
|
+
}
|
|
4762
|
+
return;
|
|
4763
|
+
}
|
|
4764
|
+
if (method === "any") {
|
|
4765
|
+
const p = phpFirstString(args);
|
|
4766
|
+
if (p !== null) {
|
|
4767
|
+
out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
|
|
4768
|
+
}
|
|
4769
|
+
return;
|
|
4770
|
+
}
|
|
4771
|
+
if (method === "map") {
|
|
4772
|
+
const vals = phpArgumentValues(args);
|
|
4773
|
+
const methods = phpStringArray(vals[0]);
|
|
4774
|
+
const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
|
|
4775
|
+
if (p !== null) {
|
|
4776
|
+
for (const m of methods) {
|
|
4777
|
+
out.push({
|
|
4778
|
+
method: m.toUpperCase(),
|
|
4779
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4780
|
+
line,
|
|
4781
|
+
framework: "slim"
|
|
4782
|
+
});
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
return;
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
slimWalk(node, prefix, appVars, out);
|
|
4790
|
+
}
|
|
4791
|
+
var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
|
|
4792
|
+
function rustStringContent(node) {
|
|
4793
|
+
if (!node || node.type !== "string_literal") return null;
|
|
4794
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4795
|
+
if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
|
|
4796
|
+
}
|
|
4797
|
+
return "";
|
|
4798
|
+
}
|
|
4799
|
+
function actixRoutesFromSource(source, parser) {
|
|
4800
|
+
const tree = parseSource2(parser, source);
|
|
4801
|
+
const out = [];
|
|
4802
|
+
walk(tree.rootNode, (node) => {
|
|
4803
|
+
if (node.type === "attribute_item") {
|
|
4804
|
+
actixAttributeRoute(node, out);
|
|
4805
|
+
return;
|
|
4806
|
+
}
|
|
4807
|
+
if (node.type === "call_expression") {
|
|
4808
|
+
actixBuilderRoute(node, out);
|
|
4809
|
+
}
|
|
4810
|
+
});
|
|
4811
|
+
return out;
|
|
4812
|
+
}
|
|
4813
|
+
function actixAttributeRoute(attrItem, out) {
|
|
4814
|
+
const attr = attrItem.namedChild(0);
|
|
4815
|
+
if (!attr || attr.type !== "attribute") return;
|
|
4816
|
+
const nameNode = attr.namedChild(0);
|
|
4817
|
+
if (!nameNode) return;
|
|
4818
|
+
const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
|
|
4819
|
+
if (!macro) return;
|
|
4820
|
+
const tokens = attr.childForFieldName("arguments");
|
|
4821
|
+
if (!tokens || tokens.type !== "token_tree") return;
|
|
4822
|
+
const strings = [];
|
|
4823
|
+
for (let i = 0; i < tokens.namedChildCount; i++) {
|
|
4824
|
+
const s = rustStringContent(tokens.namedChild(i));
|
|
4825
|
+
if (s !== null) strings.push(s);
|
|
4826
|
+
}
|
|
4827
|
+
const pathStr = strings[0];
|
|
4828
|
+
if (pathStr === void 0 || !pathStr.startsWith("/")) return;
|
|
4829
|
+
const line = attrItem.startPosition.row + 1;
|
|
4830
|
+
const template = canonicalizeTemplate(pathStr);
|
|
4831
|
+
if (ACTIX_METHODS.has(macro)) {
|
|
4832
|
+
out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
|
|
4833
|
+
return;
|
|
4834
|
+
}
|
|
4835
|
+
if (macro === "route") {
|
|
4836
|
+
const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
|
|
4837
|
+
const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
|
|
4838
|
+
for (const m of list) {
|
|
4839
|
+
out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
function actixBuilderRoute(call, out) {
|
|
4844
|
+
const fn = call.childForFieldName("function");
|
|
4845
|
+
if (fn?.type !== "field_expression") return;
|
|
4846
|
+
if (fn.childForFieldName("field")?.text !== "route") return;
|
|
4847
|
+
const args = call.childForFieldName("arguments");
|
|
4848
|
+
const pathStr = rustStringContent(args?.namedChild(0));
|
|
4849
|
+
if (pathStr === null || !pathStr.startsWith("/")) return;
|
|
4850
|
+
const second = args?.namedChild(1);
|
|
4851
|
+
if (!second) return;
|
|
4852
|
+
const method = actixBuilderMethod(second);
|
|
4853
|
+
if (!method) return;
|
|
4854
|
+
out.push({
|
|
4855
|
+
method,
|
|
4856
|
+
pathTemplate: canonicalizeTemplate(pathStr),
|
|
4857
|
+
line: call.startPosition.row + 1,
|
|
4858
|
+
framework: "actix-web"
|
|
4859
|
+
});
|
|
4860
|
+
}
|
|
4861
|
+
function actixBuilderMethod(node) {
|
|
4862
|
+
let method = null;
|
|
4863
|
+
walk(node, (n) => {
|
|
4864
|
+
if (method || n.type !== "scoped_identifier") return;
|
|
4865
|
+
const verb = n.childForFieldName("name")?.text;
|
|
4866
|
+
const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
|
|
4867
|
+
if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
|
|
4868
|
+
});
|
|
4869
|
+
return method;
|
|
4870
|
+
}
|
|
4491
4871
|
function namedArgs(argsNode) {
|
|
4492
4872
|
const out = [];
|
|
4493
4873
|
if (!argsNode) return out;
|
|
@@ -4804,6 +5184,7 @@ async function addRoutes(graph, services) {
|
|
|
4804
5184
|
const goParser = makeGoParser2();
|
|
4805
5185
|
const rubyParser = makeRubyParser();
|
|
4806
5186
|
const phpParser = makePhpParser();
|
|
5187
|
+
const rustParser = makeRustParser();
|
|
4807
5188
|
let nodesAdded = 0;
|
|
4808
5189
|
let edgesAdded = 0;
|
|
4809
5190
|
for (const service of services) {
|
|
@@ -4826,7 +5207,10 @@ async function addRoutes(graph, services) {
|
|
|
4826
5207
|
const isGoService = service.node.language === "go";
|
|
4827
5208
|
const hasRails = deps["rails"] !== void 0;
|
|
4828
5209
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4829
|
-
|
|
5210
|
+
const hasSlim = deps["slim/slim"] !== void 0;
|
|
5211
|
+
const hasSinatra = deps["sinatra"] !== void 0;
|
|
5212
|
+
const hasActix = deps["actix-web"] !== void 0;
|
|
5213
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
|
|
4830
5214
|
continue;
|
|
4831
5215
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
4832
5216
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4837,7 +5221,8 @@ async function addRoutes(graph, services) {
|
|
|
4837
5221
|
const isGo = ext === ".go";
|
|
4838
5222
|
const isRb = ext === ".rb";
|
|
4839
5223
|
const isPhp = ext === ".php";
|
|
4840
|
-
|
|
5224
|
+
const isRs = ext === ".rs";
|
|
5225
|
+
if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
|
|
4841
5226
|
const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
|
|
4842
5227
|
let routes;
|
|
4843
5228
|
try {
|
|
@@ -4847,8 +5232,12 @@ async function addRoutes(graph, services) {
|
|
|
4847
5232
|
phpParser,
|
|
4848
5233
|
relFile === "routes/api.php" ? "/api" : ""
|
|
4849
5234
|
) : [];
|
|
5235
|
+
if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
|
|
4850
5236
|
} else if (isRb) {
|
|
4851
5237
|
routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
|
|
5238
|
+
if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
|
|
5239
|
+
} else if (isRs) {
|
|
5240
|
+
routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
|
|
4852
5241
|
} else if (isGo) {
|
|
4853
5242
|
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4854
5243
|
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
@@ -5083,6 +5472,37 @@ function loadIncidentThresholdsFromEnv() {
|
|
|
5083
5472
|
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
5084
5473
|
}
|
|
5085
5474
|
}
|
|
5475
|
+
var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
|
|
5476
|
+
function latencyStreamCeilingMs() {
|
|
5477
|
+
const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
|
|
5478
|
+
if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5479
|
+
const n = Number(raw);
|
|
5480
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
5481
|
+
console.warn(
|
|
5482
|
+
`[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
|
|
5483
|
+
);
|
|
5484
|
+
return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5485
|
+
}
|
|
5486
|
+
function spanServesEventStream(attrs) {
|
|
5487
|
+
for (const key of [
|
|
5488
|
+
"http.response.header.content-type",
|
|
5489
|
+
"http.response.header.content_type"
|
|
5490
|
+
]) {
|
|
5491
|
+
const v = attrs[key];
|
|
5492
|
+
const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
|
|
5493
|
+
for (const item of values) {
|
|
5494
|
+
if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
|
|
5495
|
+
return true;
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
return false;
|
|
5500
|
+
}
|
|
5501
|
+
function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
|
|
5502
|
+
if (span.websocketChannel !== void 0) return true;
|
|
5503
|
+
if (spanServesEventStream(span.attributes)) return true;
|
|
5504
|
+
return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
|
|
5505
|
+
}
|
|
5086
5506
|
function httpResponseStatusFromAttrs(attrs) {
|
|
5087
5507
|
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
5088
5508
|
const v = attrs[key];
|
|
@@ -5134,6 +5554,14 @@ function grpcStatusCodeFromAttrs(attrs) {
|
|
|
5134
5554
|
}
|
|
5135
5555
|
return void 0;
|
|
5136
5556
|
}
|
|
5557
|
+
function spanRecordsError(span) {
|
|
5558
|
+
if (span.statusCode === 2) return true;
|
|
5559
|
+
const grpc2 = grpcStatusCodeFromAttrs(span.attributes);
|
|
5560
|
+
if (grpc2 !== void 0 && grpc2 !== 0) return true;
|
|
5561
|
+
const httpStatus = httpResponseStatusFromAttrs(span.attributes);
|
|
5562
|
+
if (httpStatus !== void 0 && httpStatus >= 500) return true;
|
|
5563
|
+
return false;
|
|
5564
|
+
}
|
|
5137
5565
|
function nonHttpFailureMessageFromAttrs(attrs) {
|
|
5138
5566
|
const grpc2 = grpcStatusCodeFromAttrs(attrs);
|
|
5139
5567
|
if (grpc2 !== void 0 && grpc2 !== 0) {
|
|
@@ -5932,6 +6360,21 @@ async function recordExceptionIncident(ctx, span, ts) {
|
|
|
5932
6360
|
};
|
|
5933
6361
|
await appendErrorEvent(ctx, ev);
|
|
5934
6362
|
}
|
|
6363
|
+
async function recordGrpcFailureIncident(ctx, span, ts) {
|
|
6364
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
6365
|
+
const ev = {
|
|
6366
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
6367
|
+
timestamp: ts,
|
|
6368
|
+
service: span.service,
|
|
6369
|
+
traceId: span.traceId,
|
|
6370
|
+
spanId: span.spanId,
|
|
6371
|
+
errorType: "grpc-failure",
|
|
6372
|
+
errorMessage: incidentMessage(span),
|
|
6373
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
6374
|
+
affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath)
|
|
6375
|
+
};
|
|
6376
|
+
await appendErrorEvent(ctx, ev);
|
|
6377
|
+
}
|
|
5935
6378
|
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
5936
6379
|
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
5937
6380
|
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
@@ -5975,6 +6418,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
|
5975
6418
|
);
|
|
5976
6419
|
ctx.burstState.delete(key);
|
|
5977
6420
|
}
|
|
6421
|
+
var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
|
|
6422
|
+
function nextApiRouteTemplate(span) {
|
|
6423
|
+
const raw = pickAttr(span, "next.span_name") ?? span.name;
|
|
6424
|
+
const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
|
|
6425
|
+
return match ? match[1] : void 0;
|
|
6426
|
+
}
|
|
5978
6427
|
function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
|
|
5979
6428
|
const target = normalizePathTemplate(httpRoute);
|
|
5980
6429
|
const m = method?.toUpperCase();
|
|
@@ -5996,8 +6445,8 @@ async function handleSpan(ctx, span) {
|
|
|
5996
6445
|
warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT);
|
|
5997
6446
|
}
|
|
5998
6447
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
5999
|
-
const isError = span
|
|
6000
|
-
const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6448
|
+
const isError = spanRecordsError(span);
|
|
6449
|
+
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6001
6450
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
6002
6451
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
6003
6452
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -6194,12 +6643,13 @@ async function handleSpan(ctx, span) {
|
|
|
6194
6643
|
}
|
|
6195
6644
|
}
|
|
6196
6645
|
}
|
|
6197
|
-
|
|
6646
|
+
const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
|
|
6647
|
+
if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
|
|
6198
6648
|
const routeNodeId = findRouteNodeByHttpRoute(
|
|
6199
6649
|
ctx.graph,
|
|
6200
6650
|
span.service,
|
|
6201
6651
|
span.httpMethod,
|
|
6202
|
-
|
|
6652
|
+
fusionRoute
|
|
6203
6653
|
);
|
|
6204
6654
|
if (routeNodeId) {
|
|
6205
6655
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
@@ -6231,10 +6681,13 @@ async function handleSpan(ctx, span) {
|
|
|
6231
6681
|
}
|
|
6232
6682
|
if (span.statusCode !== 2) {
|
|
6233
6683
|
const status2 = httpResponseStatus(span);
|
|
6684
|
+
const grpcStatus = grpcStatusCodeFromAttrs(span.attributes);
|
|
6234
6685
|
if (span.exception) {
|
|
6235
6686
|
await recordExceptionIncident(ctx, span, ts);
|
|
6236
6687
|
} else if (status2 !== void 0 && status2 >= 500) {
|
|
6237
6688
|
await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
|
|
6689
|
+
} else if (grpcStatus !== void 0 && grpcStatus !== 0) {
|
|
6690
|
+
await recordGrpcFailureIncident(ctx, span, ts);
|
|
6238
6691
|
} else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
6239
6692
|
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
|
|
6240
6693
|
}
|
|
@@ -7590,7 +8043,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
|
|
|
7590
8043
|
var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
7591
8044
|
var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
|
|
7592
8045
|
var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
|
|
7593
|
-
var
|
|
8046
|
+
var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
|
|
7594
8047
|
var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
|
|
7595
8048
|
var import_types20 = require("@neat.is/types");
|
|
7596
8049
|
var PARSE_CHUNK3 = 16384;
|
|
@@ -7611,7 +8064,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
|
|
|
7611
8064
|
".cs": import_tree_sitter_c_sharp.default,
|
|
7612
8065
|
".java": import_tree_sitter_java.default,
|
|
7613
8066
|
".kt": import_tree_sitter_kotlin.default,
|
|
7614
|
-
".rs":
|
|
8067
|
+
".rs": import_tree_sitter_rust2.default,
|
|
7615
8068
|
// C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
|
|
7616
8069
|
// `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
|
|
7617
8070
|
// headers. `.h` and `.c` are deliberately absent: they are shared with C (a
|
|
@@ -8051,15 +8504,15 @@ function collectKotlinSymbolDefs(root) {
|
|
|
8051
8504
|
});
|
|
8052
8505
|
};
|
|
8053
8506
|
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
8054
|
-
const
|
|
8507
|
+
const firstChildOfType2 = (node, types) => {
|
|
8055
8508
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8056
8509
|
const child = node.namedChild(i);
|
|
8057
8510
|
if (child && types.includes(child.type)) return child;
|
|
8058
8511
|
}
|
|
8059
8512
|
return void 0;
|
|
8060
8513
|
};
|
|
8061
|
-
const nameOf = (node, ...types) =>
|
|
8062
|
-
const bodyOf = (node) =>
|
|
8514
|
+
const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
|
|
8515
|
+
const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
|
|
8063
8516
|
let pkg;
|
|
8064
8517
|
for (let i = 0; i < root.namedChildCount; i++) {
|
|
8065
8518
|
const child = root.namedChild(i);
|
|
@@ -8560,7 +9013,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
8560
9013
|
return best;
|
|
8561
9014
|
};
|
|
8562
9015
|
const requests = [];
|
|
8563
|
-
const
|
|
9016
|
+
const walk10 = (node) => {
|
|
8564
9017
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
8565
9018
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
8566
9019
|
if (self && self.kind === "class") {
|
|
@@ -8606,10 +9059,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
8606
9059
|
}
|
|
8607
9060
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8608
9061
|
const child = node.namedChild(i);
|
|
8609
|
-
if (child)
|
|
9062
|
+
if (child) walk10(child);
|
|
8610
9063
|
}
|
|
8611
9064
|
};
|
|
8612
|
-
|
|
9065
|
+
walk10(root);
|
|
8613
9066
|
for (const req of requests) {
|
|
8614
9067
|
const targetSid = resolveTarget(req.targetName, req.wantKind);
|
|
8615
9068
|
if (!targetSid) continue;
|
|
@@ -8905,7 +9358,7 @@ async function addServerActions(graph, services) {
|
|
|
8905
9358
|
|
|
8906
9359
|
// src/extract/databases/index.ts
|
|
8907
9360
|
init_cjs_shims();
|
|
8908
|
-
var
|
|
9361
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
8909
9362
|
var import_types23 = require("@neat.is/types");
|
|
8910
9363
|
|
|
8911
9364
|
// src/extract/databases/db-config-yaml.ts
|
|
@@ -9340,9 +9793,163 @@ async function parse8(serviceDir) {
|
|
|
9340
9793
|
}
|
|
9341
9794
|
var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
9342
9795
|
|
|
9343
|
-
// src/extract/databases/
|
|
9796
|
+
// src/extract/databases/csharp.ts
|
|
9344
9797
|
init_cjs_shims();
|
|
9798
|
+
var import_node_fs22 = require("fs");
|
|
9345
9799
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
9800
|
+
var CS_EXT = ".cs";
|
|
9801
|
+
var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
|
|
9802
|
+
var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
|
|
9803
|
+
var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
|
|
9804
|
+
var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
9805
|
+
function hostIsUnresolved(host) {
|
|
9806
|
+
return host === "" || /[${}]/.test(host);
|
|
9807
|
+
}
|
|
9808
|
+
function looksLikePostgres(s) {
|
|
9809
|
+
return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
|
|
9810
|
+
}
|
|
9811
|
+
function looksLikeRedis(s) {
|
|
9812
|
+
return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
|
|
9813
|
+
}
|
|
9814
|
+
function parsePostgresConnection(raw) {
|
|
9815
|
+
const s = raw.trim();
|
|
9816
|
+
if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9817
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9818
|
+
for (const part of s.split(";")) {
|
|
9819
|
+
const eq = part.indexOf("=");
|
|
9820
|
+
if (eq < 0) continue;
|
|
9821
|
+
const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
|
|
9822
|
+
const value = part.slice(eq + 1).trim();
|
|
9823
|
+
if (value && !fields.has(key)) fields.set(key, value);
|
|
9824
|
+
}
|
|
9825
|
+
const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
|
|
9826
|
+
if (!hostRaw) return null;
|
|
9827
|
+
const host = hostRaw.split(",")[0].trim();
|
|
9828
|
+
if (hostIsUnresolved(host)) return null;
|
|
9829
|
+
const portRaw = fields.get("port");
|
|
9830
|
+
const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
|
|
9831
|
+
const database = fields.get("database") ?? fields.get("db") ?? "";
|
|
9832
|
+
return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
|
|
9833
|
+
}
|
|
9834
|
+
function parseRedisEndpoint(raw) {
|
|
9835
|
+
const s = raw.trim();
|
|
9836
|
+
if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9837
|
+
const first = s.split(",")[0].trim();
|
|
9838
|
+
const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
|
|
9839
|
+
if (!m) return null;
|
|
9840
|
+
const host = m[1];
|
|
9841
|
+
if (hostIsUnresolved(host) || host.includes("=")) return null;
|
|
9842
|
+
const port = m[2] ? Number(m[2]) : void 0;
|
|
9843
|
+
return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
|
|
9844
|
+
}
|
|
9845
|
+
async function resolveEnvUpTree(startDir, name) {
|
|
9846
|
+
let dir = import_node_path32.default.resolve(startDir);
|
|
9847
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
9848
|
+
const value = await resolveEnvVar(dir, name);
|
|
9849
|
+
if (value !== null) return value;
|
|
9850
|
+
const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
|
|
9851
|
+
const parent = import_node_path32.default.dirname(dir);
|
|
9852
|
+
if (atRepoRoot || parent === dir) break;
|
|
9853
|
+
dir = parent;
|
|
9854
|
+
}
|
|
9855
|
+
return null;
|
|
9856
|
+
}
|
|
9857
|
+
async function interpolateEnvRefs(value, dir) {
|
|
9858
|
+
const refs = /* @__PURE__ */ new Set();
|
|
9859
|
+
for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
9860
|
+
refs.add(m[1] ?? m[2]);
|
|
9861
|
+
}
|
|
9862
|
+
let out = value;
|
|
9863
|
+
for (const name of refs) {
|
|
9864
|
+
const resolved = await resolveEnvUpTree(dir, name);
|
|
9865
|
+
if (resolved === null) continue;
|
|
9866
|
+
out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
|
|
9867
|
+
}
|
|
9868
|
+
return out;
|
|
9869
|
+
}
|
|
9870
|
+
function stringLiterals(masked) {
|
|
9871
|
+
const out = [];
|
|
9872
|
+
STRING_LITERAL_RE.lastIndex = 0;
|
|
9873
|
+
let m;
|
|
9874
|
+
while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
|
|
9875
|
+
return out;
|
|
9876
|
+
}
|
|
9877
|
+
function envKeys(masked) {
|
|
9878
|
+
const out = [];
|
|
9879
|
+
ENV_READ_RE.lastIndex = 0;
|
|
9880
|
+
let m;
|
|
9881
|
+
while ((m = ENV_READ_RE.exec(masked)) !== null) {
|
|
9882
|
+
const key = m[1] ?? m[2];
|
|
9883
|
+
if (key) out.push(key);
|
|
9884
|
+
}
|
|
9885
|
+
return out;
|
|
9886
|
+
}
|
|
9887
|
+
async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
|
|
9888
|
+
const out = [];
|
|
9889
|
+
for (const key of keys) {
|
|
9890
|
+
const raw = await resolveEnvUpTree(serviceDir, key);
|
|
9891
|
+
if (raw === null) continue;
|
|
9892
|
+
const value = await interpolateEnvRefs(raw, serviceDir);
|
|
9893
|
+
if (!looksLike(value)) continue;
|
|
9894
|
+
const parsed = parse11(value);
|
|
9895
|
+
if (parsed) out.push(parsed);
|
|
9896
|
+
}
|
|
9897
|
+
for (const lit of literals) {
|
|
9898
|
+
if (!looksLike(lit)) continue;
|
|
9899
|
+
const parsed = parse11(lit);
|
|
9900
|
+
if (parsed) out.push(parsed);
|
|
9901
|
+
}
|
|
9902
|
+
return out;
|
|
9903
|
+
}
|
|
9904
|
+
async function parse9(serviceDir) {
|
|
9905
|
+
const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
|
|
9906
|
+
(f) => import_node_path32.default.extname(f) === CS_EXT
|
|
9907
|
+
);
|
|
9908
|
+
if (files.length === 0) return [];
|
|
9909
|
+
const sources = [];
|
|
9910
|
+
for (const file of files) {
|
|
9911
|
+
const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
|
|
9912
|
+
if (content !== null) sources.push({ file, content });
|
|
9913
|
+
}
|
|
9914
|
+
let pgGateFile = null;
|
|
9915
|
+
let redisGateFile = null;
|
|
9916
|
+
for (const { file, content } of sources) {
|
|
9917
|
+
if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
|
|
9918
|
+
if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
|
|
9919
|
+
}
|
|
9920
|
+
if (!pgGateFile && !redisGateFile) return [];
|
|
9921
|
+
const literals = [];
|
|
9922
|
+
const keys = [];
|
|
9923
|
+
for (const { content } of sources) {
|
|
9924
|
+
const masked = maskCommentsInSource(content);
|
|
9925
|
+
literals.push(...stringLiterals(masked));
|
|
9926
|
+
keys.push(...envKeys(masked));
|
|
9927
|
+
}
|
|
9928
|
+
const out = [];
|
|
9929
|
+
const seenHosts = /* @__PURE__ */ new Set();
|
|
9930
|
+
const push = (config, sourceFile) => {
|
|
9931
|
+
const dedupe = `${config.engine}:${config.host}`;
|
|
9932
|
+
if (seenHosts.has(dedupe)) return;
|
|
9933
|
+
seenHosts.add(dedupe);
|
|
9934
|
+
out.push({ ...config, sourceFile });
|
|
9935
|
+
};
|
|
9936
|
+
if (pgGateFile) {
|
|
9937
|
+
for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
|
|
9938
|
+
push(pg3, pgGateFile);
|
|
9939
|
+
}
|
|
9940
|
+
}
|
|
9941
|
+
if (redisGateFile) {
|
|
9942
|
+
for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
|
|
9943
|
+
push(redis, redisGateFile);
|
|
9944
|
+
}
|
|
9945
|
+
}
|
|
9946
|
+
return out;
|
|
9947
|
+
}
|
|
9948
|
+
var csharpParser = { name: "csharp", parse: parse9 };
|
|
9949
|
+
|
|
9950
|
+
// src/extract/databases/docker-compose.ts
|
|
9951
|
+
init_cjs_shims();
|
|
9952
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
9346
9953
|
function portFromService(svc) {
|
|
9347
9954
|
for (const raw of svc.ports ?? []) {
|
|
9348
9955
|
const str = String(raw);
|
|
@@ -9367,9 +9974,9 @@ function databaseFromEnv(svc) {
|
|
|
9367
9974
|
};
|
|
9368
9975
|
return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
|
|
9369
9976
|
}
|
|
9370
|
-
async function
|
|
9977
|
+
async function parse10(serviceDir) {
|
|
9371
9978
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
9372
|
-
const abs =
|
|
9979
|
+
const abs = import_node_path33.default.join(serviceDir, name);
|
|
9373
9980
|
if (!await exists(abs)) continue;
|
|
9374
9981
|
const raw = await readYaml(abs);
|
|
9375
9982
|
if (!raw?.services) return [];
|
|
@@ -9391,7 +9998,7 @@ async function parse9(serviceDir) {
|
|
|
9391
9998
|
}
|
|
9392
9999
|
return [];
|
|
9393
10000
|
}
|
|
9394
|
-
var dockerComposeParser = { name: "docker-compose", parse:
|
|
10001
|
+
var dockerComposeParser = { name: "docker-compose", parse: parse10 };
|
|
9395
10002
|
|
|
9396
10003
|
// src/extract/databases/index.ts
|
|
9397
10004
|
var DB_PARSERS = [
|
|
@@ -9403,6 +10010,7 @@ var DB_PARSERS = [
|
|
|
9403
10010
|
ormconfigParser,
|
|
9404
10011
|
typeormParser,
|
|
9405
10012
|
sequelizeParser,
|
|
10013
|
+
csharpParser,
|
|
9406
10014
|
dockerComposeParser
|
|
9407
10015
|
];
|
|
9408
10016
|
function compatibleDriversFor(engine) {
|
|
@@ -9541,7 +10149,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9541
10149
|
discoveredVia: mergedDiscoveredVia
|
|
9542
10150
|
});
|
|
9543
10151
|
}
|
|
9544
|
-
const relConfigFile = toPosix(
|
|
10152
|
+
const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
|
|
9545
10153
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9546
10154
|
graph,
|
|
9547
10155
|
service.pkg.name,
|
|
@@ -9550,7 +10158,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9550
10158
|
);
|
|
9551
10159
|
nodesAdded += fn;
|
|
9552
10160
|
edgesAdded += fe;
|
|
9553
|
-
const evidenceFile = toPosix(
|
|
10161
|
+
const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
|
|
9554
10162
|
const edge = {
|
|
9555
10163
|
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
|
|
9556
10164
|
source: fileNodeId,
|
|
@@ -9568,15 +10176,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9568
10176
|
if (allConfigs.length === 1) {
|
|
9569
10177
|
const primary = allConfigs[0];
|
|
9570
10178
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
9571
|
-
const relPath =
|
|
10179
|
+
const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
|
|
9572
10180
|
const cfgId = (0, import_types23.configId)(relPath);
|
|
9573
10181
|
if (!graph.hasNode(cfgId)) {
|
|
9574
10182
|
const cfgNode = {
|
|
9575
10183
|
id: cfgId,
|
|
9576
10184
|
type: import_types23.NodeType.ConfigNode,
|
|
9577
|
-
name:
|
|
10185
|
+
name: import_node_path34.default.basename(primary.sourceFile),
|
|
9578
10186
|
path: relPath,
|
|
9579
|
-
fileType: isConfigFile(
|
|
10187
|
+
fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
|
|
9580
10188
|
};
|
|
9581
10189
|
graph.addNode(cfgId, cfgNode);
|
|
9582
10190
|
nodesAdded++;
|
|
@@ -9617,27 +10225,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9617
10225
|
|
|
9618
10226
|
// src/extract/configs.ts
|
|
9619
10227
|
init_cjs_shims();
|
|
9620
|
-
var
|
|
9621
|
-
var
|
|
10228
|
+
var import_node_fs23 = require("fs");
|
|
10229
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
9622
10230
|
var import_types24 = require("@neat.is/types");
|
|
9623
10231
|
async function walkConfigFiles(dir, excludeDirs = []) {
|
|
9624
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10232
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
|
|
9625
10233
|
const out = [];
|
|
9626
|
-
async function
|
|
9627
|
-
const entries = await
|
|
10234
|
+
async function walk10(current) {
|
|
10235
|
+
const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
|
|
9628
10236
|
for (const entry of entries) {
|
|
9629
|
-
const full =
|
|
10237
|
+
const full = import_node_path35.default.join(current, entry.name);
|
|
9630
10238
|
if (entry.isDirectory()) {
|
|
9631
10239
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9632
|
-
if (excluded.has(
|
|
10240
|
+
if (excluded.has(import_node_path35.default.resolve(full))) continue;
|
|
9633
10241
|
if (await isPythonVenvDir(full)) continue;
|
|
9634
|
-
await
|
|
10242
|
+
await walk10(full);
|
|
9635
10243
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
9636
10244
|
out.push(full);
|
|
9637
10245
|
}
|
|
9638
10246
|
}
|
|
9639
10247
|
}
|
|
9640
|
-
await
|
|
10248
|
+
await walk10(dir);
|
|
9641
10249
|
return out;
|
|
9642
10250
|
}
|
|
9643
10251
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -9646,19 +10254,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9646
10254
|
for (const service of services) {
|
|
9647
10255
|
const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
|
|
9648
10256
|
for (const file of configFiles) {
|
|
9649
|
-
const relPath =
|
|
10257
|
+
const relPath = import_node_path35.default.relative(scanPath, file);
|
|
9650
10258
|
const node = {
|
|
9651
10259
|
id: (0, import_types24.configId)(relPath),
|
|
9652
10260
|
type: import_types24.NodeType.ConfigNode,
|
|
9653
|
-
name:
|
|
10261
|
+
name: import_node_path35.default.basename(file),
|
|
9654
10262
|
path: relPath,
|
|
9655
|
-
fileType: isConfigFile(
|
|
10263
|
+
fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
|
|
9656
10264
|
};
|
|
9657
10265
|
if (!graph.hasNode(node.id)) {
|
|
9658
10266
|
graph.addNode(node.id, node);
|
|
9659
10267
|
nodesAdded++;
|
|
9660
10268
|
}
|
|
9661
|
-
const relToService = toPosix(
|
|
10269
|
+
const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
|
|
9662
10270
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9663
10271
|
graph,
|
|
9664
10272
|
service.pkg.name,
|
|
@@ -9674,7 +10282,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9674
10282
|
type: import_types24.EdgeType.CONFIGURED_BY,
|
|
9675
10283
|
provenance: import_types24.Provenance.EXTRACTED,
|
|
9676
10284
|
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
9677
|
-
evidence: { file: relPath.split(
|
|
10285
|
+
evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
|
|
9678
10286
|
};
|
|
9679
10287
|
if (!graph.hasEdge(edge.id)) {
|
|
9680
10288
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -9687,8 +10295,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9687
10295
|
|
|
9688
10296
|
// src/extract/proto.ts
|
|
9689
10297
|
init_cjs_shims();
|
|
9690
|
-
var
|
|
9691
|
-
var
|
|
10298
|
+
var import_node_fs24 = require("fs");
|
|
10299
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
9692
10300
|
var import_types25 = require("@neat.is/types");
|
|
9693
10301
|
var PROTO_EXTENSION = ".proto";
|
|
9694
10302
|
function packageOf(content) {
|
|
@@ -9726,23 +10334,23 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
9726
10334
|
return out;
|
|
9727
10335
|
}
|
|
9728
10336
|
async function walkProtoFiles(dir, excludeDirs = []) {
|
|
9729
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10337
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
|
|
9730
10338
|
const out = [];
|
|
9731
|
-
async function
|
|
9732
|
-
const entries = await
|
|
10339
|
+
async function walk10(current) {
|
|
10340
|
+
const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
9733
10341
|
for (const entry of entries) {
|
|
9734
|
-
const full =
|
|
10342
|
+
const full = import_node_path36.default.join(current, entry.name);
|
|
9735
10343
|
if (entry.isDirectory()) {
|
|
9736
10344
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9737
|
-
if (excluded.has(
|
|
10345
|
+
if (excluded.has(import_node_path36.default.resolve(full))) continue;
|
|
9738
10346
|
if (await isPythonVenvDir(full)) continue;
|
|
9739
|
-
await
|
|
9740
|
-
} else if (entry.isFile() &&
|
|
10347
|
+
await walk10(full);
|
|
10348
|
+
} else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
9741
10349
|
out.push(full);
|
|
9742
10350
|
}
|
|
9743
10351
|
}
|
|
9744
10352
|
}
|
|
9745
|
-
await
|
|
10353
|
+
await walk10(dir);
|
|
9746
10354
|
return out;
|
|
9747
10355
|
}
|
|
9748
10356
|
async function addGrpcMethods(graph, services) {
|
|
@@ -9752,10 +10360,10 @@ async function addGrpcMethods(graph, services) {
|
|
|
9752
10360
|
const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
|
|
9753
10361
|
for (const protoPath of protoPaths) {
|
|
9754
10362
|
if (isTestPath(protoPath)) continue;
|
|
9755
|
-
const relFile = toPosix(
|
|
10363
|
+
const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
|
|
9756
10364
|
let content;
|
|
9757
10365
|
try {
|
|
9758
|
-
content = await
|
|
10366
|
+
content = await import_node_fs24.promises.readFile(protoPath, "utf8");
|
|
9759
10367
|
} catch (err) {
|
|
9760
10368
|
recordExtractionError("proto extraction", protoPath, err);
|
|
9761
10369
|
continue;
|
|
@@ -9810,11 +10418,11 @@ async function addGrpcMethods(graph, services) {
|
|
|
9810
10418
|
|
|
9811
10419
|
// src/extract/calls/index.ts
|
|
9812
10420
|
init_cjs_shims();
|
|
9813
|
-
var
|
|
10421
|
+
var import_types44 = require("@neat.is/types");
|
|
9814
10422
|
|
|
9815
10423
|
// src/extract/calls/http.ts
|
|
9816
10424
|
init_cjs_shims();
|
|
9817
|
-
var
|
|
10425
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
9818
10426
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
9819
10427
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9820
10428
|
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
@@ -9897,7 +10505,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9897
10505
|
const seen = /* @__PURE__ */ new Set();
|
|
9898
10506
|
for (const file of files) {
|
|
9899
10507
|
if (isTestPath(file.path)) continue;
|
|
9900
|
-
const parser = parserForExt(
|
|
10508
|
+
const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
|
|
9901
10509
|
let sites;
|
|
9902
10510
|
try {
|
|
9903
10511
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -9906,7 +10514,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9906
10514
|
continue;
|
|
9907
10515
|
}
|
|
9908
10516
|
if (sites.length === 0) continue;
|
|
9909
|
-
const relFile = toPosix(
|
|
10517
|
+
const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
|
|
9910
10518
|
for (const site of sites) {
|
|
9911
10519
|
const targetId = hostToNodeId.get(site.host);
|
|
9912
10520
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -9960,7 +10568,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9960
10568
|
|
|
9961
10569
|
// src/extract/calls/route-match.ts
|
|
9962
10570
|
init_cjs_shims();
|
|
9963
|
-
var
|
|
10571
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
9964
10572
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
9965
10573
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9966
10574
|
var import_types27 = require("@neat.is/types");
|
|
@@ -10159,7 +10767,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10159
10767
|
const seen = /* @__PURE__ */ new Set();
|
|
10160
10768
|
for (const file of files) {
|
|
10161
10769
|
if (isTestPath(file.path)) continue;
|
|
10162
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
10770
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
|
|
10163
10771
|
let sites;
|
|
10164
10772
|
try {
|
|
10165
10773
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -10168,7 +10776,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10168
10776
|
continue;
|
|
10169
10777
|
}
|
|
10170
10778
|
if (sites.length === 0) continue;
|
|
10171
|
-
const relFile = toPosix(
|
|
10779
|
+
const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
|
|
10172
10780
|
for (const site of sites) {
|
|
10173
10781
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
10174
10782
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -10229,7 +10837,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10229
10837
|
|
|
10230
10838
|
// src/extract/calls/kafka.ts
|
|
10231
10839
|
init_cjs_shims();
|
|
10232
|
-
var
|
|
10840
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
10233
10841
|
var import_types28 = require("@neat.is/types");
|
|
10234
10842
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10235
10843
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -10317,13 +10925,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10317
10925
|
// call sites — verified-call-site tier (ADR-066).
|
|
10318
10926
|
confidenceKind: "verified-call-site",
|
|
10319
10927
|
evidence: {
|
|
10320
|
-
file:
|
|
10928
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
10321
10929
|
line,
|
|
10322
10930
|
snippet: snippet(file.content, line)
|
|
10323
10931
|
}
|
|
10324
10932
|
});
|
|
10325
10933
|
};
|
|
10326
|
-
if (
|
|
10934
|
+
if (import_node_path39.default.extname(file.path) === ".go") {
|
|
10327
10935
|
goSaramaEndpoints(file.content, make);
|
|
10328
10936
|
} else {
|
|
10329
10937
|
for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
|
|
@@ -10334,7 +10942,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10334
10942
|
|
|
10335
10943
|
// src/extract/calls/redis.ts
|
|
10336
10944
|
init_cjs_shims();
|
|
10337
|
-
var
|
|
10945
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
10338
10946
|
var import_types29 = require("@neat.is/types");
|
|
10339
10947
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
10340
10948
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -10357,7 +10965,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10357
10965
|
// support tier (ADR-066).
|
|
10358
10966
|
confidenceKind: "url-with-structural-support",
|
|
10359
10967
|
evidence: {
|
|
10360
|
-
file:
|
|
10968
|
+
file: import_node_path40.default.relative(serviceDir, file.path),
|
|
10361
10969
|
line,
|
|
10362
10970
|
snippet: snippet(file.content, line)
|
|
10363
10971
|
}
|
|
@@ -10368,7 +10976,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10368
10976
|
|
|
10369
10977
|
// src/extract/calls/aws.ts
|
|
10370
10978
|
init_cjs_shims();
|
|
10371
|
-
var
|
|
10979
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
10372
10980
|
var import_types30 = require("@neat.is/types");
|
|
10373
10981
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10374
10982
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -10402,7 +11010,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10402
11010
|
// (ADR-066).
|
|
10403
11011
|
confidenceKind: "verified-call-site",
|
|
10404
11012
|
evidence: {
|
|
10405
|
-
file:
|
|
11013
|
+
file: import_node_path41.default.relative(serviceDir, file.path),
|
|
10406
11014
|
line,
|
|
10407
11015
|
snippet: snippet(file.content, line)
|
|
10408
11016
|
}
|
|
@@ -10427,7 +11035,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10427
11035
|
|
|
10428
11036
|
// src/extract/calls/grpc.ts
|
|
10429
11037
|
init_cjs_shims();
|
|
10430
|
-
var
|
|
11038
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
10431
11039
|
var import_types31 = require("@neat.is/types");
|
|
10432
11040
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
10433
11041
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -10486,7 +11094,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10486
11094
|
// tier (ADR-066).
|
|
10487
11095
|
confidenceKind: "verified-call-site",
|
|
10488
11096
|
evidence: {
|
|
10489
|
-
file:
|
|
11097
|
+
file: import_node_path42.default.relative(serviceDir, file.path),
|
|
10490
11098
|
line,
|
|
10491
11099
|
snippet: snippet(file.content, line)
|
|
10492
11100
|
}
|
|
@@ -10497,7 +11105,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10497
11105
|
|
|
10498
11106
|
// src/extract/calls/supabase.ts
|
|
10499
11107
|
init_cjs_shims();
|
|
10500
|
-
var
|
|
11108
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
10501
11109
|
var import_types32 = require("@neat.is/types");
|
|
10502
11110
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
10503
11111
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -10556,7 +11164,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10556
11164
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
10557
11165
|
confidenceKind: "verified-call-site",
|
|
10558
11166
|
evidence: {
|
|
10559
|
-
file:
|
|
11167
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10560
11168
|
line,
|
|
10561
11169
|
snippet: snippet(file.content, line)
|
|
10562
11170
|
}
|
|
@@ -10583,7 +11191,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10583
11191
|
edgeType: "CALLS",
|
|
10584
11192
|
confidenceKind: "verified-call-site",
|
|
10585
11193
|
evidence: {
|
|
10586
|
-
file:
|
|
11194
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10587
11195
|
line,
|
|
10588
11196
|
snippet: snippet(file.content, line)
|
|
10589
11197
|
}
|
|
@@ -10595,7 +11203,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10595
11203
|
|
|
10596
11204
|
// src/extract/calls/firestore.ts
|
|
10597
11205
|
init_cjs_shims();
|
|
10598
|
-
var
|
|
11206
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
10599
11207
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
10600
11208
|
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
10601
11209
|
var import_types33 = require("@neat.is/types");
|
|
@@ -10634,7 +11242,7 @@ function isFirestoreClientFactory(node) {
|
|
|
10634
11242
|
}
|
|
10635
11243
|
function firestoreClientVars(root) {
|
|
10636
11244
|
const vars = /* @__PURE__ */ new Set();
|
|
10637
|
-
const
|
|
11245
|
+
const walk10 = (node) => {
|
|
10638
11246
|
if (node.type === "variable_declarator") {
|
|
10639
11247
|
const name = node.childForFieldName("name");
|
|
10640
11248
|
let value = node.childForFieldName("value");
|
|
@@ -10643,9 +11251,9 @@ function firestoreClientVars(root) {
|
|
|
10643
11251
|
vars.add(name.text);
|
|
10644
11252
|
}
|
|
10645
11253
|
}
|
|
10646
|
-
for (const c of namedChildren(node))
|
|
11254
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10647
11255
|
};
|
|
10648
|
-
|
|
11256
|
+
walk10(root);
|
|
10649
11257
|
return vars;
|
|
10650
11258
|
}
|
|
10651
11259
|
function isClientExpr(node, clientVars) {
|
|
@@ -10766,7 +11374,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10766
11374
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
10767
11375
|
if (!hasClient && !hasAdmin) return [];
|
|
10768
11376
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
10769
|
-
const tree = parseSource3(parserForExt2(
|
|
11377
|
+
const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
|
|
10770
11378
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
10771
11379
|
const collLine = /* @__PURE__ */ new Map();
|
|
10772
11380
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -10800,7 +11408,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10800
11408
|
}
|
|
10801
11409
|
s.add(field);
|
|
10802
11410
|
};
|
|
10803
|
-
const
|
|
11411
|
+
const walk10 = (node) => {
|
|
10804
11412
|
if (node.type === "call_expression") {
|
|
10805
11413
|
const fn = node.childForFieldName("function");
|
|
10806
11414
|
const line = node.startPosition.row + 1;
|
|
@@ -10840,9 +11448,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10840
11448
|
}
|
|
10841
11449
|
}
|
|
10842
11450
|
}
|
|
10843
|
-
for (const c of namedChildren(node))
|
|
11451
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10844
11452
|
};
|
|
10845
|
-
|
|
11453
|
+
walk10(tree.rootNode);
|
|
10846
11454
|
const out = [];
|
|
10847
11455
|
for (const [collPath, line] of collLine) {
|
|
10848
11456
|
const byField = writes.get(collPath);
|
|
@@ -10870,7 +11478,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10870
11478
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
10871
11479
|
...sdkWrites ? { sdkWrites } : {},
|
|
10872
11480
|
evidence: {
|
|
10873
|
-
file:
|
|
11481
|
+
file: import_node_path44.default.relative(serviceDir, file.path),
|
|
10874
11482
|
line,
|
|
10875
11483
|
snippet: snippet(file.content, line)
|
|
10876
11484
|
}
|
|
@@ -10881,7 +11489,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10881
11489
|
|
|
10882
11490
|
// src/extract/calls/mongoose.ts
|
|
10883
11491
|
init_cjs_shims();
|
|
10884
|
-
var
|
|
11492
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
10885
11493
|
var import_types34 = require("@neat.is/types");
|
|
10886
11494
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
10887
11495
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -11032,7 +11640,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
11032
11640
|
kind: r.kind,
|
|
11033
11641
|
edgeType: "CALLS",
|
|
11034
11642
|
confidenceKind: "verified-call-site",
|
|
11035
|
-
evidence: { file:
|
|
11643
|
+
evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11036
11644
|
};
|
|
11037
11645
|
}
|
|
11038
11646
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -11122,7 +11730,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11122
11730
|
const registry = /* @__PURE__ */ new Map();
|
|
11123
11731
|
for (const f of mongooseFiles) {
|
|
11124
11732
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
11125
|
-
if (fx) registry.set(toPosix(
|
|
11733
|
+
if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
|
|
11126
11734
|
}
|
|
11127
11735
|
if (registry.size === 0) return [];
|
|
11128
11736
|
const out = [];
|
|
@@ -11133,7 +11741,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11133
11741
|
const directColl = /* @__PURE__ */ new Map();
|
|
11134
11742
|
const nsExports = /* @__PURE__ */ new Map();
|
|
11135
11743
|
for (const b of bindings) {
|
|
11136
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
11744
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
|
|
11137
11745
|
if (!resolvedRel) continue;
|
|
11138
11746
|
const fx = registry.get(resolvedRel);
|
|
11139
11747
|
if (!fx) continue;
|
|
@@ -11177,7 +11785,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11177
11785
|
|
|
11178
11786
|
// src/extract/calls/sqlalchemy.ts
|
|
11179
11787
|
init_cjs_shims();
|
|
11180
|
-
var
|
|
11788
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
11181
11789
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
11182
11790
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
11183
11791
|
var import_types35 = require("@neat.is/types");
|
|
@@ -11316,7 +11924,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
11316
11924
|
childTable,
|
|
11317
11925
|
parentTable,
|
|
11318
11926
|
evidence: {
|
|
11319
|
-
file:
|
|
11927
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11320
11928
|
line,
|
|
11321
11929
|
snippet: snippet(file.content, line)
|
|
11322
11930
|
}
|
|
@@ -11341,7 +11949,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
11341
11949
|
confidenceKind: "verified-call-site",
|
|
11342
11950
|
...columns && columns.length > 0 ? { columns } : {},
|
|
11343
11951
|
evidence: {
|
|
11344
|
-
file:
|
|
11952
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11345
11953
|
line,
|
|
11346
11954
|
snippet: snippet(file.content, line)
|
|
11347
11955
|
}
|
|
@@ -11448,7 +12056,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11448
12056
|
edgeType: "CALLS",
|
|
11449
12057
|
confidenceKind: "verified-call-site",
|
|
11450
12058
|
evidence: {
|
|
11451
|
-
file:
|
|
12059
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11452
12060
|
line,
|
|
11453
12061
|
snippet: snippet(file.content, line)
|
|
11454
12062
|
}
|
|
@@ -11460,7 +12068,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11460
12068
|
|
|
11461
12069
|
// src/extract/calls/django-orm.ts
|
|
11462
12070
|
init_cjs_shims();
|
|
11463
|
-
var
|
|
12071
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
11464
12072
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
11465
12073
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
11466
12074
|
var import_types36 = require("@neat.is/types");
|
|
@@ -11530,7 +12138,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11530
12138
|
const tree = parseSource7(makePyParser4(), file.content);
|
|
11531
12139
|
const out = [];
|
|
11532
12140
|
const seen = /* @__PURE__ */ new Set();
|
|
11533
|
-
const defaultAppLabel =
|
|
12141
|
+
const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
|
|
11534
12142
|
walk4(tree.rootNode, (node) => {
|
|
11535
12143
|
if (node.type !== "class_definition") return;
|
|
11536
12144
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -11548,7 +12156,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11548
12156
|
kind: "sql-table",
|
|
11549
12157
|
edgeType: "CALLS",
|
|
11550
12158
|
confidenceKind: "verified-call-site",
|
|
11551
|
-
evidence: { file:
|
|
12159
|
+
evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11552
12160
|
});
|
|
11553
12161
|
});
|
|
11554
12162
|
return out;
|
|
@@ -11556,7 +12164,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11556
12164
|
|
|
11557
12165
|
// src/extract/calls/drizzle.ts
|
|
11558
12166
|
init_cjs_shims();
|
|
11559
|
-
var
|
|
12167
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
11560
12168
|
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
11561
12169
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
11562
12170
|
var import_types37 = require("@neat.is/types");
|
|
@@ -11634,10 +12242,10 @@ function columnsFromObject(obj) {
|
|
|
11634
12242
|
}
|
|
11635
12243
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
11636
12244
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11637
|
-
const tree = parseSource3(parserForExt3(
|
|
12245
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11638
12246
|
const out = [];
|
|
11639
12247
|
const seen = /* @__PURE__ */ new Set();
|
|
11640
|
-
const
|
|
12248
|
+
const walk10 = (node) => {
|
|
11641
12249
|
if (node.type === "call_expression") {
|
|
11642
12250
|
const fn = node.childForFieldName("function");
|
|
11643
12251
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11657,7 +12265,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11657
12265
|
confidenceKind: "structural",
|
|
11658
12266
|
columns,
|
|
11659
12267
|
evidence: {
|
|
11660
|
-
file:
|
|
12268
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11661
12269
|
line,
|
|
11662
12270
|
snippet: snippet(file.content, line)
|
|
11663
12271
|
}
|
|
@@ -11665,9 +12273,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11665
12273
|
}
|
|
11666
12274
|
}
|
|
11667
12275
|
}
|
|
11668
|
-
for (const c of namedChildren4(node))
|
|
12276
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11669
12277
|
};
|
|
11670
|
-
|
|
12278
|
+
walk10(tree.rootNode);
|
|
11671
12279
|
return out;
|
|
11672
12280
|
}
|
|
11673
12281
|
function enclosingVarName(call) {
|
|
@@ -11689,7 +12297,7 @@ function enclosingVarName(call) {
|
|
|
11689
12297
|
function collectDrizzleTables(root) {
|
|
11690
12298
|
const tables = [];
|
|
11691
12299
|
const varToTable = /* @__PURE__ */ new Map();
|
|
11692
|
-
const
|
|
12300
|
+
const walk10 = (node) => {
|
|
11693
12301
|
if (node.type === "call_expression") {
|
|
11694
12302
|
const fn = node.childForFieldName("function");
|
|
11695
12303
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11704,9 +12312,9 @@ function collectDrizzleTables(root) {
|
|
|
11704
12312
|
}
|
|
11705
12313
|
}
|
|
11706
12314
|
}
|
|
11707
|
-
for (const c of namedChildren4(node))
|
|
12315
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11708
12316
|
};
|
|
11709
|
-
|
|
12317
|
+
walk10(root);
|
|
11710
12318
|
return { tables, varToTable };
|
|
11711
12319
|
}
|
|
11712
12320
|
function referencesTargetVar(call) {
|
|
@@ -11723,13 +12331,13 @@ function referencesTargetVar(call) {
|
|
|
11723
12331
|
}
|
|
11724
12332
|
function drizzleForeignKeys(file, serviceDir) {
|
|
11725
12333
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11726
|
-
const tree = parseSource3(parserForExt3(
|
|
12334
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11727
12335
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
11728
12336
|
const out = [];
|
|
11729
12337
|
const seen = /* @__PURE__ */ new Set();
|
|
11730
12338
|
for (const table of tables) {
|
|
11731
12339
|
if (!table.object) continue;
|
|
11732
|
-
const
|
|
12340
|
+
const walk10 = (node) => {
|
|
11733
12341
|
if (node.type === "call_expression") {
|
|
11734
12342
|
const targetVar = referencesTargetVar(node);
|
|
11735
12343
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -11742,7 +12350,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11742
12350
|
childTable: table.tableName,
|
|
11743
12351
|
parentTable,
|
|
11744
12352
|
evidence: {
|
|
11745
|
-
file:
|
|
12353
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11746
12354
|
line,
|
|
11747
12355
|
snippet: snippet(file.content, line)
|
|
11748
12356
|
}
|
|
@@ -11750,16 +12358,16 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11750
12358
|
}
|
|
11751
12359
|
}
|
|
11752
12360
|
}
|
|
11753
|
-
for (const c of namedChildren4(node))
|
|
12361
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11754
12362
|
};
|
|
11755
|
-
|
|
12363
|
+
walk10(table.object);
|
|
11756
12364
|
}
|
|
11757
12365
|
return out;
|
|
11758
12366
|
}
|
|
11759
12367
|
|
|
11760
12368
|
// src/extract/calls/prisma.ts
|
|
11761
12369
|
init_cjs_shims();
|
|
11762
|
-
var
|
|
12370
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
11763
12371
|
var import_types38 = require("@neat.is/types");
|
|
11764
12372
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
11765
12373
|
"Int",
|
|
@@ -11815,7 +12423,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11815
12423
|
confidenceKind: "structural",
|
|
11816
12424
|
columns: b.columns,
|
|
11817
12425
|
evidence: {
|
|
11818
|
-
file:
|
|
12426
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11819
12427
|
line: b.startLine,
|
|
11820
12428
|
snippet: snippet(content, b.startLine)
|
|
11821
12429
|
}
|
|
@@ -11876,7 +12484,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11876
12484
|
}
|
|
11877
12485
|
async function prismaColumnEndpoints(serviceDir) {
|
|
11878
12486
|
const schemaPath = await findFirst(serviceDir, [
|
|
11879
|
-
|
|
12487
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11880
12488
|
"schema.prisma"
|
|
11881
12489
|
]);
|
|
11882
12490
|
if (!schemaPath) return [];
|
|
@@ -11951,7 +12559,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11951
12559
|
childTable: current.table,
|
|
11952
12560
|
parentTable,
|
|
11953
12561
|
evidence: {
|
|
11954
|
-
file:
|
|
12562
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11955
12563
|
line: lineNo,
|
|
11956
12564
|
snippet: snippet(content, lineNo)
|
|
11957
12565
|
}
|
|
@@ -11966,7 +12574,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11966
12574
|
}
|
|
11967
12575
|
async function prismaForeignKeys(serviceDir) {
|
|
11968
12576
|
const schemaPath = await findFirst(serviceDir, [
|
|
11969
|
-
|
|
12577
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11970
12578
|
"schema.prisma"
|
|
11971
12579
|
]);
|
|
11972
12580
|
if (!schemaPath) return [];
|
|
@@ -11977,7 +12585,7 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
11977
12585
|
|
|
11978
12586
|
// src/extract/calls/activerecord.ts
|
|
11979
12587
|
init_cjs_shims();
|
|
11980
|
-
var
|
|
12588
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11981
12589
|
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
11982
12590
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
11983
12591
|
var import_types39 = require("@neat.is/types");
|
|
@@ -12185,7 +12793,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
12185
12793
|
confidenceKind: "structural",
|
|
12186
12794
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
12187
12795
|
evidence: {
|
|
12188
|
-
file:
|
|
12796
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12189
12797
|
line: table.line,
|
|
12190
12798
|
snippet: snippet(file.content, table.line)
|
|
12191
12799
|
}
|
|
@@ -12207,7 +12815,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
12207
12815
|
childTable,
|
|
12208
12816
|
parentTable,
|
|
12209
12817
|
evidence: {
|
|
12210
|
-
file:
|
|
12818
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12211
12819
|
line,
|
|
12212
12820
|
snippet: snippet(file.content, line)
|
|
12213
12821
|
}
|
|
@@ -12290,7 +12898,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
12290
12898
|
edgeType: "CALLS",
|
|
12291
12899
|
confidenceKind: "verified-call-site",
|
|
12292
12900
|
evidence: {
|
|
12293
|
-
file:
|
|
12901
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12294
12902
|
line,
|
|
12295
12903
|
snippet: snippet(file.content, line)
|
|
12296
12904
|
}
|
|
@@ -12327,7 +12935,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12327
12935
|
childTable,
|
|
12328
12936
|
parentTable,
|
|
12329
12937
|
evidence: {
|
|
12330
|
-
file:
|
|
12938
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12331
12939
|
line,
|
|
12332
12940
|
snippet: snippet(file.content, line)
|
|
12333
12941
|
}
|
|
@@ -12339,7 +12947,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12339
12947
|
|
|
12340
12948
|
// src/extract/calls/eloquent.ts
|
|
12341
12949
|
init_cjs_shims();
|
|
12342
|
-
var
|
|
12950
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12343
12951
|
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
12344
12952
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
12345
12953
|
var import_types40 = require("@neat.is/types");
|
|
@@ -12596,7 +13204,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
12596
13204
|
confidenceKind: "structural",
|
|
12597
13205
|
...columns.length > 0 ? { columns } : {},
|
|
12598
13206
|
evidence: {
|
|
12599
|
-
file:
|
|
13207
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12600
13208
|
line: bp.line,
|
|
12601
13209
|
snippet: snippet(file.content, bp.line)
|
|
12602
13210
|
}
|
|
@@ -12618,7 +13226,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
12618
13226
|
childTable,
|
|
12619
13227
|
parentTable,
|
|
12620
13228
|
evidence: {
|
|
12621
|
-
file:
|
|
13229
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12622
13230
|
line,
|
|
12623
13231
|
snippet: snippet(file.content, line)
|
|
12624
13232
|
}
|
|
@@ -12738,7 +13346,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
12738
13346
|
edgeType: "CALLS",
|
|
12739
13347
|
confidenceKind: "verified-call-site",
|
|
12740
13348
|
evidence: {
|
|
12741
|
-
file:
|
|
13349
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12742
13350
|
line,
|
|
12743
13351
|
snippet: snippet(file.content, line)
|
|
12744
13352
|
}
|
|
@@ -12774,7 +13382,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12774
13382
|
childTable,
|
|
12775
13383
|
parentTable,
|
|
12776
13384
|
evidence: {
|
|
12777
|
-
file:
|
|
13385
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12778
13386
|
line,
|
|
12779
13387
|
snippet: snippet(file.content, line)
|
|
12780
13388
|
}
|
|
@@ -12786,7 +13394,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12786
13394
|
|
|
12787
13395
|
// src/extract/calls/go.ts
|
|
12788
13396
|
init_cjs_shims();
|
|
12789
|
-
var
|
|
13397
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12790
13398
|
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
12791
13399
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
12792
13400
|
var import_types41 = require("@neat.is/types");
|
|
@@ -12861,7 +13469,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
12861
13469
|
return null;
|
|
12862
13470
|
}
|
|
12863
13471
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
12864
|
-
if (
|
|
13472
|
+
if (import_node_path54.default.extname(file.path) !== ".go") return [];
|
|
12865
13473
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
12866
13474
|
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12867
13475
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -12890,7 +13498,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12890
13498
|
confidenceKind: "verified-call-site",
|
|
12891
13499
|
...columns.length > 0 ? { columns } : {},
|
|
12892
13500
|
evidence: {
|
|
12893
|
-
file: toPosix(
|
|
13501
|
+
file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
|
|
12894
13502
|
line,
|
|
12895
13503
|
snippet: snippet(file.content, line)
|
|
12896
13504
|
}
|
|
@@ -12901,7 +13509,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12901
13509
|
|
|
12902
13510
|
// src/extract/calls/gorm.ts
|
|
12903
13511
|
init_cjs_shims();
|
|
12904
|
-
var
|
|
13512
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12905
13513
|
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
12906
13514
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
12907
13515
|
var import_types42 = require("@neat.is/types");
|
|
@@ -13334,7 +13942,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
13334
13942
|
seen.delete(struct.name);
|
|
13335
13943
|
}
|
|
13336
13944
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
13337
|
-
if (
|
|
13945
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13338
13946
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13339
13947
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13340
13948
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13356,7 +13964,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13356
13964
|
confidenceKind: "structural",
|
|
13357
13965
|
...columns.length > 0 ? { columns } : {},
|
|
13358
13966
|
evidence: {
|
|
13359
|
-
file: toPosix(
|
|
13967
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13360
13968
|
line: struct.line,
|
|
13361
13969
|
snippet: snippet(file.content, struct.line)
|
|
13362
13970
|
}
|
|
@@ -13365,7 +13973,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13365
13973
|
return out;
|
|
13366
13974
|
}
|
|
13367
13975
|
function gormForeignKeys(file, serviceDir) {
|
|
13368
|
-
if (
|
|
13976
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13369
13977
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13370
13978
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13371
13979
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13380,7 +13988,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13380
13988
|
childTable,
|
|
13381
13989
|
parentTable,
|
|
13382
13990
|
evidence: {
|
|
13383
|
-
file: toPosix(
|
|
13991
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13384
13992
|
line,
|
|
13385
13993
|
snippet: snippet(file.content, line)
|
|
13386
13994
|
}
|
|
@@ -13415,15 +14023,131 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13415
14023
|
return out;
|
|
13416
14024
|
}
|
|
13417
14025
|
|
|
14026
|
+
// src/extract/calls/efcore.ts
|
|
14027
|
+
init_cjs_shims();
|
|
14028
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
14029
|
+
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
14030
|
+
var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
14031
|
+
var import_types43 = require("@neat.is/types");
|
|
14032
|
+
var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
|
|
14033
|
+
var PARSE_CHUNK12 = 16384;
|
|
14034
|
+
function makeCsParser() {
|
|
14035
|
+
const p = new import_tree_sitter16.default();
|
|
14036
|
+
p.setLanguage(import_tree_sitter_c_sharp2.default);
|
|
14037
|
+
return p;
|
|
14038
|
+
}
|
|
14039
|
+
function parseSource12(parser, source) {
|
|
14040
|
+
return parser.parse(
|
|
14041
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
|
|
14042
|
+
);
|
|
14043
|
+
}
|
|
14044
|
+
function walk9(node, visit) {
|
|
14045
|
+
visit(node);
|
|
14046
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14047
|
+
const c = node.namedChild(i);
|
|
14048
|
+
if (c) walk9(c, visit);
|
|
14049
|
+
}
|
|
14050
|
+
}
|
|
14051
|
+
function firstChildOfType(node, type) {
|
|
14052
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14053
|
+
const c = node.namedChild(i);
|
|
14054
|
+
if (c?.type === type) return c;
|
|
14055
|
+
}
|
|
14056
|
+
return null;
|
|
14057
|
+
}
|
|
14058
|
+
function csStringLiteral(node) {
|
|
14059
|
+
if (node.type === "string_literal") {
|
|
14060
|
+
let out = "";
|
|
14061
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14062
|
+
const c = node.namedChild(i);
|
|
14063
|
+
if (c?.type === "string_literal_content") out += c.text;
|
|
14064
|
+
}
|
|
14065
|
+
return out;
|
|
14066
|
+
}
|
|
14067
|
+
if (node.type === "verbatim_string_literal") {
|
|
14068
|
+
const t = node.text;
|
|
14069
|
+
return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
|
|
14070
|
+
}
|
|
14071
|
+
return null;
|
|
14072
|
+
}
|
|
14073
|
+
function attributeName(attr) {
|
|
14074
|
+
const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
|
|
14075
|
+
if (!nameNode) return null;
|
|
14076
|
+
const text = nameNode.text;
|
|
14077
|
+
const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
|
|
14078
|
+
return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
|
|
14079
|
+
}
|
|
14080
|
+
function tableFromAttribute(attr) {
|
|
14081
|
+
if (attributeName(attr) !== "Table") return null;
|
|
14082
|
+
const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
|
|
14083
|
+
if (!args) return null;
|
|
14084
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
14085
|
+
const arg = args.namedChild(i);
|
|
14086
|
+
if (arg?.type !== "attribute_argument") continue;
|
|
14087
|
+
const first = arg.namedChild(0);
|
|
14088
|
+
if (!first) continue;
|
|
14089
|
+
const value = csStringLiteral(first);
|
|
14090
|
+
if (value !== null) return value;
|
|
14091
|
+
return null;
|
|
14092
|
+
}
|
|
14093
|
+
return null;
|
|
14094
|
+
}
|
|
14095
|
+
function tableFromToTable(call) {
|
|
14096
|
+
const fn = call.childForFieldName("function");
|
|
14097
|
+
if (fn?.type !== "member_access_expression") return null;
|
|
14098
|
+
const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
|
|
14099
|
+
if (method?.text !== "ToTable") return null;
|
|
14100
|
+
const args = call.childForFieldName("arguments");
|
|
14101
|
+
const firstArg2 = args?.namedChild(0);
|
|
14102
|
+
if (firstArg2?.type !== "argument") return null;
|
|
14103
|
+
const value = firstArg2.namedChild(0);
|
|
14104
|
+
return value ? csStringLiteral(value) : null;
|
|
14105
|
+
}
|
|
14106
|
+
function efcoreEndpointsFromFile(file, serviceDir) {
|
|
14107
|
+
if (import_node_path56.default.extname(file.path) !== ".cs") return [];
|
|
14108
|
+
if (!EFCORE_GATE.test(file.content)) return [];
|
|
14109
|
+
const tree = parseSource12(makeCsParser(), file.content);
|
|
14110
|
+
const out = [];
|
|
14111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14112
|
+
const push = (name, line) => {
|
|
14113
|
+
if (!name || seen.has(name)) return;
|
|
14114
|
+
seen.add(name);
|
|
14115
|
+
out.push({
|
|
14116
|
+
infraId: (0, import_types43.infraId)("sql-table", name),
|
|
14117
|
+
name,
|
|
14118
|
+
kind: "sql-table",
|
|
14119
|
+
edgeType: "CALLS",
|
|
14120
|
+
confidenceKind: "structural",
|
|
14121
|
+
evidence: {
|
|
14122
|
+
file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
|
|
14123
|
+
line,
|
|
14124
|
+
snippet: snippet(file.content, line)
|
|
14125
|
+
}
|
|
14126
|
+
});
|
|
14127
|
+
};
|
|
14128
|
+
walk9(tree.rootNode, (node) => {
|
|
14129
|
+
if (node.type === "attribute") {
|
|
14130
|
+
const table = tableFromAttribute(node);
|
|
14131
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14132
|
+
return;
|
|
14133
|
+
}
|
|
14134
|
+
if (node.type === "invocation_expression") {
|
|
14135
|
+
const table = tableFromToTable(node);
|
|
14136
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14137
|
+
}
|
|
14138
|
+
});
|
|
14139
|
+
return out;
|
|
14140
|
+
}
|
|
14141
|
+
|
|
13418
14142
|
// src/extract/calls/index.ts
|
|
13419
14143
|
function edgeTypeFromEndpoint(ep) {
|
|
13420
14144
|
switch (ep.edgeType) {
|
|
13421
14145
|
case "PUBLISHES_TO":
|
|
13422
|
-
return
|
|
14146
|
+
return import_types44.EdgeType.PUBLISHES_TO;
|
|
13423
14147
|
case "CONSUMES_FROM":
|
|
13424
|
-
return
|
|
14148
|
+
return import_types44.EdgeType.CONSUMES_FROM;
|
|
13425
14149
|
default:
|
|
13426
|
-
return
|
|
14150
|
+
return import_types44.EdgeType.CALLS;
|
|
13427
14151
|
}
|
|
13428
14152
|
}
|
|
13429
14153
|
function isAwsKind(kind) {
|
|
@@ -13473,6 +14197,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13473
14197
|
} catch (err) {
|
|
13474
14198
|
recordExtractionError("laravel eloquent extraction", file.path, err);
|
|
13475
14199
|
}
|
|
14200
|
+
try {
|
|
14201
|
+
endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
|
|
14202
|
+
} catch (err) {
|
|
14203
|
+
recordExtractionError("efcore data-axis extraction", file.path, err);
|
|
14204
|
+
}
|
|
13476
14205
|
}
|
|
13477
14206
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
13478
14207
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
@@ -13483,7 +14212,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13483
14212
|
if (!graph.hasNode(ep.infraId)) {
|
|
13484
14213
|
const node = {
|
|
13485
14214
|
id: ep.infraId,
|
|
13486
|
-
type:
|
|
14215
|
+
type: import_types44.NodeType.InfraNode,
|
|
13487
14216
|
name: ep.name,
|
|
13488
14217
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
13489
14218
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -13496,21 +14225,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13496
14225
|
}
|
|
13497
14226
|
if (ep.columns && ep.columns.length > 0) {
|
|
13498
14227
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13499
|
-
if (node.type ===
|
|
14228
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13500
14229
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13501
14230
|
...node,
|
|
13502
14231
|
columns: foldColumns(
|
|
13503
14232
|
node.columns,
|
|
13504
14233
|
ep.columns,
|
|
13505
|
-
|
|
13506
|
-
(0,
|
|
14234
|
+
import_types44.Provenance.EXTRACTED,
|
|
14235
|
+
(0, import_types44.confidenceForExtracted)(ep.confidenceKind)
|
|
13507
14236
|
)
|
|
13508
14237
|
});
|
|
13509
14238
|
}
|
|
13510
14239
|
}
|
|
13511
14240
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
13512
14241
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13513
|
-
if (node.type ===
|
|
14242
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13514
14243
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13515
14244
|
...node,
|
|
13516
14245
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -13518,7 +14247,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13518
14247
|
}
|
|
13519
14248
|
}
|
|
13520
14249
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
13521
|
-
const confidence = (0,
|
|
14250
|
+
const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
|
|
13522
14251
|
const relFile = toPosix(ep.evidence.file);
|
|
13523
14252
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
13524
14253
|
graph,
|
|
@@ -13528,7 +14257,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13528
14257
|
);
|
|
13529
14258
|
nodesAdded += n;
|
|
13530
14259
|
edgesAdded += e;
|
|
13531
|
-
if (!(0,
|
|
14260
|
+
if (!(0, import_types44.passesExtractedFloor)(confidence)) {
|
|
13532
14261
|
noteExtractedDropped({
|
|
13533
14262
|
source: fileNodeId,
|
|
13534
14263
|
target: ep.infraId,
|
|
@@ -13548,7 +14277,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13548
14277
|
source: fileNodeId,
|
|
13549
14278
|
target: ep.infraId,
|
|
13550
14279
|
type: edgeType,
|
|
13551
|
-
provenance:
|
|
14280
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
13552
14281
|
confidence,
|
|
13553
14282
|
evidence: ep.evidence
|
|
13554
14283
|
};
|
|
@@ -13571,7 +14300,7 @@ async function addCallEdges(graph, services) {
|
|
|
13571
14300
|
|
|
13572
14301
|
// src/extract/table-edges.ts
|
|
13573
14302
|
init_cjs_shims();
|
|
13574
|
-
var
|
|
14303
|
+
var import_types45 = require("@neat.is/types");
|
|
13575
14304
|
async function addTableEdges(graph, services) {
|
|
13576
14305
|
let nodesAdded = 0;
|
|
13577
14306
|
let edgesAdded = 0;
|
|
@@ -13599,20 +14328,20 @@ async function addTableEdges(graph, services) {
|
|
|
13599
14328
|
}
|
|
13600
14329
|
refs.push(...modelRefs);
|
|
13601
14330
|
for (const ref of refs) {
|
|
13602
|
-
const childId = (0,
|
|
13603
|
-
const parentId = (0,
|
|
14331
|
+
const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
|
|
14332
|
+
const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
|
|
13604
14333
|
if (childId === parentId) continue;
|
|
13605
14334
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
13606
14335
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
13607
|
-
const edgeId = (0,
|
|
14336
|
+
const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
|
|
13608
14337
|
if (graph.hasEdge(edgeId)) continue;
|
|
13609
14338
|
const edge = {
|
|
13610
14339
|
id: edgeId,
|
|
13611
14340
|
source: childId,
|
|
13612
14341
|
target: parentId,
|
|
13613
|
-
type:
|
|
13614
|
-
provenance:
|
|
13615
|
-
confidence: (0,
|
|
14342
|
+
type: import_types45.EdgeType.REFERENCES,
|
|
14343
|
+
provenance: import_types45.Provenance.EXTRACTED,
|
|
14344
|
+
confidence: (0, import_types45.confidenceForExtracted)("structural"),
|
|
13616
14345
|
evidence: ref.evidence
|
|
13617
14346
|
};
|
|
13618
14347
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -13625,7 +14354,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
13625
14354
|
if (graph.hasNode(id)) return 0;
|
|
13626
14355
|
const node = {
|
|
13627
14356
|
id,
|
|
13628
|
-
type:
|
|
14357
|
+
type: import_types45.NodeType.InfraNode,
|
|
13629
14358
|
name,
|
|
13630
14359
|
provider: "self",
|
|
13631
14360
|
kind: "sql-table"
|
|
@@ -13639,16 +14368,16 @@ init_cjs_shims();
|
|
|
13639
14368
|
|
|
13640
14369
|
// src/extract/infra/docker-compose.ts
|
|
13641
14370
|
init_cjs_shims();
|
|
13642
|
-
var
|
|
13643
|
-
var
|
|
14371
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
14372
|
+
var import_types47 = require("@neat.is/types");
|
|
13644
14373
|
|
|
13645
14374
|
// src/extract/infra/shared.ts
|
|
13646
14375
|
init_cjs_shims();
|
|
13647
|
-
var
|
|
14376
|
+
var import_types46 = require("@neat.is/types");
|
|
13648
14377
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
13649
14378
|
return {
|
|
13650
|
-
id: (0,
|
|
13651
|
-
type:
|
|
14379
|
+
id: (0, import_types46.infraId)(kind, name),
|
|
14380
|
+
type: import_types46.NodeType.InfraNode,
|
|
13652
14381
|
name,
|
|
13653
14382
|
provider,
|
|
13654
14383
|
kind,
|
|
@@ -13692,8 +14421,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
13692
14421
|
source: anchorId,
|
|
13693
14422
|
target: node.id,
|
|
13694
14423
|
type: edgeType,
|
|
13695
|
-
provenance:
|
|
13696
|
-
confidence: (0,
|
|
14424
|
+
provenance: import_types46.Provenance.EXTRACTED,
|
|
14425
|
+
confidence: (0, import_types46.confidenceForExtracted)("structural"),
|
|
13697
14426
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
13698
14427
|
};
|
|
13699
14428
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13710,7 +14439,7 @@ function dependsOnList(value) {
|
|
|
13710
14439
|
}
|
|
13711
14440
|
function serviceNameToServiceNode(name, services) {
|
|
13712
14441
|
for (const s of services) {
|
|
13713
|
-
if (s.node.name === name ||
|
|
14442
|
+
if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
|
|
13714
14443
|
}
|
|
13715
14444
|
return null;
|
|
13716
14445
|
}
|
|
@@ -13719,7 +14448,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13719
14448
|
let edgesAdded = 0;
|
|
13720
14449
|
let composePath = null;
|
|
13721
14450
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
13722
|
-
const abs =
|
|
14451
|
+
const abs = import_node_path57.default.join(scanPath, name);
|
|
13723
14452
|
if (await exists(abs)) {
|
|
13724
14453
|
composePath = abs;
|
|
13725
14454
|
break;
|
|
@@ -13732,13 +14461,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13732
14461
|
} catch (err) {
|
|
13733
14462
|
recordExtractionError(
|
|
13734
14463
|
"infra docker-compose",
|
|
13735
|
-
|
|
14464
|
+
import_node_path57.default.relative(scanPath, composePath),
|
|
13736
14465
|
err
|
|
13737
14466
|
);
|
|
13738
14467
|
return { nodesAdded, edgesAdded };
|
|
13739
14468
|
}
|
|
13740
14469
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
13741
|
-
const evidenceFile =
|
|
14470
|
+
const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
|
|
13742
14471
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
13743
14472
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
13744
14473
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -13760,15 +14489,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13760
14489
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
13761
14490
|
const targetId = composeNameToNodeId.get(dep);
|
|
13762
14491
|
if (!targetId) continue;
|
|
13763
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
14492
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
|
|
13764
14493
|
if (graph.hasEdge(edgeId)) continue;
|
|
13765
14494
|
const edge = {
|
|
13766
14495
|
id: edgeId,
|
|
13767
14496
|
source: sourceId,
|
|
13768
14497
|
target: targetId,
|
|
13769
|
-
type:
|
|
13770
|
-
provenance:
|
|
13771
|
-
confidence: (0,
|
|
14498
|
+
type: import_types47.EdgeType.DEPENDS_ON,
|
|
14499
|
+
provenance: import_types47.Provenance.EXTRACTED,
|
|
14500
|
+
confidence: (0, import_types47.confidenceForExtracted)("structural"),
|
|
13772
14501
|
evidence: { file: evidenceFile }
|
|
13773
14502
|
};
|
|
13774
14503
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13780,9 +14509,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13780
14509
|
|
|
13781
14510
|
// src/extract/infra/dockerfile.ts
|
|
13782
14511
|
init_cjs_shims();
|
|
13783
|
-
var
|
|
13784
|
-
var
|
|
13785
|
-
var
|
|
14512
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14513
|
+
var import_node_fs25 = require("fs");
|
|
14514
|
+
var import_types48 = require("@neat.is/types");
|
|
13786
14515
|
function readDockerfile(content) {
|
|
13787
14516
|
let image = null;
|
|
13788
14517
|
const ports = [];
|
|
@@ -13811,15 +14540,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13811
14540
|
let nodesAdded = 0;
|
|
13812
14541
|
let edgesAdded = 0;
|
|
13813
14542
|
for (const service of services) {
|
|
13814
|
-
const dockerfilePath =
|
|
14543
|
+
const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
|
|
13815
14544
|
if (!await exists(dockerfilePath)) continue;
|
|
13816
14545
|
let content;
|
|
13817
14546
|
try {
|
|
13818
|
-
content = await
|
|
14547
|
+
content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
|
|
13819
14548
|
} catch (err) {
|
|
13820
14549
|
recordExtractionError(
|
|
13821
14550
|
"infra dockerfile",
|
|
13822
|
-
|
|
14551
|
+
import_node_path58.default.relative(scanPath, dockerfilePath),
|
|
13823
14552
|
err
|
|
13824
14553
|
);
|
|
13825
14554
|
continue;
|
|
@@ -13831,8 +14560,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13831
14560
|
graph.addNode(node.id, node);
|
|
13832
14561
|
nodesAdded++;
|
|
13833
14562
|
}
|
|
13834
|
-
const relDockerfile = toPosix(
|
|
13835
|
-
const evidenceFile = toPosix(
|
|
14563
|
+
const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
|
|
14564
|
+
const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
|
|
13836
14565
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
13837
14566
|
graph,
|
|
13838
14567
|
service.pkg.name,
|
|
@@ -13841,15 +14570,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13841
14570
|
);
|
|
13842
14571
|
nodesAdded += fn;
|
|
13843
14572
|
edgesAdded += fe;
|
|
13844
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
14573
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
|
|
13845
14574
|
if (!graph.hasEdge(edgeId)) {
|
|
13846
14575
|
const edge = {
|
|
13847
14576
|
id: edgeId,
|
|
13848
14577
|
source: fileNodeId,
|
|
13849
14578
|
target: node.id,
|
|
13850
|
-
type:
|
|
13851
|
-
provenance:
|
|
13852
|
-
confidence: (0,
|
|
14579
|
+
type: import_types48.EdgeType.RUNS_ON,
|
|
14580
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14581
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13853
14582
|
evidence: {
|
|
13854
14583
|
file: evidenceFile,
|
|
13855
14584
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -13864,15 +14593,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13864
14593
|
graph.addNode(portNode.id, portNode);
|
|
13865
14594
|
nodesAdded++;
|
|
13866
14595
|
}
|
|
13867
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
14596
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
|
|
13868
14597
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
13869
14598
|
const portEdge = {
|
|
13870
14599
|
id: portEdgeId,
|
|
13871
14600
|
source: fileNodeId,
|
|
13872
14601
|
target: portNode.id,
|
|
13873
|
-
type:
|
|
13874
|
-
provenance:
|
|
13875
|
-
confidence: (0,
|
|
14602
|
+
type: import_types48.EdgeType.CONNECTS_TO,
|
|
14603
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14604
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13876
14605
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
13877
14606
|
};
|
|
13878
14607
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -13884,23 +14613,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13884
14613
|
|
|
13885
14614
|
// src/extract/infra/terraform.ts
|
|
13886
14615
|
init_cjs_shims();
|
|
13887
|
-
var
|
|
13888
|
-
var
|
|
13889
|
-
var
|
|
14616
|
+
var import_node_fs26 = require("fs");
|
|
14617
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14618
|
+
var import_types49 = require("@neat.is/types");
|
|
13890
14619
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
13891
14620
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
13892
14621
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
13893
14622
|
if (depth > max) return [];
|
|
13894
14623
|
const out = [];
|
|
13895
|
-
const entries = await
|
|
14624
|
+
const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13896
14625
|
for (const entry of entries) {
|
|
13897
14626
|
if (entry.isDirectory()) {
|
|
13898
14627
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
13899
|
-
const child =
|
|
14628
|
+
const child = import_node_path59.default.join(start, entry.name);
|
|
13900
14629
|
if (await isPythonVenvDir(child)) continue;
|
|
13901
14630
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
13902
14631
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
13903
|
-
out.push(
|
|
14632
|
+
out.push(import_node_path59.default.join(start, entry.name));
|
|
13904
14633
|
}
|
|
13905
14634
|
}
|
|
13906
14635
|
return out;
|
|
@@ -13931,8 +14660,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13931
14660
|
let edgesAdded = 0;
|
|
13932
14661
|
const files = await walkTfFiles(scanPath);
|
|
13933
14662
|
for (const file of files) {
|
|
13934
|
-
const content = await
|
|
13935
|
-
const evidenceFile = toPosix(
|
|
14663
|
+
const content = await import_node_fs26.promises.readFile(file, "utf8");
|
|
14664
|
+
const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
|
|
13936
14665
|
const resources = [];
|
|
13937
14666
|
const byKey = /* @__PURE__ */ new Map();
|
|
13938
14667
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -13967,16 +14696,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13967
14696
|
if (!target) continue;
|
|
13968
14697
|
if (seen.has(target.nodeId)) continue;
|
|
13969
14698
|
seen.add(target.nodeId);
|
|
13970
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
14699
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
|
|
13971
14700
|
if (graph.hasEdge(edgeId)) continue;
|
|
13972
14701
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
13973
14702
|
const edge = {
|
|
13974
14703
|
id: edgeId,
|
|
13975
14704
|
source: resource.nodeId,
|
|
13976
14705
|
target: target.nodeId,
|
|
13977
|
-
type:
|
|
13978
|
-
provenance:
|
|
13979
|
-
confidence: (0,
|
|
14706
|
+
type: import_types49.EdgeType.DEPENDS_ON,
|
|
14707
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
14708
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
13980
14709
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
13981
14710
|
};
|
|
13982
14711
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13989,8 +14718,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13989
14718
|
|
|
13990
14719
|
// src/extract/infra/k8s.ts
|
|
13991
14720
|
init_cjs_shims();
|
|
13992
|
-
var
|
|
13993
|
-
var
|
|
14721
|
+
var import_node_fs27 = require("fs");
|
|
14722
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
13994
14723
|
var import_yaml3 = require("yaml");
|
|
13995
14724
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
13996
14725
|
Service: "k8s-service",
|
|
@@ -14004,15 +14733,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
14004
14733
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
14005
14734
|
if (depth > max) return [];
|
|
14006
14735
|
const out = [];
|
|
14007
|
-
const entries = await
|
|
14736
|
+
const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
14008
14737
|
for (const entry of entries) {
|
|
14009
14738
|
if (entry.isDirectory()) {
|
|
14010
14739
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
14011
|
-
const child =
|
|
14740
|
+
const child = import_node_path60.default.join(start, entry.name);
|
|
14012
14741
|
if (await isPythonVenvDir(child)) continue;
|
|
14013
14742
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
14014
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
14015
|
-
out.push(
|
|
14743
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
|
|
14744
|
+
out.push(import_node_path60.default.join(start, entry.name));
|
|
14016
14745
|
}
|
|
14017
14746
|
}
|
|
14018
14747
|
return out;
|
|
@@ -14021,7 +14750,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
14021
14750
|
let nodesAdded = 0;
|
|
14022
14751
|
const files = await walkYamlFiles2(scanPath);
|
|
14023
14752
|
for (const file of files) {
|
|
14024
|
-
const content = await
|
|
14753
|
+
const content = await import_node_fs27.promises.readFile(file, "utf8");
|
|
14025
14754
|
let docs;
|
|
14026
14755
|
try {
|
|
14027
14756
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -14045,16 +14774,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
14045
14774
|
|
|
14046
14775
|
// src/extract/infra/cloudflare.ts
|
|
14047
14776
|
init_cjs_shims();
|
|
14048
|
-
var
|
|
14049
|
-
var
|
|
14777
|
+
var import_node_fs28 = require("fs");
|
|
14778
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
14050
14779
|
var import_smol_toml3 = require("smol-toml");
|
|
14051
|
-
var
|
|
14780
|
+
var import_types50 = require("@neat.is/types");
|
|
14052
14781
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
14053
14782
|
async function readWranglerConfig(dir) {
|
|
14054
14783
|
for (const filename of WRANGLER_FILENAMES) {
|
|
14055
|
-
const abs =
|
|
14784
|
+
const abs = import_node_path61.default.join(dir, filename);
|
|
14056
14785
|
if (!await exists(abs)) continue;
|
|
14057
|
-
const raw = await
|
|
14786
|
+
const raw = await import_node_fs28.promises.readFile(abs, "utf8");
|
|
14058
14787
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14059
14788
|
return { config, relFile: filename, raw };
|
|
14060
14789
|
}
|
|
@@ -14096,8 +14825,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
14096
14825
|
source: anchorId,
|
|
14097
14826
|
target: node.id,
|
|
14098
14827
|
type: edgeType,
|
|
14099
|
-
provenance:
|
|
14100
|
-
confidence: (0,
|
|
14828
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14829
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14101
14830
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
14102
14831
|
};
|
|
14103
14832
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14115,11 +14844,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14115
14844
|
try {
|
|
14116
14845
|
read = await readWranglerConfig(service.dir);
|
|
14117
14846
|
} catch (err) {
|
|
14118
|
-
recordExtractionError("infra cloudflare",
|
|
14847
|
+
recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
|
|
14119
14848
|
continue;
|
|
14120
14849
|
}
|
|
14121
14850
|
if (!read || !read.config.name) continue;
|
|
14122
|
-
const evidenceFile = toPosix(
|
|
14851
|
+
const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
|
|
14123
14852
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
14124
14853
|
}
|
|
14125
14854
|
for (const worker of discovered) {
|
|
@@ -14131,7 +14860,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14131
14860
|
}
|
|
14132
14861
|
let anchorId = service.node.id;
|
|
14133
14862
|
if (config.main) {
|
|
14134
|
-
const entryRelPath = toPosix(
|
|
14863
|
+
const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
|
|
14135
14864
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
14136
14865
|
graph,
|
|
14137
14866
|
service.pkg.name,
|
|
@@ -14158,15 +14887,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14158
14887
|
nodesAdded++;
|
|
14159
14888
|
}
|
|
14160
14889
|
if (runtimeNode.id !== anchorId) {
|
|
14161
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
14890
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
|
|
14162
14891
|
if (!graph.hasEdge(runsOnId)) {
|
|
14163
14892
|
const edge = {
|
|
14164
14893
|
id: runsOnId,
|
|
14165
14894
|
source: anchorId,
|
|
14166
14895
|
target: runtimeNode.id,
|
|
14167
|
-
type:
|
|
14168
|
-
provenance:
|
|
14169
|
-
confidence: (0,
|
|
14896
|
+
type: import_types50.EdgeType.RUNS_ON,
|
|
14897
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14898
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14170
14899
|
evidence: {
|
|
14171
14900
|
file: evidenceFile,
|
|
14172
14901
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -14180,7 +14909,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14180
14909
|
const result = addResourceEdge(
|
|
14181
14910
|
graph,
|
|
14182
14911
|
anchorId,
|
|
14183
|
-
|
|
14912
|
+
import_types50.EdgeType.CONNECTS_TO,
|
|
14184
14913
|
"cloudflare-route",
|
|
14185
14914
|
route,
|
|
14186
14915
|
evidenceFile,
|
|
@@ -14204,7 +14933,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14204
14933
|
const result = addResourceEdge(
|
|
14205
14934
|
graph,
|
|
14206
14935
|
anchorId,
|
|
14207
|
-
|
|
14936
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14208
14937
|
group.kind,
|
|
14209
14938
|
name,
|
|
14210
14939
|
evidenceFile,
|
|
@@ -14218,7 +14947,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14218
14947
|
const result = addResourceEdge(
|
|
14219
14948
|
graph,
|
|
14220
14949
|
anchorId,
|
|
14221
|
-
|
|
14950
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14222
14951
|
"cloudflare-cron",
|
|
14223
14952
|
cron,
|
|
14224
14953
|
evidenceFile,
|
|
@@ -14231,7 +14960,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14231
14960
|
const result = addResourceEdge(
|
|
14232
14961
|
graph,
|
|
14233
14962
|
anchorId,
|
|
14234
|
-
|
|
14963
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14235
14964
|
"cloudflare-env-var",
|
|
14236
14965
|
varName,
|
|
14237
14966
|
evidenceFile,
|
|
@@ -14244,15 +14973,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14244
14973
|
if (!svc.service) continue;
|
|
14245
14974
|
const target = workerIndex.get(svc.service);
|
|
14246
14975
|
if (target && target.anchorId !== anchorId) {
|
|
14247
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
14976
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
|
|
14248
14977
|
if (!graph.hasEdge(edgeId)) {
|
|
14249
14978
|
const edge = {
|
|
14250
14979
|
id: edgeId,
|
|
14251
14980
|
source: anchorId,
|
|
14252
14981
|
target: target.anchorId,
|
|
14253
|
-
type:
|
|
14254
|
-
provenance:
|
|
14255
|
-
confidence: (0,
|
|
14982
|
+
type: import_types50.EdgeType.CALLS,
|
|
14983
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14984
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14256
14985
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
14257
14986
|
};
|
|
14258
14987
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14263,7 +14992,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14263
14992
|
const result = addResourceEdge(
|
|
14264
14993
|
graph,
|
|
14265
14994
|
anchorId,
|
|
14266
|
-
|
|
14995
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14267
14996
|
"cloudflare-service-binding",
|
|
14268
14997
|
svc.service,
|
|
14269
14998
|
evidenceFile,
|
|
@@ -14278,24 +15007,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14278
15007
|
|
|
14279
15008
|
// src/extract/infra/vercel.ts
|
|
14280
15009
|
init_cjs_shims();
|
|
14281
|
-
var
|
|
14282
|
-
var
|
|
14283
|
-
var
|
|
15010
|
+
var import_node_fs29 = require("fs");
|
|
15011
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
15012
|
+
var import_types51 = require("@neat.is/types");
|
|
14284
15013
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
14285
15014
|
async function readVercelConfig(dir) {
|
|
14286
15015
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
14287
|
-
const abs =
|
|
15016
|
+
const abs = import_node_path62.default.join(dir, filename);
|
|
14288
15017
|
if (!await exists(abs)) continue;
|
|
14289
|
-
const raw = await
|
|
15018
|
+
const raw = await import_node_fs29.promises.readFile(abs, "utf8");
|
|
14290
15019
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
14291
15020
|
return { config, relFile: filename, raw };
|
|
14292
15021
|
}
|
|
14293
15022
|
return null;
|
|
14294
15023
|
}
|
|
14295
15024
|
async function readLinkedProjectName(dir) {
|
|
14296
|
-
const abs =
|
|
15025
|
+
const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
|
|
14297
15026
|
if (!await exists(abs)) return void 0;
|
|
14298
|
-
const parsed = JSON.parse(await
|
|
15027
|
+
const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
|
|
14299
15028
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
14300
15029
|
}
|
|
14301
15030
|
function routeSource(route) {
|
|
@@ -14311,7 +15040,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14311
15040
|
read = await readVercelConfig(service.dir);
|
|
14312
15041
|
projectName = await readLinkedProjectName(service.dir);
|
|
14313
15042
|
} catch (err) {
|
|
14314
|
-
recordExtractionError("infra vercel",
|
|
15043
|
+
recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
|
|
14315
15044
|
continue;
|
|
14316
15045
|
}
|
|
14317
15046
|
if (!read && !projectName) continue;
|
|
@@ -14327,7 +15056,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14327
15056
|
const anchorId = service.node.id;
|
|
14328
15057
|
if (!read) continue;
|
|
14329
15058
|
const { config, relFile, raw } = read;
|
|
14330
|
-
const evidenceFile = toPosix(
|
|
15059
|
+
const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
|
|
14331
15060
|
const add = (edgeType, kind, name) => {
|
|
14332
15061
|
if (!name) return;
|
|
14333
15062
|
const result = emitPlatformResourceEdge(
|
|
@@ -14343,12 +15072,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14343
15072
|
nodesAdded += result.nodesAdded;
|
|
14344
15073
|
edgesAdded += result.edgesAdded;
|
|
14345
15074
|
};
|
|
14346
|
-
add(
|
|
14347
|
-
for (const cron of config.crons ?? []) add(
|
|
14348
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
14349
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
15075
|
+
add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
15076
|
+
for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
15077
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
15078
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
14350
15079
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
14351
|
-
add(
|
|
15080
|
+
add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
14352
15081
|
}
|
|
14353
15082
|
}
|
|
14354
15083
|
return { nodesAdded, edgesAdded };
|
|
@@ -14356,16 +15085,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14356
15085
|
|
|
14357
15086
|
// src/extract/infra/railway.ts
|
|
14358
15087
|
init_cjs_shims();
|
|
14359
|
-
var
|
|
14360
|
-
var
|
|
15088
|
+
var import_node_fs30 = require("fs");
|
|
15089
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
14361
15090
|
var import_smol_toml4 = require("smol-toml");
|
|
14362
|
-
var
|
|
15091
|
+
var import_types52 = require("@neat.is/types");
|
|
14363
15092
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
14364
15093
|
async function readRailwayConfig(dir) {
|
|
14365
15094
|
for (const filename of RAILWAY_FILENAMES) {
|
|
14366
|
-
const abs =
|
|
15095
|
+
const abs = import_node_path63.default.join(dir, filename);
|
|
14367
15096
|
if (!await exists(abs)) continue;
|
|
14368
|
-
const raw = await
|
|
15097
|
+
const raw = await import_node_fs30.promises.readFile(abs, "utf8");
|
|
14369
15098
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14370
15099
|
return { config, relFile: filename, raw };
|
|
14371
15100
|
}
|
|
@@ -14379,7 +15108,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14379
15108
|
try {
|
|
14380
15109
|
read = await readRailwayConfig(service.dir);
|
|
14381
15110
|
} catch (err) {
|
|
14382
|
-
recordExtractionError("infra railway",
|
|
15111
|
+
recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
|
|
14383
15112
|
continue;
|
|
14384
15113
|
}
|
|
14385
15114
|
if (!read) continue;
|
|
@@ -14389,7 +15118,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14389
15118
|
}
|
|
14390
15119
|
const anchorId = service.node.id;
|
|
14391
15120
|
const { config, relFile, raw } = read;
|
|
14392
|
-
const evidenceFile = toPosix(
|
|
15121
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
|
|
14393
15122
|
const add = (edgeType, kind, name) => {
|
|
14394
15123
|
if (!name) return;
|
|
14395
15124
|
const result = emitPlatformResourceEdge(
|
|
@@ -14405,24 +15134,24 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14405
15134
|
nodesAdded += result.nodesAdded;
|
|
14406
15135
|
edgesAdded += result.edgesAdded;
|
|
14407
15136
|
};
|
|
14408
|
-
add(
|
|
14409
|
-
add(
|
|
14410
|
-
add(
|
|
15137
|
+
add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
|
|
15138
|
+
add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
15139
|
+
add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
14411
15140
|
}
|
|
14412
15141
|
return { nodesAdded, edgesAdded };
|
|
14413
15142
|
}
|
|
14414
15143
|
|
|
14415
15144
|
// src/extract/infra/supabase.ts
|
|
14416
15145
|
init_cjs_shims();
|
|
14417
|
-
var
|
|
14418
|
-
var
|
|
15146
|
+
var import_node_fs31 = require("fs");
|
|
15147
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
14419
15148
|
var import_smol_toml5 = require("smol-toml");
|
|
14420
|
-
var
|
|
15149
|
+
var import_types53 = require("@neat.is/types");
|
|
14421
15150
|
async function readSupabaseConfig(dir) {
|
|
14422
|
-
const relFile =
|
|
14423
|
-
const abs =
|
|
15151
|
+
const relFile = import_node_path64.default.join("supabase", "config.toml");
|
|
15152
|
+
const abs = import_node_path64.default.join(dir, relFile);
|
|
14424
15153
|
if (!await exists(abs)) return null;
|
|
14425
|
-
const raw = await
|
|
15154
|
+
const raw = await import_node_fs31.promises.readFile(abs, "utf8");
|
|
14426
15155
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
14427
15156
|
return { config, relFile, raw };
|
|
14428
15157
|
}
|
|
@@ -14434,7 +15163,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14434
15163
|
try {
|
|
14435
15164
|
read = await readSupabaseConfig(service.dir);
|
|
14436
15165
|
} catch (err) {
|
|
14437
|
-
recordExtractionError("infra supabase",
|
|
15166
|
+
recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
|
|
14438
15167
|
continue;
|
|
14439
15168
|
}
|
|
14440
15169
|
if (!read) continue;
|
|
@@ -14449,7 +15178,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14449
15178
|
});
|
|
14450
15179
|
}
|
|
14451
15180
|
const anchorId = service.node.id;
|
|
14452
|
-
const evidenceFile = toPosix(
|
|
15181
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
|
|
14453
15182
|
const add = (edgeType, kind, name) => {
|
|
14454
15183
|
if (!name) return;
|
|
14455
15184
|
const result = emitPlatformResourceEdge(
|
|
@@ -14465,10 +15194,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14465
15194
|
nodesAdded += result.nodesAdded;
|
|
14466
15195
|
edgesAdded += result.edgesAdded;
|
|
14467
15196
|
};
|
|
14468
|
-
add(
|
|
14469
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
14470
|
-
if (config.storage) add(
|
|
14471
|
-
if (config.auth) add(
|
|
15197
|
+
add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
15198
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
15199
|
+
if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
15200
|
+
if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
14472
15201
|
}
|
|
14473
15202
|
return { nodesAdded, edgesAdded };
|
|
14474
15203
|
}
|
|
@@ -14491,14 +15220,14 @@ async function addInfra(graph, scanPath, services) {
|
|
|
14491
15220
|
|
|
14492
15221
|
// src/extract/zod-shapes.ts
|
|
14493
15222
|
init_cjs_shims();
|
|
14494
|
-
var
|
|
14495
|
-
var
|
|
15223
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15224
|
+
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
14496
15225
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
14497
|
-
var
|
|
15226
|
+
var import_types54 = require("@neat.is/types");
|
|
14498
15227
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
14499
15228
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
14500
15229
|
function parserForExt4(ext) {
|
|
14501
|
-
const p = new
|
|
15230
|
+
const p = new import_tree_sitter17.default();
|
|
14502
15231
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
14503
15232
|
return p;
|
|
14504
15233
|
}
|
|
@@ -14586,7 +15315,7 @@ function topLevelSchemas(root) {
|
|
|
14586
15315
|
}
|
|
14587
15316
|
function zodShapesFromFile(file, serviceDir) {
|
|
14588
15317
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
14589
|
-
const tree = parseSource3(parserForExt4(
|
|
15318
|
+
const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
|
|
14590
15319
|
const out = [];
|
|
14591
15320
|
const seen = /* @__PURE__ */ new Set();
|
|
14592
15321
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14600,11 +15329,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
14600
15329
|
seen.add(name);
|
|
14601
15330
|
const line = call.startPosition.row + 1;
|
|
14602
15331
|
out.push({
|
|
14603
|
-
infraId: (0,
|
|
15332
|
+
infraId: (0, import_types54.infraId)("zod-schema", name),
|
|
14604
15333
|
name,
|
|
14605
15334
|
fields,
|
|
14606
15335
|
evidence: {
|
|
14607
|
-
file:
|
|
15336
|
+
file: import_node_path65.default.relative(serviceDir, file.path),
|
|
14608
15337
|
line,
|
|
14609
15338
|
snippet: snippet(file.content, line)
|
|
14610
15339
|
}
|
|
@@ -14635,7 +15364,7 @@ async function addZodShapes(graph, services) {
|
|
|
14635
15364
|
if (!graph.hasNode(shape.infraId)) {
|
|
14636
15365
|
const node = {
|
|
14637
15366
|
id: shape.infraId,
|
|
14638
|
-
type:
|
|
15367
|
+
type: import_types54.NodeType.InfraNode,
|
|
14639
15368
|
name: shape.name,
|
|
14640
15369
|
provider: "self",
|
|
14641
15370
|
kind: "zod-schema"
|
|
@@ -14645,14 +15374,14 @@ async function addZodShapes(graph, services) {
|
|
|
14645
15374
|
}
|
|
14646
15375
|
if (shape.fields.length > 0) {
|
|
14647
15376
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
14648
|
-
if (node.type ===
|
|
15377
|
+
if (node.type === import_types54.NodeType.InfraNode) {
|
|
14649
15378
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
14650
15379
|
...node,
|
|
14651
15380
|
columns: foldColumns(
|
|
14652
15381
|
node.columns,
|
|
14653
15382
|
shape.fields,
|
|
14654
|
-
|
|
14655
|
-
(0,
|
|
15383
|
+
import_types54.Provenance.EXTRACTED,
|
|
15384
|
+
(0, import_types54.confidenceForExtracted)("structural")
|
|
14656
15385
|
)
|
|
14657
15386
|
});
|
|
14658
15387
|
}
|
|
@@ -14666,15 +15395,15 @@ async function addZodShapes(graph, services) {
|
|
|
14666
15395
|
);
|
|
14667
15396
|
nodesAdded += n;
|
|
14668
15397
|
edgesAdded += e;
|
|
14669
|
-
const edgeId = (0,
|
|
15398
|
+
const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
|
|
14670
15399
|
if (!graph.hasEdge(edgeId)) {
|
|
14671
15400
|
const edge = {
|
|
14672
15401
|
id: edgeId,
|
|
14673
15402
|
source: fileNodeId,
|
|
14674
15403
|
target: shape.infraId,
|
|
14675
|
-
type:
|
|
14676
|
-
provenance:
|
|
14677
|
-
confidence: (0,
|
|
15404
|
+
type: import_types54.EdgeType.CONTAINS,
|
|
15405
|
+
provenance: import_types54.Provenance.EXTRACTED,
|
|
15406
|
+
confidence: (0, import_types54.confidenceForExtracted)("structural"),
|
|
14678
15407
|
evidence: shape.evidence
|
|
14679
15408
|
};
|
|
14680
15409
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -14688,7 +15417,7 @@ async function addZodShapes(graph, services) {
|
|
|
14688
15417
|
|
|
14689
15418
|
// src/extract/firestore-rules.ts
|
|
14690
15419
|
init_cjs_shims();
|
|
14691
|
-
var
|
|
15420
|
+
var import_types55 = require("@neat.is/types");
|
|
14692
15421
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
14693
15422
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
14694
15423
|
function stripComments(src) {
|
|
@@ -14828,7 +15557,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
14828
15557
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
14829
15558
|
graph.forEachNode((id, attrs) => {
|
|
14830
15559
|
const node = attrs;
|
|
14831
|
-
if (node.type !==
|
|
15560
|
+
if (node.type !== import_types55.NodeType.InfraNode) return;
|
|
14832
15561
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
14833
15562
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
14834
15563
|
if (!fields || fields.size === 0) return;
|
|
@@ -14841,17 +15570,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
14841
15570
|
}
|
|
14842
15571
|
|
|
14843
15572
|
// src/extract/index.ts
|
|
14844
|
-
var
|
|
15573
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
14845
15574
|
|
|
14846
15575
|
// src/extract/retire.ts
|
|
14847
15576
|
init_cjs_shims();
|
|
14848
|
-
var
|
|
14849
|
-
var
|
|
14850
|
-
var
|
|
15577
|
+
var import_node_fs32 = require("fs");
|
|
15578
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
15579
|
+
var import_types56 = require("@neat.is/types");
|
|
14851
15580
|
function dropOrphanedFileNodes(graph) {
|
|
14852
15581
|
const orphans = [];
|
|
14853
15582
|
graph.forEachNode((id, attrs) => {
|
|
14854
|
-
if (attrs.type !==
|
|
15583
|
+
if (attrs.type !== import_types56.NodeType.FileNode) return;
|
|
14855
15584
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
14856
15585
|
orphans.push(id);
|
|
14857
15586
|
}
|
|
@@ -14864,14 +15593,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
14864
15593
|
const bases = [scanPath, ...serviceDirs];
|
|
14865
15594
|
graph.forEachEdge((id, attrs) => {
|
|
14866
15595
|
const edge = attrs;
|
|
14867
|
-
if (edge.provenance !==
|
|
15596
|
+
if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
|
|
14868
15597
|
const evidenceFile = edge.evidence?.file;
|
|
14869
15598
|
if (!evidenceFile) return;
|
|
14870
|
-
if (
|
|
14871
|
-
if (!(0,
|
|
15599
|
+
if (import_node_path66.default.isAbsolute(evidenceFile)) {
|
|
15600
|
+
if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
|
|
14872
15601
|
return;
|
|
14873
15602
|
}
|
|
14874
|
-
const found = bases.some((base) => (0,
|
|
15603
|
+
const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
|
|
14875
15604
|
if (!found) toDrop.push(id);
|
|
14876
15605
|
});
|
|
14877
15606
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -14928,7 +15657,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14928
15657
|
}
|
|
14929
15658
|
const droppedEntries = drainDroppedExtracted();
|
|
14930
15659
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
14931
|
-
const rejectedPath =
|
|
15660
|
+
const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
14932
15661
|
try {
|
|
14933
15662
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
14934
15663
|
} catch (err) {
|
|
@@ -14962,9 +15691,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14962
15691
|
|
|
14963
15692
|
// src/persist.ts
|
|
14964
15693
|
init_cjs_shims();
|
|
14965
|
-
var
|
|
14966
|
-
var
|
|
14967
|
-
var
|
|
15694
|
+
var import_node_fs33 = require("fs");
|
|
15695
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
15696
|
+
var import_types57 = require("@neat.is/types");
|
|
14968
15697
|
var SCHEMA_VERSION = 7;
|
|
14969
15698
|
function migrateV1ToV2(payload) {
|
|
14970
15699
|
const nodes = payload.graph.nodes;
|
|
@@ -14988,7 +15717,7 @@ function migrateV5ToV6(payload) {
|
|
|
14988
15717
|
if (Array.isArray(nodes)) {
|
|
14989
15718
|
for (const node of nodes) {
|
|
14990
15719
|
const attrs = node.attributes;
|
|
14991
|
-
if (!attrs || attrs.type !==
|
|
15720
|
+
if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
|
|
14992
15721
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
14993
15722
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
14994
15723
|
}
|
|
@@ -15004,12 +15733,12 @@ function migrateV2ToV3(payload) {
|
|
|
15004
15733
|
for (const edge of edges) {
|
|
15005
15734
|
const attrs = edge.attributes;
|
|
15006
15735
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
15007
|
-
attrs.provenance =
|
|
15736
|
+
attrs.provenance = import_types57.Provenance.OBSERVED;
|
|
15008
15737
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
15009
15738
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
15010
15739
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
15011
15740
|
if (type && source && target) {
|
|
15012
|
-
const newId = (0,
|
|
15741
|
+
const newId = (0, import_types57.observedEdgeId)(source, target, type);
|
|
15013
15742
|
attrs.id = newId;
|
|
15014
15743
|
if (edge.key) edge.key = newId;
|
|
15015
15744
|
}
|
|
@@ -15018,7 +15747,7 @@ function migrateV2ToV3(payload) {
|
|
|
15018
15747
|
return { ...payload, schemaVersion: 3 };
|
|
15019
15748
|
}
|
|
15020
15749
|
async function ensureDir(filePath) {
|
|
15021
|
-
await
|
|
15750
|
+
await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
|
|
15022
15751
|
}
|
|
15023
15752
|
async function saveGraphToDisk(graph, outPath) {
|
|
15024
15753
|
await ensureDir(outPath);
|
|
@@ -15028,13 +15757,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
15028
15757
|
graph: graph.export()
|
|
15029
15758
|
};
|
|
15030
15759
|
const tmp = `${outPath}.tmp`;
|
|
15031
|
-
await
|
|
15032
|
-
await
|
|
15760
|
+
await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
15761
|
+
await import_node_fs33.promises.rename(tmp, outPath);
|
|
15033
15762
|
}
|
|
15034
15763
|
async function loadGraphFromDisk(graph, outPath) {
|
|
15035
15764
|
let raw;
|
|
15036
15765
|
try {
|
|
15037
|
-
raw = await
|
|
15766
|
+
raw = await import_node_fs33.promises.readFile(outPath, "utf8");
|
|
15038
15767
|
} catch (err) {
|
|
15039
15768
|
if (err.code === "ENOENT") return;
|
|
15040
15769
|
throw err;
|
|
@@ -15110,19 +15839,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
15110
15839
|
init_cjs_shims();
|
|
15111
15840
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15112
15841
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15113
|
-
var
|
|
15842
|
+
var import_types92 = require("@neat.is/types");
|
|
15114
15843
|
|
|
15115
15844
|
// src/extend/index.ts
|
|
15116
15845
|
init_cjs_shims();
|
|
15117
|
-
var
|
|
15118
|
-
var
|
|
15846
|
+
var import_node_fs35 = require("fs");
|
|
15847
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
15119
15848
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
15120
15849
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
15121
15850
|
|
|
15122
15851
|
// src/installers/package-manager.ts
|
|
15123
15852
|
init_cjs_shims();
|
|
15124
|
-
var
|
|
15125
|
-
var
|
|
15853
|
+
var import_node_fs34 = require("fs");
|
|
15854
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
15126
15855
|
var import_node_child_process = require("child_process");
|
|
15127
15856
|
var LOCKFILE_PRIORITY = [
|
|
15128
15857
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -15137,29 +15866,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
15137
15866
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
15138
15867
|
async function exists2(p) {
|
|
15139
15868
|
try {
|
|
15140
|
-
await
|
|
15869
|
+
await import_node_fs34.promises.access(p);
|
|
15141
15870
|
return true;
|
|
15142
15871
|
} catch {
|
|
15143
15872
|
return false;
|
|
15144
15873
|
}
|
|
15145
15874
|
}
|
|
15146
15875
|
async function detectPackageManager(serviceDir) {
|
|
15147
|
-
let dir =
|
|
15876
|
+
let dir = import_node_path69.default.resolve(serviceDir);
|
|
15148
15877
|
const stops = /* @__PURE__ */ new Set();
|
|
15149
15878
|
for (let i = 0; i < 64; i++) {
|
|
15150
15879
|
if (stops.has(dir)) break;
|
|
15151
15880
|
stops.add(dir);
|
|
15152
15881
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
15153
|
-
const lockPath =
|
|
15882
|
+
const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
|
|
15154
15883
|
if (await exists2(lockPath)) {
|
|
15155
15884
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
15156
15885
|
}
|
|
15157
15886
|
}
|
|
15158
|
-
const parent =
|
|
15887
|
+
const parent = import_node_path69.default.dirname(dir);
|
|
15159
15888
|
if (parent === dir) break;
|
|
15160
15889
|
dir = parent;
|
|
15161
15890
|
}
|
|
15162
|
-
return { pm: "npm", cwd:
|
|
15891
|
+
return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
15163
15892
|
}
|
|
15164
15893
|
async function runPackageManagerInstall(cmd) {
|
|
15165
15894
|
return new Promise((resolve) => {
|
|
@@ -15201,15 +15930,15 @@ ${err.message}`
|
|
|
15201
15930
|
// src/extend/index.ts
|
|
15202
15931
|
async function fileExists2(p) {
|
|
15203
15932
|
try {
|
|
15204
|
-
await
|
|
15933
|
+
await import_node_fs35.promises.access(p);
|
|
15205
15934
|
return true;
|
|
15206
15935
|
} catch {
|
|
15207
15936
|
return false;
|
|
15208
15937
|
}
|
|
15209
15938
|
}
|
|
15210
15939
|
async function readPackageJson(scanPath) {
|
|
15211
|
-
const pkgPath =
|
|
15212
|
-
const raw = await
|
|
15940
|
+
const pkgPath = import_node_path70.default.join(scanPath, "package.json");
|
|
15941
|
+
const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
|
|
15213
15942
|
return JSON.parse(raw);
|
|
15214
15943
|
}
|
|
15215
15944
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -15222,27 +15951,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
15222
15951
|
]);
|
|
15223
15952
|
async function findHookFiles(scanPath) {
|
|
15224
15953
|
const found = [];
|
|
15225
|
-
const
|
|
15226
|
-
const entries = await
|
|
15954
|
+
const walk10 = async (dir) => {
|
|
15955
|
+
const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
15227
15956
|
for (const entry of entries) {
|
|
15228
15957
|
if (entry.isDirectory()) {
|
|
15229
15958
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
15230
|
-
await
|
|
15959
|
+
await walk10(import_node_path70.default.join(dir, entry.name));
|
|
15231
15960
|
} else if (entry.isFile()) {
|
|
15232
15961
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
15233
|
-
const rel =
|
|
15234
|
-
found.push(rel.split(
|
|
15962
|
+
const rel = import_node_path70.default.relative(scanPath, import_node_path70.default.join(dir, entry.name));
|
|
15963
|
+
found.push(rel.split(import_node_path70.default.sep).join("/"));
|
|
15235
15964
|
}
|
|
15236
15965
|
}
|
|
15237
15966
|
}
|
|
15238
15967
|
};
|
|
15239
|
-
await
|
|
15968
|
+
await walk10(scanPath);
|
|
15240
15969
|
return found.sort();
|
|
15241
15970
|
}
|
|
15242
15971
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
15243
15972
|
let fallback = null;
|
|
15244
15973
|
for (const file of hookFiles) {
|
|
15245
|
-
const content = await
|
|
15974
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
|
|
15246
15975
|
const patched = splicedContent(content, snippet2);
|
|
15247
15976
|
if (patched !== null) return { file, content, patched };
|
|
15248
15977
|
if (fallback === null) fallback = { file, content };
|
|
@@ -15250,12 +15979,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
15250
15979
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
15251
15980
|
}
|
|
15252
15981
|
function extendLogPath() {
|
|
15253
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
15982
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path70.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
15254
15983
|
}
|
|
15255
15984
|
async function appendExtendLog(entry) {
|
|
15256
15985
|
const logPath = extendLogPath();
|
|
15257
|
-
await
|
|
15258
|
-
await
|
|
15986
|
+
await import_node_fs35.promises.mkdir(import_node_path70.default.dirname(logPath), { recursive: true });
|
|
15987
|
+
await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
15259
15988
|
}
|
|
15260
15989
|
function splicedContent(fileContent, snippet2) {
|
|
15261
15990
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -15313,7 +16042,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
15313
16042
|
}
|
|
15314
16043
|
async function describeProjectInstrumentation(ctx) {
|
|
15315
16044
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15316
|
-
const envNeat = await fileExists2(
|
|
16045
|
+
const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
|
|
15317
16046
|
const registryInstrPackages = new Set(
|
|
15318
16047
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
15319
16048
|
);
|
|
@@ -15335,7 +16064,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
15335
16064
|
);
|
|
15336
16065
|
}
|
|
15337
16066
|
for (const file of hookFiles) {
|
|
15338
|
-
const content = await
|
|
16067
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
|
|
15339
16068
|
if (content.includes(args.registration_snippet)) {
|
|
15340
16069
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
15341
16070
|
}
|
|
@@ -15347,18 +16076,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
15347
16076
|
);
|
|
15348
16077
|
}
|
|
15349
16078
|
const primaryFile = primary.file;
|
|
15350
|
-
const primaryPath =
|
|
16079
|
+
const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
|
|
15351
16080
|
const filesTouched = [];
|
|
15352
16081
|
const depsAdded = [];
|
|
15353
|
-
const pkgPath =
|
|
16082
|
+
const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
|
|
15354
16083
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15355
16084
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
15356
16085
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
15357
|
-
await
|
|
16086
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15358
16087
|
filesTouched.push("package.json");
|
|
15359
16088
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
15360
16089
|
}
|
|
15361
|
-
await
|
|
16090
|
+
await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
15362
16091
|
filesTouched.push(primaryFile);
|
|
15363
16092
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
15364
16093
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -15389,7 +16118,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
15389
16118
|
};
|
|
15390
16119
|
}
|
|
15391
16120
|
for (const file of hookFiles) {
|
|
15392
|
-
const content = await
|
|
16121
|
+
const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
|
|
15393
16122
|
if (content.includes(args.registration_snippet)) {
|
|
15394
16123
|
return {
|
|
15395
16124
|
library: args.library,
|
|
@@ -15424,28 +16153,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
15424
16153
|
if (!await fileExists2(logPath)) {
|
|
15425
16154
|
return { undone: false, message: "no apply found for library" };
|
|
15426
16155
|
}
|
|
15427
|
-
const raw = await
|
|
16156
|
+
const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
|
|
15428
16157
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
15429
16158
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
15430
16159
|
if (!match) {
|
|
15431
16160
|
return { undone: false, message: "no apply found for library" };
|
|
15432
16161
|
}
|
|
15433
|
-
const pkgPath =
|
|
16162
|
+
const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
|
|
15434
16163
|
if (await fileExists2(pkgPath)) {
|
|
15435
16164
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15436
16165
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
15437
16166
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
15438
16167
|
pkg.dependencies = rest;
|
|
15439
|
-
await
|
|
16168
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15440
16169
|
}
|
|
15441
16170
|
}
|
|
15442
16171
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15443
16172
|
for (const file of hookFiles) {
|
|
15444
|
-
const filePath =
|
|
15445
|
-
const content = await
|
|
16173
|
+
const filePath = import_node_path70.default.join(ctx.scanPath, file);
|
|
16174
|
+
const content = await import_node_fs35.promises.readFile(filePath, "utf8");
|
|
15446
16175
|
if (content.includes(match.registration_snippet)) {
|
|
15447
16176
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
15448
|
-
await
|
|
16177
|
+
await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
|
|
15449
16178
|
break;
|
|
15450
16179
|
}
|
|
15451
16180
|
}
|
|
@@ -15457,39 +16186,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
15457
16186
|
|
|
15458
16187
|
// src/divergences.ts
|
|
15459
16188
|
init_cjs_shims();
|
|
15460
|
-
var
|
|
16189
|
+
var import_types58 = require("@neat.is/types");
|
|
15461
16190
|
function bucketKey(source, target, type) {
|
|
15462
16191
|
return `${type}|${source}|${target}`;
|
|
15463
16192
|
}
|
|
15464
16193
|
function bucketSourceFor(graph, edge) {
|
|
15465
|
-
if (edge.type !==
|
|
15466
|
-
const parsed = (0,
|
|
16194
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
|
|
16195
|
+
const parsed = (0, import_types58.parseFileId)(edge.source);
|
|
15467
16196
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
15468
16197
|
const target = graph.getNodeAttributes(edge.target);
|
|
15469
|
-
if (target.type !==
|
|
15470
|
-
return (0,
|
|
16198
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
|
|
16199
|
+
return (0, import_types58.serviceId)(parsed.service);
|
|
15471
16200
|
}
|
|
15472
16201
|
function bucketEdges(graph) {
|
|
15473
16202
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
15474
16203
|
graph.forEachEdge((id, attrs) => {
|
|
15475
16204
|
const e = attrs;
|
|
15476
|
-
const parsed = (0,
|
|
16205
|
+
const parsed = (0, import_types58.parseEdgeId)(id);
|
|
15477
16206
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
15478
16207
|
const source = bucketSourceFor(graph, e);
|
|
15479
16208
|
const key = bucketKey(source, e.target, e.type);
|
|
15480
16209
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
15481
16210
|
switch (provenance) {
|
|
15482
|
-
case
|
|
16211
|
+
case import_types58.Provenance.EXTRACTED:
|
|
15483
16212
|
cur.extracted = e;
|
|
15484
16213
|
break;
|
|
15485
|
-
case
|
|
16214
|
+
case import_types58.Provenance.OBSERVED:
|
|
15486
16215
|
cur.observed = e;
|
|
15487
16216
|
break;
|
|
15488
|
-
case
|
|
16217
|
+
case import_types58.Provenance.INFERRED:
|
|
15489
16218
|
cur.inferred = e;
|
|
15490
16219
|
break;
|
|
15491
16220
|
default:
|
|
15492
|
-
if (e.provenance ===
|
|
16221
|
+
if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
|
|
15493
16222
|
}
|
|
15494
16223
|
buckets2.set(key, cur);
|
|
15495
16224
|
});
|
|
@@ -15498,22 +16227,22 @@ function bucketEdges(graph) {
|
|
|
15498
16227
|
function nodeIsFrontier(graph, nodeId) {
|
|
15499
16228
|
if (!graph.hasNode(nodeId)) return false;
|
|
15500
16229
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15501
|
-
return attrs.type ===
|
|
16230
|
+
return attrs.type === import_types58.NodeType.FrontierNode;
|
|
15502
16231
|
}
|
|
15503
16232
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
15504
16233
|
if (!graph.hasNode(nodeId)) return false;
|
|
15505
16234
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15506
|
-
return attrs.type ===
|
|
16235
|
+
return attrs.type === import_types58.NodeType.WebSocketChannelNode;
|
|
15507
16236
|
}
|
|
15508
16237
|
function nodeIsServerAction(graph, nodeId) {
|
|
15509
16238
|
if (!graph.hasNode(nodeId)) return false;
|
|
15510
16239
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15511
|
-
return attrs.type ===
|
|
16240
|
+
return attrs.type === import_types58.NodeType.ServerActionNode;
|
|
15512
16241
|
}
|
|
15513
16242
|
function nodeIsSymbol(graph, nodeId) {
|
|
15514
16243
|
if (!graph.hasNode(nodeId)) return false;
|
|
15515
16244
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15516
|
-
return attrs.type ===
|
|
16245
|
+
return attrs.type === import_types58.NodeType.SymbolNode;
|
|
15517
16246
|
}
|
|
15518
16247
|
function clampConfidence(n) {
|
|
15519
16248
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -15533,14 +16262,14 @@ function gradedConfidence(edge) {
|
|
|
15533
16262
|
return clampConfidence(confidenceForEdge(edge));
|
|
15534
16263
|
}
|
|
15535
16264
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
16265
|
+
import_types58.EdgeType.CALLS,
|
|
16266
|
+
import_types58.EdgeType.CONNECTS_TO,
|
|
16267
|
+
import_types58.EdgeType.PUBLISHES_TO,
|
|
16268
|
+
import_types58.EdgeType.CONSUMES_FROM
|
|
15540
16269
|
]);
|
|
15541
16270
|
function detectMissingDivergences(graph, bucket) {
|
|
15542
16271
|
const out = [];
|
|
15543
|
-
if (bucket.type ===
|
|
16272
|
+
if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
|
|
15544
16273
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
15545
16274
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
15546
16275
|
if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
|
|
@@ -15582,7 +16311,7 @@ function declaredHostFor(svc) {
|
|
|
15582
16311
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
15583
16312
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15584
16313
|
const e = graph.getEdgeAttributes(edgeId);
|
|
15585
|
-
if (e.type ===
|
|
16314
|
+
if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
|
|
15586
16315
|
return true;
|
|
15587
16316
|
}
|
|
15588
16317
|
}
|
|
@@ -15595,10 +16324,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
15595
16324
|
const out = [];
|
|
15596
16325
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15597
16326
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15598
|
-
if (edge.type !==
|
|
15599
|
-
if (edge.provenance !==
|
|
16327
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16328
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15600
16329
|
const target = graph.getNodeAttributes(edge.target);
|
|
15601
|
-
if (target.type !==
|
|
16330
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15602
16331
|
const observedHost = target.host?.trim();
|
|
15603
16332
|
if (!observedHost) continue;
|
|
15604
16333
|
if (observedHost === declaredHost) continue;
|
|
@@ -15620,10 +16349,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
15620
16349
|
const deps = svc.dependencies ?? {};
|
|
15621
16350
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15622
16351
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15623
|
-
if (edge.type !==
|
|
15624
|
-
if (edge.provenance !==
|
|
16352
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16353
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15625
16354
|
const target = graph.getNodeAttributes(edge.target);
|
|
15626
|
-
if (target.type !==
|
|
16355
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15627
16356
|
for (const pair of compatPairs()) {
|
|
15628
16357
|
if (pair.engine !== target.engine) continue;
|
|
15629
16358
|
const declared = deps[pair.driver];
|
|
@@ -15720,7 +16449,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
15720
16449
|
for (const d of all) {
|
|
15721
16450
|
if (d.type !== "host-mismatch") continue;
|
|
15722
16451
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
15723
|
-
declaredHalf.add((0,
|
|
16452
|
+
declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
|
|
15724
16453
|
}
|
|
15725
16454
|
if (observedHalf.size === 0) return all;
|
|
15726
16455
|
return all.filter((d) => {
|
|
@@ -15739,13 +16468,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15739
16468
|
}
|
|
15740
16469
|
graph.forEachNode((nodeId, attrs) => {
|
|
15741
16470
|
const n = attrs;
|
|
15742
|
-
if (n.type ===
|
|
16471
|
+
if (n.type === import_types58.NodeType.ServiceNode) {
|
|
15743
16472
|
const svc = n;
|
|
15744
16473
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
15745
16474
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
15746
16475
|
return;
|
|
15747
16476
|
}
|
|
15748
|
-
if (n.type ===
|
|
16477
|
+
if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
|
|
15749
16478
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
15750
16479
|
}
|
|
15751
16480
|
});
|
|
@@ -15781,7 +16510,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15781
16510
|
const bc = "column" in b && b.column ? b.column : "";
|
|
15782
16511
|
return ac.localeCompare(bc);
|
|
15783
16512
|
});
|
|
15784
|
-
return
|
|
16513
|
+
return import_types58.DivergenceResultSchema.parse({
|
|
15785
16514
|
divergences: filtered,
|
|
15786
16515
|
totalAffected: filtered.length,
|
|
15787
16516
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15835,7 +16564,7 @@ function queryLogEntries(opts) {
|
|
|
15835
16564
|
|
|
15836
16565
|
// src/diff.ts
|
|
15837
16566
|
init_cjs_shims();
|
|
15838
|
-
var
|
|
16567
|
+
var import_node_fs36 = require("fs");
|
|
15839
16568
|
async function loadSnapshotForDiff(target) {
|
|
15840
16569
|
if (/^https?:\/\//i.test(target)) {
|
|
15841
16570
|
const res = await fetch(target);
|
|
@@ -15844,7 +16573,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
15844
16573
|
}
|
|
15845
16574
|
return await res.json();
|
|
15846
16575
|
}
|
|
15847
|
-
const raw = await
|
|
16576
|
+
const raw = await import_node_fs36.promises.readFile(target, "utf8");
|
|
15848
16577
|
return JSON.parse(raw);
|
|
15849
16578
|
}
|
|
15850
16579
|
function indexEntries(entries) {
|
|
@@ -15912,23 +16641,23 @@ function canonicalJson(value) {
|
|
|
15912
16641
|
|
|
15913
16642
|
// src/projects.ts
|
|
15914
16643
|
init_cjs_shims();
|
|
15915
|
-
var
|
|
16644
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
15916
16645
|
function pathsForProject(project, baseDir) {
|
|
15917
16646
|
if (project === DEFAULT_PROJECT) {
|
|
15918
16647
|
return {
|
|
15919
|
-
snapshotPath:
|
|
15920
|
-
errorsPath:
|
|
15921
|
-
staleEventsPath:
|
|
15922
|
-
embeddingsCachePath:
|
|
15923
|
-
policyViolationsPath:
|
|
16648
|
+
snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
|
|
16649
|
+
errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
|
|
16650
|
+
staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
|
|
16651
|
+
embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
|
|
16652
|
+
policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
|
|
15924
16653
|
};
|
|
15925
16654
|
}
|
|
15926
16655
|
return {
|
|
15927
|
-
snapshotPath:
|
|
15928
|
-
errorsPath:
|
|
15929
|
-
staleEventsPath:
|
|
15930
|
-
embeddingsCachePath:
|
|
15931
|
-
policyViolationsPath:
|
|
16656
|
+
snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
|
|
16657
|
+
errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
|
|
16658
|
+
staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
16659
|
+
embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
|
|
16660
|
+
policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
15932
16661
|
};
|
|
15933
16662
|
}
|
|
15934
16663
|
var Projects = class {
|
|
@@ -15964,28 +16693,28 @@ var Projects = class {
|
|
|
15964
16693
|
|
|
15965
16694
|
// src/registry.ts
|
|
15966
16695
|
init_cjs_shims();
|
|
15967
|
-
var
|
|
16696
|
+
var import_node_fs37 = require("fs");
|
|
15968
16697
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
15969
|
-
var
|
|
15970
|
-
var
|
|
16698
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
16699
|
+
var import_types59 = require("@neat.is/types");
|
|
15971
16700
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
15972
16701
|
var LOCK_RETRY_MS = 50;
|
|
15973
16702
|
function neatHome() {
|
|
15974
16703
|
const override = process.env.NEAT_HOME;
|
|
15975
|
-
if (override && override.length > 0) return
|
|
15976
|
-
return
|
|
16704
|
+
if (override && override.length > 0) return import_node_path72.default.resolve(override);
|
|
16705
|
+
return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
|
|
15977
16706
|
}
|
|
15978
16707
|
function registryPath() {
|
|
15979
|
-
return
|
|
16708
|
+
return import_node_path72.default.join(neatHome(), "projects.json");
|
|
15980
16709
|
}
|
|
15981
16710
|
function registryLockPath() {
|
|
15982
|
-
return
|
|
16711
|
+
return import_node_path72.default.join(neatHome(), "projects.json.lock");
|
|
15983
16712
|
}
|
|
15984
16713
|
function daemonPidPath() {
|
|
15985
|
-
return
|
|
16714
|
+
return import_node_path72.default.join(neatHome(), "neatd.pid");
|
|
15986
16715
|
}
|
|
15987
16716
|
function daemonsDir() {
|
|
15988
|
-
return
|
|
16717
|
+
return import_node_path72.default.join(neatHome(), "daemons");
|
|
15989
16718
|
}
|
|
15990
16719
|
function isFiniteInt(v) {
|
|
15991
16720
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -16018,7 +16747,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
16018
16747
|
const dir = daemonsDir();
|
|
16019
16748
|
let names;
|
|
16020
16749
|
try {
|
|
16021
|
-
names = await
|
|
16750
|
+
names = await import_node_fs37.promises.readdir(dir);
|
|
16022
16751
|
} catch (err) {
|
|
16023
16752
|
if (err.code === "ENOENT") return [];
|
|
16024
16753
|
throw err;
|
|
@@ -16026,10 +16755,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
16026
16755
|
const out = [];
|
|
16027
16756
|
for (const name of names) {
|
|
16028
16757
|
if (!name.endsWith(".json")) continue;
|
|
16029
|
-
const file =
|
|
16758
|
+
const file = import_node_path72.default.join(dir, name);
|
|
16030
16759
|
let raw;
|
|
16031
16760
|
try {
|
|
16032
|
-
raw = await
|
|
16761
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16033
16762
|
} catch {
|
|
16034
16763
|
continue;
|
|
16035
16764
|
}
|
|
@@ -16055,7 +16784,7 @@ function isPidAliveDefault(pid) {
|
|
|
16055
16784
|
}
|
|
16056
16785
|
async function readPidFile(file) {
|
|
16057
16786
|
try {
|
|
16058
|
-
const raw = await
|
|
16787
|
+
const raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16059
16788
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
16060
16789
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
16061
16790
|
} catch {
|
|
@@ -16103,32 +16832,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
16103
16832
|
}
|
|
16104
16833
|
}
|
|
16105
16834
|
async function normalizeProjectPath(input) {
|
|
16106
|
-
const resolved =
|
|
16835
|
+
const resolved = import_node_path72.default.resolve(input);
|
|
16107
16836
|
try {
|
|
16108
|
-
return await
|
|
16837
|
+
return await import_node_fs37.promises.realpath(resolved);
|
|
16109
16838
|
} catch {
|
|
16110
16839
|
return resolved;
|
|
16111
16840
|
}
|
|
16112
16841
|
}
|
|
16113
16842
|
async function writeAtomically(target, contents) {
|
|
16114
|
-
await
|
|
16843
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
|
|
16115
16844
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
16116
|
-
const fd = await
|
|
16845
|
+
const fd = await import_node_fs37.promises.open(tmp, "w");
|
|
16117
16846
|
try {
|
|
16118
16847
|
await fd.writeFile(contents, "utf8");
|
|
16119
16848
|
await fd.sync();
|
|
16120
16849
|
} finally {
|
|
16121
16850
|
await fd.close();
|
|
16122
16851
|
}
|
|
16123
|
-
await
|
|
16852
|
+
await import_node_fs37.promises.rename(tmp, target);
|
|
16124
16853
|
}
|
|
16125
16854
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
16126
16855
|
const deadline = Date.now() + timeoutMs;
|
|
16127
|
-
await
|
|
16856
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
|
|
16128
16857
|
let probedHolder = false;
|
|
16129
16858
|
while (true) {
|
|
16130
16859
|
try {
|
|
16131
|
-
const fd = await
|
|
16860
|
+
const fd = await import_node_fs37.promises.open(lockPath, "wx");
|
|
16132
16861
|
try {
|
|
16133
16862
|
await fd.writeFile(`${process.pid}
|
|
16134
16863
|
`, "utf8");
|
|
@@ -16153,7 +16882,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
16153
16882
|
}
|
|
16154
16883
|
}
|
|
16155
16884
|
async function releaseLock(lockPath) {
|
|
16156
|
-
await
|
|
16885
|
+
await import_node_fs37.promises.unlink(lockPath).catch(() => {
|
|
16157
16886
|
});
|
|
16158
16887
|
}
|
|
16159
16888
|
async function withLock(fn) {
|
|
@@ -16169,7 +16898,7 @@ async function readRegistry() {
|
|
|
16169
16898
|
const file = registryPath();
|
|
16170
16899
|
let raw;
|
|
16171
16900
|
try {
|
|
16172
|
-
raw = await
|
|
16901
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16173
16902
|
} catch (err) {
|
|
16174
16903
|
if (err.code === "ENOENT") {
|
|
16175
16904
|
return { version: 1, projects: [] };
|
|
@@ -16177,10 +16906,10 @@ async function readRegistry() {
|
|
|
16177
16906
|
throw err;
|
|
16178
16907
|
}
|
|
16179
16908
|
const parsed = JSON.parse(raw);
|
|
16180
|
-
return
|
|
16909
|
+
return import_types59.RegistryFileSchema.parse(parsed);
|
|
16181
16910
|
}
|
|
16182
16911
|
async function writeRegistry(reg) {
|
|
16183
|
-
const validated =
|
|
16912
|
+
const validated = import_types59.RegistryFileSchema.parse(reg);
|
|
16184
16913
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16185
16914
|
}
|
|
16186
16915
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -16274,7 +17003,7 @@ function pruneTtlMs() {
|
|
|
16274
17003
|
}
|
|
16275
17004
|
async function statPathStatus(p) {
|
|
16276
17005
|
try {
|
|
16277
|
-
const stat = await
|
|
17006
|
+
const stat = await import_node_fs37.promises.stat(p);
|
|
16278
17007
|
return stat.isDirectory() ? "present" : "unknown";
|
|
16279
17008
|
} catch (err) {
|
|
16280
17009
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -16375,8 +17104,8 @@ init_auth();
|
|
|
16375
17104
|
// src/connectors-config.ts
|
|
16376
17105
|
init_cjs_shims();
|
|
16377
17106
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
16378
|
-
var
|
|
16379
|
-
var
|
|
17107
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
17108
|
+
var import_node_fs38 = require("fs");
|
|
16380
17109
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
16381
17110
|
var EnvRefUnsetError = class extends Error {
|
|
16382
17111
|
ref;
|
|
@@ -16390,17 +17119,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
16390
17119
|
};
|
|
16391
17120
|
function neatHome2() {
|
|
16392
17121
|
const override = process.env.NEAT_HOME;
|
|
16393
|
-
if (override && override.length > 0) return
|
|
16394
|
-
return
|
|
17122
|
+
if (override && override.length > 0) return import_node_path73.default.resolve(override);
|
|
17123
|
+
return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
|
|
16395
17124
|
}
|
|
16396
17125
|
function connectorsConfigPath(home = neatHome2()) {
|
|
16397
|
-
return
|
|
17126
|
+
return import_node_path73.default.join(home, "connectors.json");
|
|
16398
17127
|
}
|
|
16399
17128
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
16400
17129
|
async function warnIfModeLooserThan0600(file) {
|
|
16401
17130
|
if (process.platform === "win32") return;
|
|
16402
17131
|
try {
|
|
16403
|
-
const stat = await
|
|
17132
|
+
const stat = await import_node_fs38.promises.stat(file);
|
|
16404
17133
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
16405
17134
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
16406
17135
|
console.warn(
|
|
@@ -16414,7 +17143,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
16414
17143
|
const file = connectorsConfigPath(home);
|
|
16415
17144
|
let raw;
|
|
16416
17145
|
try {
|
|
16417
|
-
raw = await
|
|
17146
|
+
raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
16418
17147
|
} catch (err) {
|
|
16419
17148
|
if (err.code === "ENOENT") {
|
|
16420
17149
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -16581,15 +17310,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
16581
17310
|
|
|
16582
17311
|
// src/connectors/index.ts
|
|
16583
17312
|
init_cjs_shims();
|
|
16584
|
-
var
|
|
17313
|
+
var import_types60 = require("@neat.is/types");
|
|
16585
17314
|
var NO_ENV = "unknown";
|
|
16586
17315
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
16587
17316
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16588
17317
|
const sites = [];
|
|
16589
17318
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
16590
17319
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16591
|
-
if (edge.provenance !==
|
|
16592
|
-
const parsed = (0,
|
|
17320
|
+
if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
|
|
17321
|
+
const parsed = (0, import_types60.parseFileId)(edge.source);
|
|
16593
17322
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
16594
17323
|
const site = { relPath: edge.evidence.file };
|
|
16595
17324
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -16600,7 +17329,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
16600
17329
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
16601
17330
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16602
17331
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
16603
|
-
if (attrs.type !==
|
|
17332
|
+
if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
|
|
16604
17333
|
const site = { relPath: attrs.path };
|
|
16605
17334
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
16606
17335
|
return site;
|
|
@@ -17098,10 +17827,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
17098
17827
|
// src/connectors/supabase/map.ts
|
|
17099
17828
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
17100
17829
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
17101
|
-
function targetFromRestPath(
|
|
17102
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
17830
|
+
function targetFromRestPath(path76) {
|
|
17831
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path76);
|
|
17103
17832
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
17104
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
17833
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path76);
|
|
17105
17834
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
17106
17835
|
return null;
|
|
17107
17836
|
}
|
|
@@ -17212,23 +17941,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17212
17941
|
|
|
17213
17942
|
// src/connectors/supabase/resolve.ts
|
|
17214
17943
|
init_cjs_shims();
|
|
17215
|
-
var
|
|
17944
|
+
var import_types62 = require("@neat.is/types");
|
|
17216
17945
|
function createSupabaseResolveTarget(graph, config) {
|
|
17217
17946
|
return (signal, _ctx) => {
|
|
17218
17947
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17219
17948
|
return null;
|
|
17220
17949
|
}
|
|
17221
|
-
const subResourceId = (0,
|
|
17950
|
+
const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17222
17951
|
if (graph.hasNode(subResourceId)) {
|
|
17223
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
17952
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17224
17953
|
}
|
|
17225
|
-
const bareResourceId = (0,
|
|
17954
|
+
const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
|
|
17226
17955
|
if (graph.hasNode(bareResourceId)) {
|
|
17227
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
17956
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17228
17957
|
}
|
|
17229
|
-
const projectLevelId = (0,
|
|
17958
|
+
const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
|
|
17230
17959
|
if (graph.hasNode(projectLevelId)) {
|
|
17231
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
17960
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17232
17961
|
}
|
|
17233
17962
|
return null;
|
|
17234
17963
|
};
|
|
@@ -17321,7 +18050,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
17321
18050
|
|
|
17322
18051
|
// src/connectors/railway/index.ts
|
|
17323
18052
|
init_cjs_shims();
|
|
17324
|
-
var
|
|
18053
|
+
var import_types66 = require("@neat.is/types");
|
|
17325
18054
|
|
|
17326
18055
|
// src/connectors/railway/client.ts
|
|
17327
18056
|
init_cjs_shims();
|
|
@@ -17472,7 +18201,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
17472
18201
|
const out = [];
|
|
17473
18202
|
graph.forEachNode((_id, attrs) => {
|
|
17474
18203
|
const node = attrs;
|
|
17475
|
-
if (node.type !==
|
|
18204
|
+
if (node.type !== import_types66.NodeType.RouteNode) return;
|
|
17476
18205
|
const route = attrs;
|
|
17477
18206
|
if (route.service !== serviceName) return;
|
|
17478
18207
|
out.push({
|
|
@@ -17576,12 +18305,12 @@ function createRailwayResolveTarget(config) {
|
|
|
17576
18305
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
17577
18306
|
if (!serviceName) return null;
|
|
17578
18307
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
17579
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18308
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
17580
18309
|
}
|
|
17581
18310
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
17582
18311
|
const peerName = config.serviceNameById[signal.targetName];
|
|
17583
18312
|
if (!peerName) return null;
|
|
17584
|
-
return { targetNodeId: (0,
|
|
18313
|
+
return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
|
|
17585
18314
|
}
|
|
17586
18315
|
return null;
|
|
17587
18316
|
};
|
|
@@ -17705,9 +18434,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
17705
18434
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
17706
18435
|
if (secondSep === -1) return null;
|
|
17707
18436
|
const method = rest.slice(0, secondSep);
|
|
17708
|
-
const
|
|
17709
|
-
if (!resourceName || !method || !
|
|
17710
|
-
return { resourceName, method, path:
|
|
18437
|
+
const path76 = rest.slice(secondSep + 1);
|
|
18438
|
+
if (!resourceName || !method || !path76) return null;
|
|
18439
|
+
return { resourceName, method, path: path76 };
|
|
17711
18440
|
}
|
|
17712
18441
|
function resourceNameFor(type, labels) {
|
|
17713
18442
|
if (!labels) return null;
|
|
@@ -17745,14 +18474,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
17745
18474
|
if (!req) return null;
|
|
17746
18475
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
17747
18476
|
const method = req.requestMethod.toUpperCase();
|
|
17748
|
-
const
|
|
17749
|
-
if (
|
|
18477
|
+
const path76 = pathFromRequestUrl(req.requestUrl);
|
|
18478
|
+
if (path76 === null) return null;
|
|
17750
18479
|
const timestamp = entry.timestamp;
|
|
17751
18480
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
17752
18481
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
17753
18482
|
return {
|
|
17754
18483
|
targetKind: resourceType,
|
|
17755
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
18484
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
|
|
17756
18485
|
callCount: 1,
|
|
17757
18486
|
errorCount: isError ? 1 : 0,
|
|
17758
18487
|
lastObservedIso: timestamp
|
|
@@ -17769,7 +18498,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
17769
18498
|
|
|
17770
18499
|
// src/connectors/firebase/resolve.ts
|
|
17771
18500
|
init_cjs_shims();
|
|
17772
|
-
var
|
|
18501
|
+
var import_types67 = require("@neat.is/types");
|
|
17773
18502
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
17774
18503
|
switch (resourceType) {
|
|
17775
18504
|
case "cloud_function":
|
|
@@ -17784,7 +18513,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
17784
18513
|
const entries = [];
|
|
17785
18514
|
graph.forEachNode((_id, attrs) => {
|
|
17786
18515
|
const node = attrs;
|
|
17787
|
-
if (node.type !==
|
|
18516
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
17788
18517
|
const route = attrs;
|
|
17789
18518
|
if (route.service !== serviceName) return;
|
|
17790
18519
|
entries.push({
|
|
@@ -17816,7 +18545,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
17816
18545
|
return {
|
|
17817
18546
|
targetNodeId: match.routeNodeId,
|
|
17818
18547
|
serviceName,
|
|
17819
|
-
edgeType:
|
|
18548
|
+
edgeType: import_types67.EdgeType.CALLS
|
|
17820
18549
|
};
|
|
17821
18550
|
};
|
|
17822
18551
|
}
|
|
@@ -17843,7 +18572,7 @@ init_cjs_shims();
|
|
|
17843
18572
|
|
|
17844
18573
|
// src/connectors/cloudflare/connector.ts
|
|
17845
18574
|
init_cjs_shims();
|
|
17846
|
-
var
|
|
18575
|
+
var import_types69 = require("@neat.is/types");
|
|
17847
18576
|
|
|
17848
18577
|
// src/connectors/cloudflare/client.ts
|
|
17849
18578
|
init_cjs_shims();
|
|
@@ -17959,7 +18688,7 @@ function mapEventToSignal(event) {
|
|
|
17959
18688
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
17960
18689
|
const statusCode = metadata?.statusCode;
|
|
17961
18690
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
17962
|
-
const
|
|
18691
|
+
const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
17963
18692
|
return {
|
|
17964
18693
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
17965
18694
|
targetName: scriptName,
|
|
@@ -17967,7 +18696,7 @@ function mapEventToSignal(event) {
|
|
|
17967
18696
|
errorCount: isError ? 1 : 0,
|
|
17968
18697
|
lastObservedIso: observedAt.toISOString(),
|
|
17969
18698
|
method,
|
|
17970
|
-
...
|
|
18699
|
+
...path76 ? { path: path76 } : {},
|
|
17971
18700
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
17972
18701
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
17973
18702
|
};
|
|
@@ -18007,19 +18736,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
18007
18736
|
graph.forEachNode((id, attrs) => {
|
|
18008
18737
|
if (found) return;
|
|
18009
18738
|
const a = attrs;
|
|
18010
|
-
if (a.type ===
|
|
18739
|
+
if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
18011
18740
|
found = id;
|
|
18012
18741
|
}
|
|
18013
18742
|
});
|
|
18014
18743
|
return found;
|
|
18015
18744
|
}
|
|
18016
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
18017
|
-
const normalizedPath = normalizePathTemplate(
|
|
18745
|
+
function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
18746
|
+
const normalizedPath = normalizePathTemplate(path76);
|
|
18018
18747
|
let found = null;
|
|
18019
18748
|
graph.forEachNode((id, attrs) => {
|
|
18020
18749
|
if (found) return;
|
|
18021
18750
|
const a = attrs;
|
|
18022
|
-
if (a.type !==
|
|
18751
|
+
if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
|
|
18023
18752
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
18024
18753
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
18025
18754
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -18031,18 +18760,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18031
18760
|
return (signal) => {
|
|
18032
18761
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
18033
18762
|
const scriptName = signal.targetName;
|
|
18034
|
-
const { method, path:
|
|
18763
|
+
const { method, path: path76 } = signal;
|
|
18035
18764
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
18036
|
-
if (!method || !
|
|
18037
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
18765
|
+
if (!method || !path76) return wholeFileId;
|
|
18766
|
+
return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
|
|
18038
18767
|
};
|
|
18039
18768
|
const mapping = config.workers?.[scriptName];
|
|
18040
18769
|
if (mapping) {
|
|
18041
|
-
const wholeFileId = (0,
|
|
18770
|
+
const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
|
|
18042
18771
|
return {
|
|
18043
18772
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
18044
18773
|
serviceName: mapping.service,
|
|
18045
|
-
edgeType:
|
|
18774
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
18046
18775
|
};
|
|
18047
18776
|
}
|
|
18048
18777
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -18051,13 +18780,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18051
18780
|
return {
|
|
18052
18781
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
18053
18782
|
serviceName: fileNode.service,
|
|
18054
|
-
edgeType:
|
|
18783
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
18055
18784
|
};
|
|
18056
18785
|
}
|
|
18057
18786
|
return {
|
|
18058
|
-
targetNodeId: (0,
|
|
18787
|
+
targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
|
|
18059
18788
|
serviceName: scriptName,
|
|
18060
|
-
edgeType:
|
|
18789
|
+
edgeType: import_types69.EdgeType.CALLS,
|
|
18061
18790
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
18062
18791
|
};
|
|
18063
18792
|
};
|
|
@@ -18253,14 +18982,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18253
18982
|
|
|
18254
18983
|
// src/connectors/neon/resolve.ts
|
|
18255
18984
|
init_cjs_shims();
|
|
18256
|
-
var
|
|
18985
|
+
var import_types73 = require("@neat.is/types");
|
|
18257
18986
|
function createNeonResolveTarget(config) {
|
|
18258
18987
|
return (signal) => {
|
|
18259
18988
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18260
18989
|
return {
|
|
18261
|
-
targetNodeId: (0,
|
|
18990
|
+
targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
|
|
18262
18991
|
serviceName: config.serviceName,
|
|
18263
|
-
edgeType:
|
|
18992
|
+
edgeType: import_types73.EdgeType.CALLS,
|
|
18264
18993
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18265
18994
|
};
|
|
18266
18995
|
};
|
|
@@ -18386,9 +19115,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
18386
19115
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
18387
19116
|
if (secondSep === -1) return null;
|
|
18388
19117
|
const method = rest.slice(0, secondSep);
|
|
18389
|
-
const
|
|
18390
|
-
if (!serviceName || !method || !
|
|
18391
|
-
return { serviceName, method, path:
|
|
19118
|
+
const path76 = rest.slice(secondSep + 1);
|
|
19119
|
+
if (!serviceName || !method || !path76) return null;
|
|
19120
|
+
return { serviceName, method, path: path76 };
|
|
18392
19121
|
}
|
|
18393
19122
|
|
|
18394
19123
|
// src/connectors/cloud-run/map.ts
|
|
@@ -18417,14 +19146,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
18417
19146
|
if (!req) return null;
|
|
18418
19147
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
18419
19148
|
const method = req.requestMethod.toUpperCase();
|
|
18420
|
-
const
|
|
18421
|
-
if (
|
|
19149
|
+
const path76 = pathFromRequestUrl2(req.requestUrl);
|
|
19150
|
+
if (path76 === null) return null;
|
|
18422
19151
|
const timestamp = entry.timestamp;
|
|
18423
19152
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
18424
19153
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
18425
19154
|
return {
|
|
18426
19155
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
18427
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
19156
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
|
|
18428
19157
|
callCount: 1,
|
|
18429
19158
|
errorCount: isError ? 1 : 0,
|
|
18430
19159
|
lastObservedIso: timestamp
|
|
@@ -18441,14 +19170,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
18441
19170
|
|
|
18442
19171
|
// src/connectors/cloud-run/resolve.ts
|
|
18443
19172
|
init_cjs_shims();
|
|
18444
|
-
var
|
|
19173
|
+
var import_types77 = require("@neat.is/types");
|
|
18445
19174
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
18446
19175
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
18447
19176
|
let found = null;
|
|
18448
19177
|
graph.forEachNode((_id, attrs) => {
|
|
18449
19178
|
if (found) return;
|
|
18450
19179
|
const node = attrs;
|
|
18451
|
-
if (node.type !==
|
|
19180
|
+
if (node.type !== import_types77.NodeType.RouteNode) return;
|
|
18452
19181
|
const route = attrs;
|
|
18453
19182
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
18454
19183
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -18463,23 +19192,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
18463
19192
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
18464
19193
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
18465
19194
|
if (!identity) return null;
|
|
18466
|
-
const { serviceName: gcpServiceName, method, path:
|
|
19195
|
+
const { serviceName: gcpServiceName, method, path: path76 } = identity;
|
|
18467
19196
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
18468
19197
|
if (mappedService) {
|
|
18469
19198
|
const routeNodeId = findMatchingRouteNode2(
|
|
18470
19199
|
graph,
|
|
18471
19200
|
mappedService,
|
|
18472
19201
|
method,
|
|
18473
|
-
normalizePathTemplate(
|
|
19202
|
+
normalizePathTemplate(path76)
|
|
18474
19203
|
);
|
|
18475
19204
|
if (routeNodeId) {
|
|
18476
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19205
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
|
|
18477
19206
|
}
|
|
18478
19207
|
}
|
|
18479
19208
|
return {
|
|
18480
|
-
targetNodeId: (0,
|
|
19209
|
+
targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
18481
19210
|
serviceName: mappedService ?? gcpServiceName,
|
|
18482
|
-
edgeType:
|
|
19211
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
18483
19212
|
ensureInfraNode: {
|
|
18484
19213
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
18485
19214
|
name: gcpServiceName,
|
|
@@ -18520,7 +19249,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
18520
19249
|
|
|
18521
19250
|
// src/connectors/render/index.ts
|
|
18522
19251
|
init_cjs_shims();
|
|
18523
|
-
var
|
|
19252
|
+
var import_types80 = require("@neat.is/types");
|
|
18524
19253
|
|
|
18525
19254
|
// src/connectors/render/types.ts
|
|
18526
19255
|
init_cjs_shims();
|
|
@@ -18598,7 +19327,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
18598
19327
|
const out = [];
|
|
18599
19328
|
graph.forEachNode((_id, attrs) => {
|
|
18600
19329
|
const node = attrs;
|
|
18601
|
-
if (node.type !==
|
|
19330
|
+
if (node.type !== import_types80.NodeType.RouteNode) return;
|
|
18602
19331
|
const route = attrs;
|
|
18603
19332
|
if (route.service !== serviceName) return;
|
|
18604
19333
|
out.push({
|
|
@@ -18683,7 +19412,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
18683
19412
|
function createRenderResolveTarget(config) {
|
|
18684
19413
|
return (signal) => {
|
|
18685
19414
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
18686
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
19415
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
|
|
18687
19416
|
}
|
|
18688
19417
|
return null;
|
|
18689
19418
|
};
|
|
@@ -18821,21 +19550,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
18821
19550
|
|
|
18822
19551
|
// src/connectors/planetscale/resolve.ts
|
|
18823
19552
|
init_cjs_shims();
|
|
18824
|
-
var
|
|
19553
|
+
var import_types84 = require("@neat.is/types");
|
|
18825
19554
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
18826
19555
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
18827
19556
|
const databaseName = `${config.organization}/${config.database}`;
|
|
18828
19557
|
return (signal, _ctx) => {
|
|
18829
19558
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18830
|
-
const tableId = (0,
|
|
19559
|
+
const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
|
|
18831
19560
|
if (graph.hasNode(tableId)) {
|
|
18832
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
19561
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
|
|
18833
19562
|
}
|
|
18834
|
-
const providerId = (0,
|
|
19563
|
+
const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
18835
19564
|
return {
|
|
18836
19565
|
targetNodeId: providerId,
|
|
18837
19566
|
serviceName: config.serviceName,
|
|
18838
|
-
edgeType:
|
|
19567
|
+
edgeType: import_types84.EdgeType.CALLS,
|
|
18839
19568
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
18840
19569
|
};
|
|
18841
19570
|
};
|
|
@@ -19100,7 +19829,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19100
19829
|
|
|
19101
19830
|
// src/connectors/eas/resolve.ts
|
|
19102
19831
|
init_cjs_shims();
|
|
19103
|
-
var
|
|
19832
|
+
var import_types89 = require("@neat.is/types");
|
|
19104
19833
|
var NO_ENV2 = "unknown";
|
|
19105
19834
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19106
19835
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19116,8 +19845,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19116
19845
|
function configNodeService(graph, configNodeId) {
|
|
19117
19846
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19118
19847
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19119
|
-
if (edge.type !==
|
|
19120
|
-
const parsed = (0,
|
|
19848
|
+
if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
|
|
19849
|
+
const parsed = (0, import_types89.parseFileId)(edge.source);
|
|
19121
19850
|
if (parsed) return parsed.service;
|
|
19122
19851
|
}
|
|
19123
19852
|
return null;
|
|
@@ -19128,7 +19857,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19128
19857
|
graph.forEachNode((id, attrs) => {
|
|
19129
19858
|
if (scoped) return;
|
|
19130
19859
|
const node = attrs;
|
|
19131
|
-
if (node.type !==
|
|
19860
|
+
if (node.type !== import_types89.NodeType.ConfigNode) return;
|
|
19132
19861
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19133
19862
|
if (anyMatch === null) anyMatch = id;
|
|
19134
19863
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19145,13 +19874,13 @@ function createEasResolveTarget(graph) {
|
|
|
19145
19874
|
if (basenames.length > 0) {
|
|
19146
19875
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19147
19876
|
if (configNodeId) {
|
|
19148
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
19877
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
|
|
19149
19878
|
}
|
|
19150
19879
|
}
|
|
19151
19880
|
return {
|
|
19152
19881
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19153
19882
|
serviceName,
|
|
19154
|
-
edgeType:
|
|
19883
|
+
edgeType: import_types89.EdgeType.CALLS
|
|
19155
19884
|
};
|
|
19156
19885
|
};
|
|
19157
19886
|
}
|
|
@@ -19876,11 +20605,11 @@ function registerRoutes(scope, ctx) {
|
|
|
19876
20605
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
19877
20606
|
const parsed = [];
|
|
19878
20607
|
for (const c of candidates) {
|
|
19879
|
-
const r =
|
|
20608
|
+
const r = import_types92.DivergenceTypeSchema.safeParse(c);
|
|
19880
20609
|
if (!r.success) {
|
|
19881
20610
|
return reply.code(400).send({
|
|
19882
20611
|
error: `unknown divergence type "${c}"`,
|
|
19883
|
-
allowed:
|
|
20612
|
+
allowed: import_types92.DivergenceTypeSchema.options
|
|
19884
20613
|
});
|
|
19885
20614
|
}
|
|
19886
20615
|
parsed.push(r.data);
|
|
@@ -20222,7 +20951,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20222
20951
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20223
20952
|
let violations = await log.readAll();
|
|
20224
20953
|
if (req.query.severity) {
|
|
20225
|
-
const sev =
|
|
20954
|
+
const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
|
|
20226
20955
|
if (!sev.success) {
|
|
20227
20956
|
return reply.code(400).send({
|
|
20228
20957
|
error: "invalid severity",
|
|
@@ -20261,7 +20990,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20261
20990
|
scope.post("/policies/check", async (req, reply) => {
|
|
20262
20991
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
20263
20992
|
if (!proj) return;
|
|
20264
|
-
const parsed =
|
|
20993
|
+
const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
20265
20994
|
if (!parsed.success) {
|
|
20266
20995
|
return reply.code(400).send({
|
|
20267
20996
|
error: "invalid /policies/check body",
|
|
@@ -20582,16 +21311,16 @@ init_otel_grpc();
|
|
|
20582
21311
|
|
|
20583
21312
|
// src/daemon.ts
|
|
20584
21313
|
init_cjs_shims();
|
|
20585
|
-
var
|
|
20586
|
-
var
|
|
21314
|
+
var import_node_fs40 = require("fs");
|
|
21315
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
20587
21316
|
var import_node_module = require("module");
|
|
20588
21317
|
init_otel();
|
|
20589
21318
|
init_auth();
|
|
20590
21319
|
|
|
20591
21320
|
// src/unrouted.ts
|
|
20592
21321
|
init_cjs_shims();
|
|
20593
|
-
var
|
|
20594
|
-
var
|
|
21322
|
+
var import_node_fs39 = require("fs");
|
|
21323
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
20595
21324
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
20596
21325
|
return {
|
|
20597
21326
|
timestamp: now.toISOString(),
|
|
@@ -20601,34 +21330,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
20601
21330
|
};
|
|
20602
21331
|
}
|
|
20603
21332
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
20604
|
-
const target =
|
|
20605
|
-
await
|
|
20606
|
-
await
|
|
21333
|
+
const target = import_node_path74.default.join(neatHome3, "errors.ndjson");
|
|
21334
|
+
await import_node_fs39.promises.mkdir(neatHome3, { recursive: true });
|
|
21335
|
+
await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
20607
21336
|
}
|
|
20608
21337
|
function unroutedErrorsPath(neatHome3) {
|
|
20609
|
-
return
|
|
21338
|
+
return import_node_path74.default.join(neatHome3, "errors.ndjson");
|
|
20610
21339
|
}
|
|
20611
21340
|
|
|
20612
21341
|
// src/daemon.ts
|
|
20613
|
-
var
|
|
21342
|
+
var import_types93 = require("@neat.is/types");
|
|
20614
21343
|
function daemonJsonPath(scanPath) {
|
|
20615
|
-
return
|
|
21344
|
+
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
20616
21345
|
}
|
|
20617
21346
|
function daemonsDiscoveryDir(home) {
|
|
20618
21347
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
20619
|
-
return
|
|
21348
|
+
return import_node_path75.default.join(base, "daemons");
|
|
20620
21349
|
}
|
|
20621
21350
|
function daemonDiscoveryPath(project, home) {
|
|
20622
|
-
return
|
|
21351
|
+
return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
20623
21352
|
}
|
|
20624
21353
|
function sanitizeDiscoveryName(project) {
|
|
20625
21354
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
20626
21355
|
}
|
|
20627
21356
|
function neatHomeFromEnv() {
|
|
20628
21357
|
const env = process.env.NEAT_HOME;
|
|
20629
|
-
if (env && env.length > 0) return
|
|
21358
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20630
21359
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20631
|
-
return
|
|
21360
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20632
21361
|
}
|
|
20633
21362
|
function resolveNeatVersion() {
|
|
20634
21363
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -20660,7 +21389,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
20660
21389
|
} catch {
|
|
20661
21390
|
}
|
|
20662
21391
|
try {
|
|
20663
|
-
await
|
|
21392
|
+
await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
20664
21393
|
} catch {
|
|
20665
21394
|
}
|
|
20666
21395
|
}
|
|
@@ -20683,11 +21412,11 @@ function teardownSlot(slot) {
|
|
|
20683
21412
|
}
|
|
20684
21413
|
}
|
|
20685
21414
|
function neatHomeFor(opts) {
|
|
20686
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
21415
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
|
|
20687
21416
|
const env = process.env.NEAT_HOME;
|
|
20688
|
-
if (env && env.length > 0) return
|
|
21417
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20689
21418
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20690
|
-
return
|
|
21419
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20691
21420
|
}
|
|
20692
21421
|
function routeSpanToProject(serviceName, projects) {
|
|
20693
21422
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -20735,13 +21464,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
20735
21464
|
if (!serviceName) return true;
|
|
20736
21465
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
20737
21466
|
return graph.someNode(
|
|
20738
|
-
(_id, attrs) => attrs.type ===
|
|
21467
|
+
(_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
|
|
20739
21468
|
);
|
|
20740
21469
|
}
|
|
20741
21470
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
20742
|
-
const paths = pathsForProject(entry.name,
|
|
21471
|
+
const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
|
|
20743
21472
|
try {
|
|
20744
|
-
const stat = await
|
|
21473
|
+
const stat = await import_node_fs40.promises.stat(entry.path);
|
|
20745
21474
|
if (!stat.isDirectory()) {
|
|
20746
21475
|
throw new Error(`registered path ${entry.path} is not a directory`);
|
|
20747
21476
|
}
|
|
@@ -20859,7 +21588,7 @@ async function startDaemon(opts = {}) {
|
|
|
20859
21588
|
const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
|
|
20860
21589
|
const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
|
|
20861
21590
|
const singleProject = projectArg;
|
|
20862
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
21591
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
|
|
20863
21592
|
if (singleProject && !singleProjectPath) {
|
|
20864
21593
|
throw new Error(
|
|
20865
21594
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -20867,14 +21596,14 @@ async function startDaemon(opts = {}) {
|
|
|
20867
21596
|
}
|
|
20868
21597
|
if (!singleProject) {
|
|
20869
21598
|
try {
|
|
20870
|
-
await
|
|
21599
|
+
await import_node_fs40.promises.access(regPath);
|
|
20871
21600
|
} catch {
|
|
20872
21601
|
throw new Error(
|
|
20873
21602
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
20874
21603
|
);
|
|
20875
21604
|
}
|
|
20876
21605
|
}
|
|
20877
|
-
const pidPath =
|
|
21606
|
+
const pidPath = import_node_path75.default.join(home, "neatd.pid");
|
|
20878
21607
|
await writeAtomically(pidPath, `${process.pid}
|
|
20879
21608
|
`);
|
|
20880
21609
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -21086,7 +21815,7 @@ async function startDaemon(opts = {}) {
|
|
|
21086
21815
|
}
|
|
21087
21816
|
if (restApp) await restApp.close().catch(() => {
|
|
21088
21817
|
});
|
|
21089
|
-
await
|
|
21818
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21090
21819
|
});
|
|
21091
21820
|
throw new Error(
|
|
21092
21821
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -21254,7 +21983,7 @@ async function startDaemon(opts = {}) {
|
|
|
21254
21983
|
});
|
|
21255
21984
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21256
21985
|
});
|
|
21257
|
-
await
|
|
21986
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21258
21987
|
});
|
|
21259
21988
|
throw new Error(
|
|
21260
21989
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -21289,7 +22018,7 @@ async function startDaemon(opts = {}) {
|
|
|
21289
22018
|
});
|
|
21290
22019
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21291
22020
|
});
|
|
21292
|
-
await
|
|
22021
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21293
22022
|
});
|
|
21294
22023
|
throw new Error(
|
|
21295
22024
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -21336,9 +22065,9 @@ async function startDaemon(opts = {}) {
|
|
|
21336
22065
|
let registryWatcher = null;
|
|
21337
22066
|
let reloadTimer = null;
|
|
21338
22067
|
if (!singleProject) try {
|
|
21339
|
-
const regDir =
|
|
21340
|
-
const regBase =
|
|
21341
|
-
registryWatcher = (0,
|
|
22068
|
+
const regDir = import_node_path75.default.dirname(regPath);
|
|
22069
|
+
const regBase = import_node_path75.default.basename(regPath);
|
|
22070
|
+
registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
|
|
21342
22071
|
if (filename !== null && filename !== regBase) return;
|
|
21343
22072
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
21344
22073
|
reloadTimer = setTimeout(() => {
|
|
@@ -21387,7 +22116,7 @@ async function startDaemon(opts = {}) {
|
|
|
21387
22116
|
if (daemonRecord) {
|
|
21388
22117
|
await clearDaemonRecord(daemonRecord, home);
|
|
21389
22118
|
}
|
|
21390
|
-
await
|
|
22119
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21391
22120
|
});
|
|
21392
22121
|
};
|
|
21393
22122
|
return {
|