@neat.is/core 0.9.11 → 0.9.12
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-3UAUMPIY.js → chunk-RBZNXA5L.js} +591 -460
- package/dist/chunk-RBZNXA5L.js.map +1 -0
- package/dist/{chunk-UNO3X6ZV.js → chunk-WE3AFQYL.js} +468 -19
- package/dist/chunk-WE3AFQYL.js.map +1 -0
- package/dist/cli.cjs +1108 -951
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1229 -645
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +1245 -661
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +661 -534
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-3UAUMPIY.js.map +0 -1
- package/dist/chunk-UNO3X6ZV.js.map +0 -1
package/dist/neatd.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path80 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path80) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path80)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path53.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path53.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url,
|
|
266
|
+
var import_node_url, import_node_path53, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path53 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
415
415
|
const v = attrs[key];
|
|
416
416
|
if (typeof v === "string" && v.length > 0) {
|
|
417
417
|
const q = v.indexOf("?");
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
418
|
+
const path80 = q === -1 ? v : v.slice(0, q);
|
|
419
|
+
if (path80.length > 0) return path80;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
return void 0;
|
|
@@ -478,10 +478,10 @@ function parseOtlpRequest(body) {
|
|
|
478
478
|
return out;
|
|
479
479
|
}
|
|
480
480
|
function loadProtoRoot() {
|
|
481
|
-
const here =
|
|
482
|
-
const protoRoot =
|
|
481
|
+
const here = import_node_path54.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
482
|
+
const protoRoot = import_node_path54.default.resolve(here, "..", "proto");
|
|
483
483
|
const root = new import_protobufjs.default.Root();
|
|
484
|
-
root.resolvePath = (_origin, target) =>
|
|
484
|
+
root.resolvePath = (_origin, target) => import_node_path54.default.resolve(protoRoot, target);
|
|
485
485
|
root.loadSync(
|
|
486
486
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
487
487
|
{ keepCase: true }
|
|
@@ -759,12 +759,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
759
759
|
}
|
|
760
760
|
}
|
|
761
761
|
}
|
|
762
|
-
var
|
|
762
|
+
var import_node_path54, 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;
|
|
763
763
|
var init_otel = __esm({
|
|
764
764
|
"src/otel.ts"() {
|
|
765
765
|
"use strict";
|
|
766
766
|
init_cjs_shims();
|
|
767
|
-
|
|
767
|
+
import_node_path54 = __toESM(require("path"), 1);
|
|
768
768
|
import_node_url2 = require("url");
|
|
769
769
|
import_node_zlib = __toESM(require("zlib"), 1);
|
|
770
770
|
import_fastify = __toESM(require("fastify"), 1);
|
|
@@ -788,14 +788,14 @@ __export(neatd_exports, {
|
|
|
788
788
|
});
|
|
789
789
|
module.exports = __toCommonJS(neatd_exports);
|
|
790
790
|
init_cjs_shims();
|
|
791
|
-
var
|
|
792
|
-
var
|
|
791
|
+
var import_node_fs43 = require("fs");
|
|
792
|
+
var import_node_path79 = __toESM(require("path"), 1);
|
|
793
793
|
var import_node_module2 = require("module");
|
|
794
794
|
|
|
795
795
|
// src/daemon.ts
|
|
796
796
|
init_cjs_shims();
|
|
797
|
-
var
|
|
798
|
-
var
|
|
797
|
+
var import_node_fs41 = require("fs");
|
|
798
|
+
var import_node_path77 = __toESM(require("path"), 1);
|
|
799
799
|
var import_node_module = require("module");
|
|
800
800
|
|
|
801
801
|
// src/graph.ts
|
|
@@ -1328,19 +1328,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1328
1328
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1329
1329
|
let best = { path: [start], edges: [] };
|
|
1330
1330
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1331
|
-
function step(node,
|
|
1332
|
-
if (
|
|
1333
|
-
best = { path: [...
|
|
1331
|
+
function step(node, path80, edges) {
|
|
1332
|
+
if (path80.length > best.path.length) {
|
|
1333
|
+
best = { path: [...path80], edges: [...edges] };
|
|
1334
1334
|
}
|
|
1335
|
-
if (
|
|
1335
|
+
if (path80.length - 1 >= maxDepth) return;
|
|
1336
1336
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1337
1337
|
for (const [srcId, edge] of incoming) {
|
|
1338
1338
|
if (visited.has(srcId)) continue;
|
|
1339
1339
|
visited.add(srcId);
|
|
1340
|
-
|
|
1340
|
+
path80.push(srcId);
|
|
1341
1341
|
edges.push(edge);
|
|
1342
|
-
step(srcId,
|
|
1343
|
-
|
|
1342
|
+
step(srcId, path80, edges);
|
|
1343
|
+
path80.pop();
|
|
1344
1344
|
edges.pop();
|
|
1345
1345
|
visited.delete(srcId);
|
|
1346
1346
|
}
|
|
@@ -1348,14 +1348,14 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1348
1348
|
step(start, [start], []);
|
|
1349
1349
|
return best;
|
|
1350
1350
|
}
|
|
1351
|
-
function databaseRootCauseShape(graph, origin,
|
|
1351
|
+
function databaseRootCauseShape(graph, origin, walk11) {
|
|
1352
1352
|
const targetDb = origin;
|
|
1353
1353
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1354
1354
|
if (candidatePairs.length === 0) return null;
|
|
1355
|
-
for (const id of
|
|
1355
|
+
for (const id of walk11.path) {
|
|
1356
1356
|
const owner = resolveOwningService(graph, id);
|
|
1357
1357
|
if (!owner) continue;
|
|
1358
|
-
const { id:
|
|
1358
|
+
const { id: serviceId17, svc } = owner;
|
|
1359
1359
|
const deps = svc.dependencies ?? {};
|
|
1360
1360
|
for (const pair of candidatePairs) {
|
|
1361
1361
|
const declared = deps[pair.driver];
|
|
@@ -1368,7 +1368,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
|
|
|
1368
1368
|
);
|
|
1369
1369
|
if (!result.compatible) {
|
|
1370
1370
|
return {
|
|
1371
|
-
rootCauseNode:
|
|
1371
|
+
rootCauseNode: serviceId17,
|
|
1372
1372
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1373
1373
|
...result.minDriverVersion ? {
|
|
1374
1374
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1379,11 +1379,11 @@ function databaseRootCauseShape(graph, origin, walk10) {
|
|
|
1379
1379
|
}
|
|
1380
1380
|
return null;
|
|
1381
1381
|
}
|
|
1382
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1383
|
-
for (const id of
|
|
1382
|
+
function serviceRootCauseShape(graph, _origin, walk11) {
|
|
1383
|
+
for (const id of walk11.path) {
|
|
1384
1384
|
const owner = resolveOwningService(graph, id);
|
|
1385
1385
|
if (!owner) continue;
|
|
1386
|
-
const { id:
|
|
1386
|
+
const { id: serviceId17, svc } = owner;
|
|
1387
1387
|
const deps = svc.dependencies ?? {};
|
|
1388
1388
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1389
1389
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1392,7 +1392,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1392
1392
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1393
1393
|
if (!result.compatible && result.reason) {
|
|
1394
1394
|
return {
|
|
1395
|
-
rootCauseNode:
|
|
1395
|
+
rootCauseNode: serviceId17,
|
|
1396
1396
|
rootCauseReason: result.reason,
|
|
1397
1397
|
...result.requiredNodeVersion ? {
|
|
1398
1398
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1407,7 +1407,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1407
1407
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1408
1408
|
if (!result.compatible && result.reason) {
|
|
1409
1409
|
return {
|
|
1410
|
-
rootCauseNode:
|
|
1410
|
+
rootCauseNode: serviceId17,
|
|
1411
1411
|
rootCauseReason: result.reason,
|
|
1412
1412
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1413
1413
|
};
|
|
@@ -1416,15 +1416,15 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1416
1416
|
}
|
|
1417
1417
|
return null;
|
|
1418
1418
|
}
|
|
1419
|
-
function fileRootCauseShape(graph, origin,
|
|
1419
|
+
function fileRootCauseShape(graph, origin, walk11) {
|
|
1420
1420
|
const owner = resolveOwningService(graph, origin.id);
|
|
1421
1421
|
if (!owner) return null;
|
|
1422
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1422
|
+
return serviceRootCauseShape(graph, owner.svc, walk11);
|
|
1423
1423
|
}
|
|
1424
|
-
function symbolRootCauseShape(graph, origin,
|
|
1424
|
+
function symbolRootCauseShape(graph, origin, walk11) {
|
|
1425
1425
|
const owner = resolveOwningService(graph, origin.id);
|
|
1426
1426
|
if (!owner) return null;
|
|
1427
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1427
|
+
return serviceRootCauseShape(graph, owner.svc, walk11);
|
|
1428
1428
|
}
|
|
1429
1429
|
var rootCauseShapes = {
|
|
1430
1430
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1437,8 +1437,8 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1437
1437
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1438
1438
|
const shape = rootCauseShapes[origin.type];
|
|
1439
1439
|
if (shape) {
|
|
1440
|
-
const
|
|
1441
|
-
const match = shape(graph, origin,
|
|
1440
|
+
const walk11 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1441
|
+
const match = shape(graph, origin, walk11);
|
|
1442
1442
|
if (match) {
|
|
1443
1443
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1444
1444
|
return {
|
|
@@ -1446,9 +1446,9 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1446
1446
|
result: import_types.RootCauseResultSchema.parse({
|
|
1447
1447
|
rootCauseNode: match.rootCauseNode,
|
|
1448
1448
|
rootCauseReason: reason,
|
|
1449
|
-
traversalPath:
|
|
1450
|
-
edgeProvenances:
|
|
1451
|
-
confidence: confidenceFromMix(
|
|
1449
|
+
traversalPath: walk11.path,
|
|
1450
|
+
edgeProvenances: walk11.edges.map((e) => e.provenance),
|
|
1451
|
+
confidence: confidenceFromMix(walk11.edges),
|
|
1452
1452
|
fixRecommendation: match.fixRecommendation
|
|
1453
1453
|
})
|
|
1454
1454
|
};
|
|
@@ -1512,9 +1512,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1512
1512
|
function isFailingCallEdge(e) {
|
|
1513
1513
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1514
1514
|
}
|
|
1515
|
-
function callSourcesForService(graph,
|
|
1516
|
-
const ids = [
|
|
1517
|
-
for (const edgeId of graph.outboundEdges(
|
|
1515
|
+
function callSourcesForService(graph, serviceId17) {
|
|
1516
|
+
const ids = [serviceId17];
|
|
1517
|
+
for (const edgeId of graph.outboundEdges(serviceId17)) {
|
|
1518
1518
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1519
1519
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1520
1520
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1538,9 +1538,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1538
1538
|
}
|
|
1539
1539
|
return id < curId;
|
|
1540
1540
|
}
|
|
1541
|
-
function dominantFailingCall(graph,
|
|
1541
|
+
function dominantFailingCall(graph, serviceId17, visited) {
|
|
1542
1542
|
let best = null;
|
|
1543
|
-
for (const src of callSourcesForService(graph,
|
|
1543
|
+
for (const src of callSourcesForService(graph, serviceId17)) {
|
|
1544
1544
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1545
1545
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1546
1546
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -1555,20 +1555,20 @@ function dominantFailingCall(graph, serviceId16, visited) {
|
|
|
1555
1555
|
return best;
|
|
1556
1556
|
}
|
|
1557
1557
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1558
|
-
const
|
|
1558
|
+
const path80 = [originServiceId];
|
|
1559
1559
|
const edges = [];
|
|
1560
1560
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1561
1561
|
let current = originServiceId;
|
|
1562
1562
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1563
1563
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1564
1564
|
if (!hop) break;
|
|
1565
|
-
|
|
1565
|
+
path80.push(hop.nextService);
|
|
1566
1566
|
edges.push(hop.edge);
|
|
1567
1567
|
visited.add(hop.nextService);
|
|
1568
1568
|
current = hop.nextService;
|
|
1569
1569
|
}
|
|
1570
1570
|
if (edges.length === 0) return null;
|
|
1571
|
-
return { path:
|
|
1571
|
+
return { path: path80, edges, culprit: current };
|
|
1572
1572
|
}
|
|
1573
1573
|
function isStaleCallEdge(e) {
|
|
1574
1574
|
return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
|
|
@@ -1579,9 +1579,9 @@ function staleCallDominates(e, id, curEdge, curId) {
|
|
|
1579
1579
|
if (ev !== cv) return ev > cv;
|
|
1580
1580
|
return id < curId;
|
|
1581
1581
|
}
|
|
1582
|
-
function dominantStaleCall(graph,
|
|
1582
|
+
function dominantStaleCall(graph, serviceId17, visited) {
|
|
1583
1583
|
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1584
|
-
for (const src of callSourcesForService(graph,
|
|
1584
|
+
for (const src of callSourcesForService(graph, serviceId17)) {
|
|
1585
1585
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1586
1586
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1587
1587
|
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
@@ -1604,26 +1604,26 @@ function dominantStaleCall(graph, serviceId16, visited) {
|
|
|
1604
1604
|
return best;
|
|
1605
1605
|
}
|
|
1606
1606
|
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
1607
|
-
const
|
|
1607
|
+
const path80 = [originServiceId];
|
|
1608
1608
|
const edges = [];
|
|
1609
1609
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1610
1610
|
let current = originServiceId;
|
|
1611
1611
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1612
1612
|
const hop = dominantStaleCall(graph, current, visited);
|
|
1613
1613
|
if (!hop) break;
|
|
1614
|
-
|
|
1614
|
+
path80.push(hop.nextService);
|
|
1615
1615
|
edges.push(hop.edge);
|
|
1616
1616
|
visited.add(hop.nextService);
|
|
1617
1617
|
current = hop.nextService;
|
|
1618
1618
|
}
|
|
1619
1619
|
if (edges.length === 0) return null;
|
|
1620
|
-
return { path:
|
|
1620
|
+
return { path: path80, edges, culprit: current };
|
|
1621
1621
|
}
|
|
1622
1622
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1623
1623
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1624
1624
|
if (!chain) return null;
|
|
1625
1625
|
const culprit = chain.culprit;
|
|
1626
|
-
const
|
|
1626
|
+
const path80 = [...chain.path];
|
|
1627
1627
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1628
1628
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1629
1629
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1631,14 +1631,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1631
1631
|
if (loc) {
|
|
1632
1632
|
let rootCauseNode = culprit;
|
|
1633
1633
|
if (loc.fileNode) {
|
|
1634
|
-
|
|
1634
|
+
path80.push(loc.fileNode);
|
|
1635
1635
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1636
1636
|
rootCauseNode = loc.fileNode;
|
|
1637
1637
|
}
|
|
1638
1638
|
return import_types.RootCauseResultSchema.parse({
|
|
1639
1639
|
rootCauseNode,
|
|
1640
1640
|
rootCauseReason: loc.rootCauseReason,
|
|
1641
|
-
traversalPath:
|
|
1641
|
+
traversalPath: path80,
|
|
1642
1642
|
edgeProvenances,
|
|
1643
1643
|
confidence,
|
|
1644
1644
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1650,7 +1650,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1650
1650
|
return import_types.RootCauseResultSchema.parse({
|
|
1651
1651
|
rootCauseNode: culprit,
|
|
1652
1652
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1653
|
-
traversalPath:
|
|
1653
|
+
traversalPath: path80,
|
|
1654
1654
|
edgeProvenances,
|
|
1655
1655
|
confidence,
|
|
1656
1656
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2263,10 +2263,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
2263
2263
|
traversalPath = staleChain.path;
|
|
2264
2264
|
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
2265
2265
|
} else if (top.node !== seedNode) {
|
|
2266
|
-
const
|
|
2267
|
-
if (
|
|
2268
|
-
traversalPath =
|
|
2269
|
-
edgeProvenances =
|
|
2266
|
+
const path80 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
2267
|
+
if (path80) {
|
|
2268
|
+
traversalPath = path80.nodes;
|
|
2269
|
+
edgeProvenances = path80.edges.map((e) => e.provenance);
|
|
2270
2270
|
} else {
|
|
2271
2271
|
traversalPath = [errorNodeId, top.node];
|
|
2272
2272
|
edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
|
|
@@ -3090,7 +3090,7 @@ function buildServiceHostIndex(services) {
|
|
|
3090
3090
|
async function walkSourceFiles(dir, excludeDirs = []) {
|
|
3091
3091
|
const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
|
|
3092
3092
|
const out = [];
|
|
3093
|
-
async function
|
|
3093
|
+
async function walk11(current) {
|
|
3094
3094
|
const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
3095
3095
|
for (const entry2 of entries) {
|
|
3096
3096
|
const full = import_node_path5.default.join(current, entry2.name);
|
|
@@ -3098,7 +3098,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
3098
3098
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
3099
3099
|
if (excluded.has(import_node_path5.default.resolve(full))) continue;
|
|
3100
3100
|
if (await isPythonVenvDir(full)) continue;
|
|
3101
|
-
await
|
|
3101
|
+
await walk11(full);
|
|
3102
3102
|
} else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
3103
3103
|
// would attribute our instrumentation imports to the user's service.
|
|
3104
3104
|
!isNeatAuthoredSourceFile(entry2.name)) {
|
|
@@ -3106,7 +3106,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
3106
3106
|
}
|
|
3107
3107
|
}
|
|
3108
3108
|
}
|
|
3109
|
-
await
|
|
3109
|
+
await walk11(dir);
|
|
3110
3110
|
return out;
|
|
3111
3111
|
}
|
|
3112
3112
|
async function loadSourceFiles(dir, excludeDirs = []) {
|
|
@@ -3702,8 +3702,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3702
3702
|
chiWalk(tree.rootNode, "", out);
|
|
3703
3703
|
return out;
|
|
3704
3704
|
}
|
|
3705
|
-
function stripChiRegex(
|
|
3706
|
-
return
|
|
3705
|
+
function stripChiRegex(path80) {
|
|
3706
|
+
return path80.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3707
3707
|
}
|
|
3708
3708
|
function chiWalk(node, prefix, out) {
|
|
3709
3709
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -4375,9 +4375,9 @@ function rubyRocketRoute(args) {
|
|
|
4375
4375
|
if (!pair || pair.type !== "pair") continue;
|
|
4376
4376
|
const k = pair.childForFieldName("key");
|
|
4377
4377
|
if (k?.type !== "string") continue;
|
|
4378
|
-
const
|
|
4379
|
-
if (
|
|
4380
|
-
return { path:
|
|
4378
|
+
const path80 = rubyLiteral(k);
|
|
4379
|
+
if (path80 === null) continue;
|
|
4380
|
+
return { path: path80, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
4381
4381
|
}
|
|
4382
4382
|
return null;
|
|
4383
4383
|
}
|
|
@@ -5988,8 +5988,8 @@ function pickContainingSymbol(candidates, fn) {
|
|
|
5988
5988
|
return b.symbol.span.startLine - a.symbol.span.startLine;
|
|
5989
5989
|
};
|
|
5990
5990
|
if (fn) {
|
|
5991
|
-
const
|
|
5992
|
-
if (
|
|
5991
|
+
const named2 = candidates.filter((c) => terminalName(c.symbol.qualname) === fn);
|
|
5992
|
+
if (named2.length > 0) return [...named2].sort(bySpan)[0].id;
|
|
5993
5993
|
}
|
|
5994
5994
|
return [...candidates].sort(bySpan)[0].id;
|
|
5995
5995
|
}
|
|
@@ -6932,29 +6932,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
6932
6932
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
6933
6933
|
});
|
|
6934
6934
|
let promoted = 0;
|
|
6935
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
6935
|
+
for (const { frontierId: frontierId2, serviceId: serviceId17 } of toPromote) {
|
|
6936
6936
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
6937
6937
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
6938
6938
|
if (!gate.allowed) {
|
|
6939
6939
|
continue;
|
|
6940
6940
|
}
|
|
6941
6941
|
}
|
|
6942
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
6942
|
+
rewireFrontierEdges(graph, frontierId2, serviceId17);
|
|
6943
6943
|
graph.dropNode(frontierId2);
|
|
6944
6944
|
promoted++;
|
|
6945
6945
|
}
|
|
6946
6946
|
return promoted;
|
|
6947
6947
|
}
|
|
6948
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
6948
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId17) {
|
|
6949
6949
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
6950
6950
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
6951
6951
|
for (const edgeId of inbound) {
|
|
6952
6952
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6953
|
-
rebuildEdge(graph, edge, edge.source,
|
|
6953
|
+
rebuildEdge(graph, edge, edge.source, serviceId17, edgeId);
|
|
6954
6954
|
}
|
|
6955
6955
|
for (const edgeId of outbound) {
|
|
6956
6956
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6957
|
-
rebuildEdge(graph, edge,
|
|
6957
|
+
rebuildEdge(graph, edge, serviceId17, edge.target, edgeId);
|
|
6958
6958
|
}
|
|
6959
6959
|
}
|
|
6960
6960
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -8087,9 +8087,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
8087
8087
|
"StatefulSet",
|
|
8088
8088
|
"DaemonSet"
|
|
8089
8089
|
]);
|
|
8090
|
-
function addAliases(graph,
|
|
8091
|
-
if (!graph.hasNode(
|
|
8092
|
-
const node = graph.getNodeAttributes(
|
|
8090
|
+
function addAliases(graph, serviceId17, candidates) {
|
|
8091
|
+
if (!graph.hasNode(serviceId17)) return;
|
|
8092
|
+
const node = graph.getNodeAttributes(serviceId17);
|
|
8093
8093
|
if (node.type !== import_types19.NodeType.ServiceNode) return;
|
|
8094
8094
|
const set = new Set(node.aliases ?? []);
|
|
8095
8095
|
for (const c of candidates) {
|
|
@@ -8099,7 +8099,7 @@ function addAliases(graph, serviceId16, candidates) {
|
|
|
8099
8099
|
}
|
|
8100
8100
|
if (set.size === 0) return;
|
|
8101
8101
|
const updated = { ...node, aliases: [...set].sort() };
|
|
8102
|
-
graph.replaceNodeAttributes(
|
|
8102
|
+
graph.replaceNodeAttributes(serviceId17, updated);
|
|
8103
8103
|
}
|
|
8104
8104
|
function indexServicesByName(services) {
|
|
8105
8105
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -8132,12 +8132,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
8132
8132
|
}
|
|
8133
8133
|
if (!compose?.services) return;
|
|
8134
8134
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
8135
|
-
const
|
|
8136
|
-
if (!
|
|
8135
|
+
const serviceId17 = serviceIndex.get(composeName);
|
|
8136
|
+
if (!serviceId17) continue;
|
|
8137
8137
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
8138
8138
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
8139
8139
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
8140
|
-
addAliases(graph,
|
|
8140
|
+
addAliases(graph, serviceId17, aliases);
|
|
8141
8141
|
}
|
|
8142
8142
|
}
|
|
8143
8143
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -9248,7 +9248,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
9248
9248
|
return best;
|
|
9249
9249
|
};
|
|
9250
9250
|
const requests = [];
|
|
9251
|
-
const
|
|
9251
|
+
const walk11 = (node) => {
|
|
9252
9252
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
9253
9253
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
9254
9254
|
if (self && self.kind === "class") {
|
|
@@ -9294,10 +9294,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
9294
9294
|
}
|
|
9295
9295
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
9296
9296
|
const child = node.namedChild(i);
|
|
9297
|
-
if (child)
|
|
9297
|
+
if (child) walk11(child);
|
|
9298
9298
|
}
|
|
9299
9299
|
};
|
|
9300
|
-
|
|
9300
|
+
walk11(root);
|
|
9301
9301
|
for (const req2 of requests) {
|
|
9302
9302
|
const targetSid = resolveTarget(req2.targetName, req2.wantKind);
|
|
9303
9303
|
if (!targetSid) continue;
|
|
@@ -9405,10 +9405,10 @@ function collectNamedImports(root) {
|
|
|
9405
9405
|
const clause = node.namedChild(i);
|
|
9406
9406
|
if (clause?.type !== "import_clause") continue;
|
|
9407
9407
|
for (let j = 0; j < clause.namedChildCount; j++) {
|
|
9408
|
-
const
|
|
9409
|
-
if (
|
|
9410
|
-
for (let k = 0; k <
|
|
9411
|
-
const spec =
|
|
9408
|
+
const named2 = clause.namedChild(j);
|
|
9409
|
+
if (named2?.type !== "named_imports") continue;
|
|
9410
|
+
for (let k = 0; k < named2.namedChildCount; k++) {
|
|
9411
|
+
const spec = named2.namedChild(k);
|
|
9412
9412
|
if (spec?.type !== "import_specifier") continue;
|
|
9413
9413
|
let isType = false;
|
|
9414
9414
|
for (let t = 0; t < spec.childCount; t++) {
|
|
@@ -10469,7 +10469,7 @@ var import_types24 = require("@neat.is/types");
|
|
|
10469
10469
|
async function walkConfigFiles(dir, excludeDirs = []) {
|
|
10470
10470
|
const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
|
|
10471
10471
|
const out = [];
|
|
10472
|
-
async function
|
|
10472
|
+
async function walk11(current) {
|
|
10473
10473
|
const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
|
|
10474
10474
|
for (const entry2 of entries) {
|
|
10475
10475
|
const full = import_node_path35.default.join(current, entry2.name);
|
|
@@ -10477,13 +10477,13 @@ async function walkConfigFiles(dir, excludeDirs = []) {
|
|
|
10477
10477
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
10478
10478
|
if (excluded.has(import_node_path35.default.resolve(full))) continue;
|
|
10479
10479
|
if (await isPythonVenvDir(full)) continue;
|
|
10480
|
-
await
|
|
10480
|
+
await walk11(full);
|
|
10481
10481
|
} else if (entry2.isFile() && isConfigFile(entry2.name).match) {
|
|
10482
10482
|
out.push(full);
|
|
10483
10483
|
}
|
|
10484
10484
|
}
|
|
10485
10485
|
}
|
|
10486
|
-
await
|
|
10486
|
+
await walk11(dir);
|
|
10487
10487
|
return out;
|
|
10488
10488
|
}
|
|
10489
10489
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -10574,7 +10574,7 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
10574
10574
|
async function walkProtoFiles(dir, excludeDirs = []) {
|
|
10575
10575
|
const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
|
|
10576
10576
|
const out = [];
|
|
10577
|
-
async function
|
|
10577
|
+
async function walk11(current) {
|
|
10578
10578
|
const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
10579
10579
|
for (const entry2 of entries) {
|
|
10580
10580
|
const full = import_node_path36.default.join(current, entry2.name);
|
|
@@ -10582,13 +10582,13 @@ async function walkProtoFiles(dir, excludeDirs = []) {
|
|
|
10582
10582
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
10583
10583
|
if (excluded.has(import_node_path36.default.resolve(full))) continue;
|
|
10584
10584
|
if (await isPythonVenvDir(full)) continue;
|
|
10585
|
-
await
|
|
10585
|
+
await walk11(full);
|
|
10586
10586
|
} else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
|
|
10587
10587
|
out.push(full);
|
|
10588
10588
|
}
|
|
10589
10589
|
}
|
|
10590
10590
|
}
|
|
10591
|
-
await
|
|
10591
|
+
await walk11(dir);
|
|
10592
10592
|
return out;
|
|
10593
10593
|
}
|
|
10594
10594
|
async function addGrpcMethods(graph, services) {
|
|
@@ -10656,7 +10656,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
10656
10656
|
|
|
10657
10657
|
// src/extract/calls/index.ts
|
|
10658
10658
|
init_cjs_shims();
|
|
10659
|
-
var
|
|
10659
|
+
var import_types45 = require("@neat.is/types");
|
|
10660
10660
|
|
|
10661
10661
|
// src/extract/calls/http.ts
|
|
10662
10662
|
init_cjs_shims();
|
|
@@ -11591,7 +11591,7 @@ function isFirestoreClientFactory(node) {
|
|
|
11591
11591
|
}
|
|
11592
11592
|
function firestoreClientVars(root) {
|
|
11593
11593
|
const vars = /* @__PURE__ */ new Set();
|
|
11594
|
-
const
|
|
11594
|
+
const walk11 = (node) => {
|
|
11595
11595
|
if (node.type === "variable_declarator") {
|
|
11596
11596
|
const name = node.childForFieldName("name");
|
|
11597
11597
|
let value = node.childForFieldName("value");
|
|
@@ -11600,9 +11600,9 @@ function firestoreClientVars(root) {
|
|
|
11600
11600
|
vars.add(name.text);
|
|
11601
11601
|
}
|
|
11602
11602
|
}
|
|
11603
|
-
for (const c of namedChildren(node))
|
|
11603
|
+
for (const c of namedChildren(node)) walk11(c);
|
|
11604
11604
|
};
|
|
11605
|
-
|
|
11605
|
+
walk11(root);
|
|
11606
11606
|
return vars;
|
|
11607
11607
|
}
|
|
11608
11608
|
function isClientExpr(node, clientVars) {
|
|
@@ -11757,7 +11757,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
11757
11757
|
}
|
|
11758
11758
|
s.add(field);
|
|
11759
11759
|
};
|
|
11760
|
-
const
|
|
11760
|
+
const walk11 = (node) => {
|
|
11761
11761
|
if (node.type === "call_expression") {
|
|
11762
11762
|
const fn = node.childForFieldName("function");
|
|
11763
11763
|
const line = node.startPosition.row + 1;
|
|
@@ -11797,9 +11797,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
11797
11797
|
}
|
|
11798
11798
|
}
|
|
11799
11799
|
}
|
|
11800
|
-
for (const c of namedChildren(node))
|
|
11800
|
+
for (const c of namedChildren(node)) walk11(c);
|
|
11801
11801
|
};
|
|
11802
|
-
|
|
11802
|
+
walk11(tree.rootNode);
|
|
11803
11803
|
const out = [];
|
|
11804
11804
|
for (const [collPath, line] of collLine) {
|
|
11805
11805
|
const byField = writes.get(collPath);
|
|
@@ -12042,8 +12042,8 @@ function parseImportBindings(content) {
|
|
|
12042
12042
|
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
12043
12043
|
continue;
|
|
12044
12044
|
}
|
|
12045
|
-
const
|
|
12046
|
-
if (
|
|
12045
|
+
const named2 = /\{([^}]*)\}/.exec(clause);
|
|
12046
|
+
if (named2) parseDestructure(named2[1], spec, out);
|
|
12047
12047
|
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
12048
12048
|
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
12049
12049
|
}
|
|
@@ -12063,8 +12063,8 @@ function fileExportsOf(content, pluralizeOn) {
|
|
|
12063
12063
|
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
12064
12064
|
const byName = new Map(byVar);
|
|
12065
12065
|
let def;
|
|
12066
|
-
const
|
|
12067
|
-
if (
|
|
12066
|
+
const named2 = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
12067
|
+
if (named2 && byVar.has(named2[1])) def = byVar.get(named2[1]);
|
|
12068
12068
|
if (!def) {
|
|
12069
12069
|
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
12070
12070
|
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
@@ -12415,13 +12415,13 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
12415
12415
|
return out;
|
|
12416
12416
|
}
|
|
12417
12417
|
|
|
12418
|
-
// src/extract/calls/
|
|
12418
|
+
// src/extract/calls/socket.ts
|
|
12419
12419
|
init_cjs_shims();
|
|
12420
12420
|
var import_node_path47 = __toESM(require("path"), 1);
|
|
12421
12421
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
12422
12422
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
12423
12423
|
var import_types36 = require("@neat.is/types");
|
|
12424
|
-
var
|
|
12424
|
+
var SOCKET_IMPORT_RE = /(?:^|\n)\s*(?:import\s+socket\b|from\s+socket\s+import\b)/;
|
|
12425
12425
|
var PARSE_CHUNK7 = 16384;
|
|
12426
12426
|
function makePyParser4() {
|
|
12427
12427
|
const p = new import_tree_sitter10.default();
|
|
@@ -12441,22 +12441,109 @@ function namedChildren3(node) {
|
|
|
12441
12441
|
}
|
|
12442
12442
|
return out;
|
|
12443
12443
|
}
|
|
12444
|
+
function walk4(node, visit) {
|
|
12445
|
+
visit(node);
|
|
12446
|
+
for (const c of namedChildren3(node)) walk4(c, visit);
|
|
12447
|
+
}
|
|
12444
12448
|
function pyStaticStringText3(node) {
|
|
12445
12449
|
if (node.type !== "string") return null;
|
|
12446
|
-
for (const
|
|
12450
|
+
for (const child of namedChildren3(node)) {
|
|
12451
|
+
if (child.type === "interpolation") return null;
|
|
12452
|
+
if (child.type === "string_content") return child.text;
|
|
12453
|
+
}
|
|
12454
|
+
return "";
|
|
12455
|
+
}
|
|
12456
|
+
function hasSocketConstructor(root) {
|
|
12457
|
+
let found = false;
|
|
12458
|
+
walk4(root, (n) => {
|
|
12459
|
+
if (found || n.type !== "call") return;
|
|
12460
|
+
const t = n.childForFieldName("function")?.text;
|
|
12461
|
+
if (t === "socket.socket" || t === "socket") found = true;
|
|
12462
|
+
});
|
|
12463
|
+
return found;
|
|
12464
|
+
}
|
|
12465
|
+
function socketEndpointsFromFile(file, serviceDir) {
|
|
12466
|
+
if (!SOCKET_IMPORT_RE.test(file.content)) return [];
|
|
12467
|
+
const tree = parseSource7(makePyParser4(), file.content);
|
|
12468
|
+
if (!hasSocketConstructor(tree.rootNode)) return [];
|
|
12469
|
+
const out = [];
|
|
12470
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12471
|
+
walk4(tree.rootNode, (node) => {
|
|
12472
|
+
if (node.type !== "call") return;
|
|
12473
|
+
const fn = node.childForFieldName("function");
|
|
12474
|
+
if (fn?.type !== "attribute" || fn.childForFieldName("attribute")?.text !== "connect") return;
|
|
12475
|
+
const args = node.childForFieldName("arguments");
|
|
12476
|
+
const first = args ? namedChildren3(args).find((a) => a.type !== "keyword_argument") : void 0;
|
|
12477
|
+
if (!first || first.type !== "tuple") return;
|
|
12478
|
+
const elems = namedChildren3(first);
|
|
12479
|
+
if (elems.length < 2) return;
|
|
12480
|
+
const hostNode = elems[0];
|
|
12481
|
+
const host = hostNode.type === "string" ? pyStaticStringText3(hostNode) : null;
|
|
12482
|
+
const portNode = elems[1];
|
|
12483
|
+
const port = portNode.type === "integer" ? portNode.text : null;
|
|
12484
|
+
const name = host ? port ? `${host}:${port}` : host : "env";
|
|
12485
|
+
if (seen.has(name)) return;
|
|
12486
|
+
seen.add(name);
|
|
12487
|
+
const line = node.startPosition.row + 1;
|
|
12488
|
+
out.push({
|
|
12489
|
+
infraId: (0, import_types36.infraId)("socket", name),
|
|
12490
|
+
name,
|
|
12491
|
+
kind: "socket",
|
|
12492
|
+
edgeType: "CALLS",
|
|
12493
|
+
// A verified `socket.socket(...)` + `.connect((host, port))` call site.
|
|
12494
|
+
confidenceKind: "verified-call-site",
|
|
12495
|
+
evidence: {
|
|
12496
|
+
file: import_node_path47.default.relative(serviceDir, file.path),
|
|
12497
|
+
line,
|
|
12498
|
+
snippet: snippet(file.content, line)
|
|
12499
|
+
}
|
|
12500
|
+
});
|
|
12501
|
+
});
|
|
12502
|
+
return out;
|
|
12503
|
+
}
|
|
12504
|
+
|
|
12505
|
+
// src/extract/calls/django-orm.ts
|
|
12506
|
+
init_cjs_shims();
|
|
12507
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
12508
|
+
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
12509
|
+
var import_tree_sitter_python7 = __toESM(require("tree-sitter-python"), 1);
|
|
12510
|
+
var import_types37 = require("@neat.is/types");
|
|
12511
|
+
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
12512
|
+
var PARSE_CHUNK8 = 16384;
|
|
12513
|
+
function makePyParser5() {
|
|
12514
|
+
const p = new import_tree_sitter11.default();
|
|
12515
|
+
p.setLanguage(import_tree_sitter_python7.default);
|
|
12516
|
+
return p;
|
|
12517
|
+
}
|
|
12518
|
+
function parseSource8(parser, source) {
|
|
12519
|
+
return parser.parse(
|
|
12520
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK8)
|
|
12521
|
+
);
|
|
12522
|
+
}
|
|
12523
|
+
function namedChildren4(node) {
|
|
12524
|
+
const out = [];
|
|
12525
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
12526
|
+
const c = node.namedChild(i);
|
|
12527
|
+
if (c) out.push(c);
|
|
12528
|
+
}
|
|
12529
|
+
return out;
|
|
12530
|
+
}
|
|
12531
|
+
function pyStaticStringText4(node) {
|
|
12532
|
+
if (node.type !== "string") return null;
|
|
12533
|
+
for (const c of namedChildren4(node)) {
|
|
12447
12534
|
if (c.type === "interpolation") return null;
|
|
12448
12535
|
if (c.type === "string_content") return c.text;
|
|
12449
12536
|
}
|
|
12450
12537
|
return "";
|
|
12451
12538
|
}
|
|
12452
|
-
function
|
|
12539
|
+
function walk5(node, visit) {
|
|
12453
12540
|
visit(node);
|
|
12454
|
-
for (const c of
|
|
12541
|
+
for (const c of namedChildren4(node)) walk5(c, visit);
|
|
12455
12542
|
}
|
|
12456
12543
|
function extendsDjangoModel(cls) {
|
|
12457
12544
|
const supers = cls.childForFieldName("superclasses");
|
|
12458
12545
|
if (!supers) return false;
|
|
12459
|
-
for (const a of
|
|
12546
|
+
for (const a of namedChildren4(supers)) {
|
|
12460
12547
|
const t = a.text;
|
|
12461
12548
|
if (t === "models.Model" || t === "Model" || t.endsWith(".Model")) return true;
|
|
12462
12549
|
}
|
|
@@ -12464,31 +12551,31 @@ function extendsDjangoModel(cls) {
|
|
|
12464
12551
|
}
|
|
12465
12552
|
function readMeta(body) {
|
|
12466
12553
|
const out = {};
|
|
12467
|
-
for (const stmt of
|
|
12554
|
+
for (const stmt of namedChildren4(body)) {
|
|
12468
12555
|
if (stmt.type !== "class_definition") continue;
|
|
12469
12556
|
if (stmt.childForFieldName("name")?.text !== "Meta") continue;
|
|
12470
12557
|
const metaBody = stmt.childForFieldName("body");
|
|
12471
12558
|
if (!metaBody) continue;
|
|
12472
|
-
for (const ms of
|
|
12559
|
+
for (const ms of namedChildren4(metaBody)) {
|
|
12473
12560
|
if (ms.type !== "expression_statement") continue;
|
|
12474
12561
|
const a = ms.namedChild(0);
|
|
12475
12562
|
if (a?.type !== "assignment") continue;
|
|
12476
12563
|
const l = a.childForFieldName("left")?.text;
|
|
12477
12564
|
const r = a.childForFieldName("right");
|
|
12478
12565
|
if (r?.type !== "string") continue;
|
|
12479
|
-
if (l === "db_table") out.dbTable =
|
|
12480
|
-
if (l === "app_label") out.appLabel =
|
|
12566
|
+
if (l === "db_table") out.dbTable = pyStaticStringText4(r) ?? void 0;
|
|
12567
|
+
if (l === "app_label") out.appLabel = pyStaticStringText4(r) ?? void 0;
|
|
12481
12568
|
}
|
|
12482
12569
|
}
|
|
12483
12570
|
return out;
|
|
12484
12571
|
}
|
|
12485
12572
|
function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
12486
12573
|
if (!DJANGO_IMPORT_RE.test(file.content)) return [];
|
|
12487
|
-
const tree =
|
|
12574
|
+
const tree = parseSource8(makePyParser5(), file.content);
|
|
12488
12575
|
const out = [];
|
|
12489
12576
|
const seen = /* @__PURE__ */ new Set();
|
|
12490
|
-
const defaultAppLabel =
|
|
12491
|
-
|
|
12577
|
+
const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
|
|
12578
|
+
walk5(tree.rootNode, (node) => {
|
|
12492
12579
|
if (node.type !== "class_definition") return;
|
|
12493
12580
|
if (!extendsDjangoModel(node)) return;
|
|
12494
12581
|
const nameNode = node.childForFieldName("name");
|
|
@@ -12500,12 +12587,12 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
12500
12587
|
seen.add(table);
|
|
12501
12588
|
const line = node.startPosition.row + 1;
|
|
12502
12589
|
out.push({
|
|
12503
|
-
infraId: (0,
|
|
12590
|
+
infraId: (0, import_types37.infraId)("sql-table", table),
|
|
12504
12591
|
name: table,
|
|
12505
12592
|
kind: "sql-table",
|
|
12506
12593
|
edgeType: "CALLS",
|
|
12507
12594
|
confidenceKind: "verified-call-site",
|
|
12508
|
-
evidence: { file:
|
|
12595
|
+
evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
12509
12596
|
});
|
|
12510
12597
|
});
|
|
12511
12598
|
return out;
|
|
@@ -12513,18 +12600,18 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
12513
12600
|
|
|
12514
12601
|
// src/extract/calls/drizzle.ts
|
|
12515
12602
|
init_cjs_shims();
|
|
12516
|
-
var
|
|
12517
|
-
var
|
|
12603
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
12604
|
+
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
12518
12605
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
12519
|
-
var
|
|
12606
|
+
var import_types38 = require("@neat.is/types");
|
|
12520
12607
|
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
12521
12608
|
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
12522
12609
|
function parserForExt3(ext) {
|
|
12523
|
-
const p = new
|
|
12610
|
+
const p = new import_tree_sitter12.default();
|
|
12524
12611
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
|
|
12525
12612
|
return p;
|
|
12526
12613
|
}
|
|
12527
|
-
function
|
|
12614
|
+
function namedChildren5(node) {
|
|
12528
12615
|
const out = [];
|
|
12529
12616
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
12530
12617
|
const c = node.namedChild(i);
|
|
@@ -12534,7 +12621,7 @@ function namedChildren4(node) {
|
|
|
12534
12621
|
}
|
|
12535
12622
|
function stringLiteralText4(node) {
|
|
12536
12623
|
if (!node || node.type !== "string") return null;
|
|
12537
|
-
for (const child of
|
|
12624
|
+
for (const child of namedChildren5(node)) {
|
|
12538
12625
|
if (child.type === "string_fragment") return child.text;
|
|
12539
12626
|
}
|
|
12540
12627
|
return "";
|
|
@@ -12542,7 +12629,7 @@ function stringLiteralText4(node) {
|
|
|
12542
12629
|
function firstStringArg(call) {
|
|
12543
12630
|
const args = call.childForFieldName("arguments");
|
|
12544
12631
|
if (!args) return null;
|
|
12545
|
-
for (const arg of
|
|
12632
|
+
for (const arg of namedChildren5(args)) {
|
|
12546
12633
|
if (arg.type === "string") return stringLiteralText4(arg);
|
|
12547
12634
|
return null;
|
|
12548
12635
|
}
|
|
@@ -12577,7 +12664,7 @@ function keyName2(key) {
|
|
|
12577
12664
|
function columnsFromObject(obj) {
|
|
12578
12665
|
const out = [];
|
|
12579
12666
|
const seen = /* @__PURE__ */ new Set();
|
|
12580
|
-
for (const child of
|
|
12667
|
+
for (const child of namedChildren5(obj)) {
|
|
12581
12668
|
if (child.type !== "pair") continue;
|
|
12582
12669
|
const value = child.childForFieldName("value");
|
|
12583
12670
|
const key = keyName2(child.childForFieldName("key"));
|
|
@@ -12591,15 +12678,15 @@ function columnsFromObject(obj) {
|
|
|
12591
12678
|
}
|
|
12592
12679
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
12593
12680
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
12594
|
-
const tree = parseSource3(parserForExt3(
|
|
12681
|
+
const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
|
|
12595
12682
|
const out = [];
|
|
12596
12683
|
const seen = /* @__PURE__ */ new Set();
|
|
12597
|
-
const
|
|
12684
|
+
const walk11 = (node) => {
|
|
12598
12685
|
if (node.type === "call_expression") {
|
|
12599
12686
|
const fn = node.childForFieldName("function");
|
|
12600
12687
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
12601
12688
|
const args = node.childForFieldName("arguments");
|
|
12602
|
-
const argNodes = args ?
|
|
12689
|
+
const argNodes = args ? namedChildren5(args) : [];
|
|
12603
12690
|
const tableName = stringLiteralText4(argNodes[0] ?? null);
|
|
12604
12691
|
const obj = argNodes[1];
|
|
12605
12692
|
if (tableName && obj?.type === "object" && !seen.has(tableName)) {
|
|
@@ -12607,14 +12694,14 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
12607
12694
|
const columns = columnsFromObject(obj);
|
|
12608
12695
|
const line = node.startPosition.row + 1;
|
|
12609
12696
|
out.push({
|
|
12610
|
-
infraId: (0,
|
|
12697
|
+
infraId: (0, import_types38.infraId)("sql-table", tableName),
|
|
12611
12698
|
name: tableName,
|
|
12612
12699
|
kind: "sql-table",
|
|
12613
12700
|
edgeType: "CALLS",
|
|
12614
12701
|
confidenceKind: "structural",
|
|
12615
12702
|
columns,
|
|
12616
12703
|
evidence: {
|
|
12617
|
-
file:
|
|
12704
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
12618
12705
|
line,
|
|
12619
12706
|
snippet: snippet(file.content, line)
|
|
12620
12707
|
}
|
|
@@ -12622,9 +12709,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
12622
12709
|
}
|
|
12623
12710
|
}
|
|
12624
12711
|
}
|
|
12625
|
-
for (const c of
|
|
12712
|
+
for (const c of namedChildren5(node)) walk11(c);
|
|
12626
12713
|
};
|
|
12627
|
-
|
|
12714
|
+
walk11(tree.rootNode);
|
|
12628
12715
|
return out;
|
|
12629
12716
|
}
|
|
12630
12717
|
function enclosingVarName(call) {
|
|
@@ -12646,12 +12733,12 @@ function enclosingVarName(call) {
|
|
|
12646
12733
|
function collectDrizzleTables(root) {
|
|
12647
12734
|
const tables = [];
|
|
12648
12735
|
const varToTable = /* @__PURE__ */ new Map();
|
|
12649
|
-
const
|
|
12736
|
+
const walk11 = (node) => {
|
|
12650
12737
|
if (node.type === "call_expression") {
|
|
12651
12738
|
const fn = node.childForFieldName("function");
|
|
12652
12739
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
12653
12740
|
const args = node.childForFieldName("arguments");
|
|
12654
|
-
const argNodes = args ?
|
|
12741
|
+
const argNodes = args ? namedChildren5(args) : [];
|
|
12655
12742
|
const tableName = stringLiteralText4(argNodes[0] ?? null);
|
|
12656
12743
|
const obj = argNodes[1]?.type === "object" ? argNodes[1] : null;
|
|
12657
12744
|
if (tableName) {
|
|
@@ -12661,9 +12748,9 @@ function collectDrizzleTables(root) {
|
|
|
12661
12748
|
}
|
|
12662
12749
|
}
|
|
12663
12750
|
}
|
|
12664
|
-
for (const c of
|
|
12751
|
+
for (const c of namedChildren5(node)) walk11(c);
|
|
12665
12752
|
};
|
|
12666
|
-
|
|
12753
|
+
walk11(root);
|
|
12667
12754
|
return { tables, varToTable };
|
|
12668
12755
|
}
|
|
12669
12756
|
function referencesTargetVar(call) {
|
|
@@ -12671,7 +12758,7 @@ function referencesTargetVar(call) {
|
|
|
12671
12758
|
if (fn?.type !== "member_expression") return null;
|
|
12672
12759
|
if (fn.childForFieldName("property")?.text !== "references") return null;
|
|
12673
12760
|
const args = call.childForFieldName("arguments");
|
|
12674
|
-
const first = args ?
|
|
12761
|
+
const first = args ? namedChildren5(args)[0] ?? null : null;
|
|
12675
12762
|
if (first?.type !== "arrow_function") return null;
|
|
12676
12763
|
const body = first.childForFieldName("body");
|
|
12677
12764
|
if (body?.type !== "member_expression") return null;
|
|
@@ -12680,13 +12767,13 @@ function referencesTargetVar(call) {
|
|
|
12680
12767
|
}
|
|
12681
12768
|
function drizzleForeignKeys(file, serviceDir) {
|
|
12682
12769
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
12683
|
-
const tree = parseSource3(parserForExt3(
|
|
12770
|
+
const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
|
|
12684
12771
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
12685
12772
|
const out = [];
|
|
12686
12773
|
const seen = /* @__PURE__ */ new Set();
|
|
12687
12774
|
for (const table of tables) {
|
|
12688
12775
|
if (!table.object) continue;
|
|
12689
|
-
const
|
|
12776
|
+
const walk11 = (node) => {
|
|
12690
12777
|
if (node.type === "call_expression") {
|
|
12691
12778
|
const targetVar = referencesTargetVar(node);
|
|
12692
12779
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -12699,7 +12786,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
12699
12786
|
childTable: table.tableName,
|
|
12700
12787
|
parentTable,
|
|
12701
12788
|
evidence: {
|
|
12702
|
-
file:
|
|
12789
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
12703
12790
|
line,
|
|
12704
12791
|
snippet: snippet(file.content, line)
|
|
12705
12792
|
}
|
|
@@ -12707,17 +12794,17 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
12707
12794
|
}
|
|
12708
12795
|
}
|
|
12709
12796
|
}
|
|
12710
|
-
for (const c of
|
|
12797
|
+
for (const c of namedChildren5(node)) walk11(c);
|
|
12711
12798
|
};
|
|
12712
|
-
|
|
12799
|
+
walk11(table.object);
|
|
12713
12800
|
}
|
|
12714
12801
|
return out;
|
|
12715
12802
|
}
|
|
12716
12803
|
|
|
12717
12804
|
// src/extract/calls/prisma.ts
|
|
12718
12805
|
init_cjs_shims();
|
|
12719
|
-
var
|
|
12720
|
-
var
|
|
12806
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
12807
|
+
var import_types39 = require("@neat.is/types");
|
|
12721
12808
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
12722
12809
|
"Int",
|
|
12723
12810
|
"String",
|
|
@@ -12765,14 +12852,14 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
12765
12852
|
if (seenTable.has(b.tableName)) return;
|
|
12766
12853
|
seenTable.add(b.tableName);
|
|
12767
12854
|
out.push({
|
|
12768
|
-
infraId: (0,
|
|
12855
|
+
infraId: (0, import_types39.infraId)("sql-table", b.tableName),
|
|
12769
12856
|
name: b.tableName,
|
|
12770
12857
|
kind: "sql-table",
|
|
12771
12858
|
edgeType: "CALLS",
|
|
12772
12859
|
confidenceKind: "structural",
|
|
12773
12860
|
columns: b.columns,
|
|
12774
12861
|
evidence: {
|
|
12775
|
-
file:
|
|
12862
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12776
12863
|
line: b.startLine,
|
|
12777
12864
|
snippet: snippet(content, b.startLine)
|
|
12778
12865
|
}
|
|
@@ -12833,7 +12920,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
12833
12920
|
}
|
|
12834
12921
|
async function prismaColumnEndpoints(serviceDir) {
|
|
12835
12922
|
const schemaPath = await findFirst(serviceDir, [
|
|
12836
|
-
|
|
12923
|
+
import_node_path50.default.join("prisma", "schema.prisma"),
|
|
12837
12924
|
"schema.prisma"
|
|
12838
12925
|
]);
|
|
12839
12926
|
if (!schemaPath) return [];
|
|
@@ -12908,7 +12995,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
12908
12995
|
childTable: current.table,
|
|
12909
12996
|
parentTable,
|
|
12910
12997
|
evidence: {
|
|
12911
|
-
file:
|
|
12998
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12912
12999
|
line: lineNo,
|
|
12913
13000
|
snippet: snippet(content, lineNo)
|
|
12914
13001
|
}
|
|
@@ -12923,7 +13010,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
12923
13010
|
}
|
|
12924
13011
|
async function prismaForeignKeys(serviceDir) {
|
|
12925
13012
|
const schemaPath = await findFirst(serviceDir, [
|
|
12926
|
-
|
|
13013
|
+
import_node_path50.default.join("prisma", "schema.prisma"),
|
|
12927
13014
|
"schema.prisma"
|
|
12928
13015
|
]);
|
|
12929
13016
|
if (!schemaPath) return [];
|
|
@@ -12934,27 +13021,27 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
12934
13021
|
|
|
12935
13022
|
// src/extract/calls/activerecord.ts
|
|
12936
13023
|
init_cjs_shims();
|
|
12937
|
-
var
|
|
12938
|
-
var
|
|
13024
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
13025
|
+
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
12939
13026
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
12940
|
-
var
|
|
13027
|
+
var import_types40 = require("@neat.is/types");
|
|
12941
13028
|
var AR_SCHEMA_RE = /ActiveRecord::Schema/;
|
|
12942
|
-
var
|
|
13029
|
+
var PARSE_CHUNK9 = 16384;
|
|
12943
13030
|
function makeRubyParser2() {
|
|
12944
|
-
const p = new
|
|
13031
|
+
const p = new import_tree_sitter13.default();
|
|
12945
13032
|
p.setLanguage(import_tree_sitter_ruby3.default);
|
|
12946
13033
|
return p;
|
|
12947
13034
|
}
|
|
12948
|
-
function
|
|
13035
|
+
function parseSource9(parser, source) {
|
|
12949
13036
|
return parser.parse(
|
|
12950
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
13037
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK9)
|
|
12951
13038
|
);
|
|
12952
13039
|
}
|
|
12953
|
-
function
|
|
13040
|
+
function walk6(node, visit) {
|
|
12954
13041
|
visit(node);
|
|
12955
13042
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
12956
13043
|
const c = node.namedChild(i);
|
|
12957
|
-
if (c)
|
|
13044
|
+
if (c) walk6(c, visit);
|
|
12958
13045
|
}
|
|
12959
13046
|
}
|
|
12960
13047
|
function rubyLiteral2(node) {
|
|
@@ -13125,24 +13212,24 @@ function readCreateTable(call) {
|
|
|
13125
13212
|
}
|
|
13126
13213
|
function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
13127
13214
|
if (!AR_SCHEMA_RE.test(file.content)) return [];
|
|
13128
|
-
const tree =
|
|
13215
|
+
const tree = parseSource9(makeRubyParser2(), file.content);
|
|
13129
13216
|
const out = [];
|
|
13130
13217
|
const seen = /* @__PURE__ */ new Set();
|
|
13131
|
-
|
|
13218
|
+
walk6(tree.rootNode, (node) => {
|
|
13132
13219
|
if (node.type !== "call") return;
|
|
13133
13220
|
if (blockCallMethod(node) !== "create_table") return;
|
|
13134
13221
|
const table = readCreateTable(node);
|
|
13135
13222
|
if (!table || seen.has(table.name)) return;
|
|
13136
13223
|
seen.add(table.name);
|
|
13137
13224
|
out.push({
|
|
13138
|
-
infraId: (0,
|
|
13225
|
+
infraId: (0, import_types40.infraId)("sql-table", table.name),
|
|
13139
13226
|
name: table.name,
|
|
13140
13227
|
kind: "sql-table",
|
|
13141
13228
|
edgeType: "CALLS",
|
|
13142
13229
|
confidenceKind: "structural",
|
|
13143
13230
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
13144
13231
|
evidence: {
|
|
13145
|
-
file:
|
|
13232
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
13146
13233
|
line: table.line,
|
|
13147
13234
|
snippet: snippet(file.content, table.line)
|
|
13148
13235
|
}
|
|
@@ -13152,7 +13239,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
13152
13239
|
}
|
|
13153
13240
|
function railsSchemaForeignKeys(file, serviceDir) {
|
|
13154
13241
|
if (!AR_SCHEMA_RE.test(file.content)) return [];
|
|
13155
|
-
const tree =
|
|
13242
|
+
const tree = parseSource9(makeRubyParser2(), file.content);
|
|
13156
13243
|
const out = [];
|
|
13157
13244
|
const seen = /* @__PURE__ */ new Set();
|
|
13158
13245
|
const emit = (childTable, parentTable, line) => {
|
|
@@ -13164,13 +13251,13 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
13164
13251
|
childTable,
|
|
13165
13252
|
parentTable,
|
|
13166
13253
|
evidence: {
|
|
13167
|
-
file:
|
|
13254
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
13168
13255
|
line,
|
|
13169
13256
|
snippet: snippet(file.content, line)
|
|
13170
13257
|
}
|
|
13171
13258
|
});
|
|
13172
13259
|
};
|
|
13173
|
-
|
|
13260
|
+
walk6(tree.rootNode, (node) => {
|
|
13174
13261
|
if (node.type !== "call") return;
|
|
13175
13262
|
const method = blockCallMethod(node);
|
|
13176
13263
|
if (method === "add_foreign_key") {
|
|
@@ -13231,23 +13318,23 @@ function modelTable(cls) {
|
|
|
13231
13318
|
return tableize(nameNode.text);
|
|
13232
13319
|
}
|
|
13233
13320
|
function railsModelEndpointsFromFile(file, serviceDir) {
|
|
13234
|
-
const tree =
|
|
13321
|
+
const tree = parseSource9(makeRubyParser2(), file.content);
|
|
13235
13322
|
const out = [];
|
|
13236
13323
|
const seen = /* @__PURE__ */ new Set();
|
|
13237
|
-
|
|
13324
|
+
walk6(tree.rootNode, (node) => {
|
|
13238
13325
|
if (node.type !== "class" || !isActiveRecordModel(node)) return;
|
|
13239
13326
|
const table = modelTable(node);
|
|
13240
13327
|
if (!table || seen.has(table)) return;
|
|
13241
13328
|
seen.add(table);
|
|
13242
13329
|
const line = node.startPosition.row + 1;
|
|
13243
13330
|
out.push({
|
|
13244
|
-
infraId: (0,
|
|
13331
|
+
infraId: (0, import_types40.infraId)("sql-table", table),
|
|
13245
13332
|
name: table,
|
|
13246
13333
|
kind: "sql-table",
|
|
13247
13334
|
edgeType: "CALLS",
|
|
13248
13335
|
confidenceKind: "verified-call-site",
|
|
13249
13336
|
evidence: {
|
|
13250
|
-
file:
|
|
13337
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
13251
13338
|
line,
|
|
13252
13339
|
snippet: snippet(file.content, line)
|
|
13253
13340
|
}
|
|
@@ -13257,10 +13344,10 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
13257
13344
|
}
|
|
13258
13345
|
var AR_ASSOCIATIONS = /* @__PURE__ */ new Set(["belongs_to", "has_many", "has_one"]);
|
|
13259
13346
|
function railsModelForeignKeys(file, serviceDir) {
|
|
13260
|
-
const tree =
|
|
13347
|
+
const tree = parseSource9(makeRubyParser2(), file.content);
|
|
13261
13348
|
const out = [];
|
|
13262
13349
|
const seen = /* @__PURE__ */ new Set();
|
|
13263
|
-
|
|
13350
|
+
walk6(tree.rootNode, (node) => {
|
|
13264
13351
|
if (node.type !== "class" || !isActiveRecordModel(node)) return;
|
|
13265
13352
|
const thisTable = modelTable(node);
|
|
13266
13353
|
if (!thisTable) return;
|
|
@@ -13284,7 +13371,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
13284
13371
|
childTable,
|
|
13285
13372
|
parentTable,
|
|
13286
13373
|
evidence: {
|
|
13287
|
-
file:
|
|
13374
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
13288
13375
|
line,
|
|
13289
13376
|
snippet: snippet(file.content, line)
|
|
13290
13377
|
}
|
|
@@ -13296,27 +13383,27 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
13296
13383
|
|
|
13297
13384
|
// src/extract/calls/eloquent.ts
|
|
13298
13385
|
init_cjs_shims();
|
|
13299
|
-
var
|
|
13300
|
-
var
|
|
13386
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
13387
|
+
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
13301
13388
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
13302
|
-
var
|
|
13389
|
+
var import_types41 = require("@neat.is/types");
|
|
13303
13390
|
var LARAVEL_SCHEMA_RE = /\bSchema::(create|createIfNotExists|table)\b/;
|
|
13304
|
-
var
|
|
13391
|
+
var PARSE_CHUNK10 = 16384;
|
|
13305
13392
|
function makePhpParser2() {
|
|
13306
|
-
const p = new
|
|
13393
|
+
const p = new import_tree_sitter14.default();
|
|
13307
13394
|
p.setLanguage(import_tree_sitter_php3.default.php_only);
|
|
13308
13395
|
return p;
|
|
13309
13396
|
}
|
|
13310
|
-
function
|
|
13397
|
+
function parseSource10(parser, source) {
|
|
13311
13398
|
return parser.parse(
|
|
13312
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
13399
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
|
|
13313
13400
|
);
|
|
13314
13401
|
}
|
|
13315
|
-
function
|
|
13402
|
+
function walk7(node, visit) {
|
|
13316
13403
|
visit(node);
|
|
13317
13404
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13318
13405
|
const c = node.namedChild(i);
|
|
13319
|
-
if (c)
|
|
13406
|
+
if (c) walk7(c, visit);
|
|
13320
13407
|
}
|
|
13321
13408
|
}
|
|
13322
13409
|
function phpStaticString2(node) {
|
|
@@ -13527,9 +13614,9 @@ function columnsFromCall(chain, blueprintVar) {
|
|
|
13527
13614
|
}
|
|
13528
13615
|
function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
13529
13616
|
if (!LARAVEL_SCHEMA_RE.test(file.content)) return [];
|
|
13530
|
-
const tree =
|
|
13617
|
+
const tree = parseSource10(makePhpParser2(), file.content);
|
|
13531
13618
|
const out = [];
|
|
13532
|
-
|
|
13619
|
+
walk7(tree.rootNode, (node) => {
|
|
13533
13620
|
if (!isSchemaCall(node)) return;
|
|
13534
13621
|
const bp = readBlueprint(node);
|
|
13535
13622
|
if (!bp) return;
|
|
@@ -13546,14 +13633,14 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
13546
13633
|
}
|
|
13547
13634
|
}
|
|
13548
13635
|
out.push({
|
|
13549
|
-
infraId: (0,
|
|
13636
|
+
infraId: (0, import_types41.infraId)("sql-table", bp.table),
|
|
13550
13637
|
name: bp.table,
|
|
13551
13638
|
kind: "sql-table",
|
|
13552
13639
|
edgeType: "CALLS",
|
|
13553
13640
|
confidenceKind: "structural",
|
|
13554
13641
|
...columns.length > 0 ? { columns } : {},
|
|
13555
13642
|
evidence: {
|
|
13556
|
-
file:
|
|
13643
|
+
file: import_node_path52.default.relative(serviceDir, file.path),
|
|
13557
13644
|
line: bp.line,
|
|
13558
13645
|
snippet: snippet(file.content, bp.line)
|
|
13559
13646
|
}
|
|
@@ -13563,7 +13650,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
13563
13650
|
}
|
|
13564
13651
|
function laravelMigrationForeignKeys(file, serviceDir) {
|
|
13565
13652
|
if (!LARAVEL_SCHEMA_RE.test(file.content)) return [];
|
|
13566
|
-
const tree =
|
|
13653
|
+
const tree = parseSource10(makePhpParser2(), file.content);
|
|
13567
13654
|
const out = [];
|
|
13568
13655
|
const seen = /* @__PURE__ */ new Set();
|
|
13569
13656
|
const emit = (childTable, parentTable, line) => {
|
|
@@ -13575,13 +13662,13 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
13575
13662
|
childTable,
|
|
13576
13663
|
parentTable,
|
|
13577
13664
|
evidence: {
|
|
13578
|
-
file:
|
|
13665
|
+
file: import_node_path52.default.relative(serviceDir, file.path),
|
|
13579
13666
|
line,
|
|
13580
13667
|
snippet: snippet(file.content, line)
|
|
13581
13668
|
}
|
|
13582
13669
|
});
|
|
13583
13670
|
};
|
|
13584
|
-
|
|
13671
|
+
walk7(tree.rootNode, (node) => {
|
|
13585
13672
|
if (!isSchemaCall(node)) return;
|
|
13586
13673
|
const bp = readBlueprint(node);
|
|
13587
13674
|
if (!bp || !bp.closureBody) return;
|
|
@@ -13679,23 +13766,23 @@ function walkWithinClass(cls, visit) {
|
|
|
13679
13766
|
recur(body);
|
|
13680
13767
|
}
|
|
13681
13768
|
function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
13682
|
-
const tree =
|
|
13769
|
+
const tree = parseSource10(makePhpParser2(), file.content);
|
|
13683
13770
|
const out = [];
|
|
13684
13771
|
const seen = /* @__PURE__ */ new Set();
|
|
13685
|
-
|
|
13772
|
+
walk7(tree.rootNode, (node) => {
|
|
13686
13773
|
if (node.type !== "class_declaration" || !isEloquentModel(node)) return;
|
|
13687
13774
|
const table = modelTable2(node);
|
|
13688
13775
|
if (!table || seen.has(table)) return;
|
|
13689
13776
|
seen.add(table);
|
|
13690
13777
|
const line = node.startPosition.row + 1;
|
|
13691
13778
|
out.push({
|
|
13692
|
-
infraId: (0,
|
|
13779
|
+
infraId: (0, import_types41.infraId)("sql-table", table),
|
|
13693
13780
|
name: table,
|
|
13694
13781
|
kind: "sql-table",
|
|
13695
13782
|
edgeType: "CALLS",
|
|
13696
13783
|
confidenceKind: "verified-call-site",
|
|
13697
13784
|
evidence: {
|
|
13698
|
-
file:
|
|
13785
|
+
file: import_node_path52.default.relative(serviceDir, file.path),
|
|
13699
13786
|
line,
|
|
13700
13787
|
snippet: snippet(file.content, line)
|
|
13701
13788
|
}
|
|
@@ -13705,10 +13792,10 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
13705
13792
|
}
|
|
13706
13793
|
var ELOQUENT_RELATIONS = /* @__PURE__ */ new Set(["belongsTo", "hasMany", "hasOne"]);
|
|
13707
13794
|
function laravelModelForeignKeys(file, serviceDir) {
|
|
13708
|
-
const tree =
|
|
13795
|
+
const tree = parseSource10(makePhpParser2(), file.content);
|
|
13709
13796
|
const out = [];
|
|
13710
13797
|
const seen = /* @__PURE__ */ new Set();
|
|
13711
|
-
|
|
13798
|
+
walk7(tree.rootNode, (node) => {
|
|
13712
13799
|
if (node.type !== "class_declaration" || !isEloquentModel(node)) return;
|
|
13713
13800
|
const thisTable = modelTable2(node);
|
|
13714
13801
|
if (!thisTable) return;
|
|
@@ -13731,7 +13818,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
13731
13818
|
childTable,
|
|
13732
13819
|
parentTable,
|
|
13733
13820
|
evidence: {
|
|
13734
|
-
file:
|
|
13821
|
+
file: import_node_path52.default.relative(serviceDir, file.path),
|
|
13735
13822
|
line,
|
|
13736
13823
|
snippet: snippet(file.content, line)
|
|
13737
13824
|
}
|
|
@@ -13743,12 +13830,12 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
13743
13830
|
|
|
13744
13831
|
// src/extract/calls/go.ts
|
|
13745
13832
|
init_cjs_shims();
|
|
13746
|
-
var
|
|
13747
|
-
var
|
|
13833
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
13834
|
+
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
13748
13835
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
13749
|
-
var
|
|
13836
|
+
var import_types42 = require("@neat.is/types");
|
|
13750
13837
|
init_otel();
|
|
13751
|
-
var
|
|
13838
|
+
var PARSE_CHUNK11 = 16384;
|
|
13752
13839
|
var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
|
|
13753
13840
|
"Query",
|
|
13754
13841
|
"QueryContext",
|
|
@@ -13774,20 +13861,20 @@ var SQLX_METHODS = /* @__PURE__ */ new Set([
|
|
|
13774
13861
|
var DATABASE_SQL_IMPORT = "database/sql";
|
|
13775
13862
|
var SQLX_IMPORT = "github.com/jmoiron/sqlx";
|
|
13776
13863
|
function makeGoParser3() {
|
|
13777
|
-
const p = new
|
|
13864
|
+
const p = new import_tree_sitter15.default();
|
|
13778
13865
|
p.setLanguage(import_tree_sitter_go4.default);
|
|
13779
13866
|
return p;
|
|
13780
13867
|
}
|
|
13781
|
-
function
|
|
13868
|
+
function parseSource11(parser, source) {
|
|
13782
13869
|
return parser.parse(
|
|
13783
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
13870
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
|
|
13784
13871
|
);
|
|
13785
13872
|
}
|
|
13786
|
-
function
|
|
13873
|
+
function walk8(node, visit) {
|
|
13787
13874
|
visit(node);
|
|
13788
13875
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13789
13876
|
const child = node.namedChild(i);
|
|
13790
|
-
if (child)
|
|
13877
|
+
if (child) walk8(child, visit);
|
|
13791
13878
|
}
|
|
13792
13879
|
}
|
|
13793
13880
|
function goStringLiteralValue(node) {
|
|
@@ -13800,7 +13887,7 @@ function goStringLiteralValue(node) {
|
|
|
13800
13887
|
}
|
|
13801
13888
|
function goImportsAny(root, names) {
|
|
13802
13889
|
let found = false;
|
|
13803
|
-
|
|
13890
|
+
walk8(root, (node) => {
|
|
13804
13891
|
if (found || node.type !== "import_spec") return;
|
|
13805
13892
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13806
13893
|
const value = goStringLiteralValue(node.namedChild(i));
|
|
@@ -13818,14 +13905,14 @@ function firstStringLiteralArg(argsNode) {
|
|
|
13818
13905
|
return null;
|
|
13819
13906
|
}
|
|
13820
13907
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
13821
|
-
if (
|
|
13908
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13822
13909
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
13823
|
-
const tree =
|
|
13910
|
+
const tree = parseSource11(makeGoParser3(), file.content);
|
|
13824
13911
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
13825
13912
|
const importsSqlx = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([SQLX_IMPORT]));
|
|
13826
13913
|
if (!importsDatabaseSql && !importsSqlx) return [];
|
|
13827
13914
|
const out = [];
|
|
13828
|
-
|
|
13915
|
+
walk8(tree.rootNode, (node) => {
|
|
13829
13916
|
if (node.type !== "call_expression") return;
|
|
13830
13917
|
const fn = node.childForFieldName("function");
|
|
13831
13918
|
if (fn?.type !== "selector_expression") return;
|
|
@@ -13840,14 +13927,14 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
13840
13927
|
const columns = columnsFromSqlStatement(sql);
|
|
13841
13928
|
const line = node.startPosition.row + 1;
|
|
13842
13929
|
out.push({
|
|
13843
|
-
infraId: (0,
|
|
13930
|
+
infraId: (0, import_types42.infraId)("sql-table", table),
|
|
13844
13931
|
name: table,
|
|
13845
13932
|
kind: "sql-table",
|
|
13846
13933
|
edgeType: "CALLS",
|
|
13847
13934
|
confidenceKind: "verified-call-site",
|
|
13848
13935
|
...columns.length > 0 ? { columns } : {},
|
|
13849
13936
|
evidence: {
|
|
13850
|
-
file: toPosix(
|
|
13937
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13851
13938
|
line,
|
|
13852
13939
|
snippet: snippet(file.content, line)
|
|
13853
13940
|
}
|
|
@@ -13858,27 +13945,27 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
13858
13945
|
|
|
13859
13946
|
// src/extract/calls/gorm.ts
|
|
13860
13947
|
init_cjs_shims();
|
|
13861
|
-
var
|
|
13862
|
-
var
|
|
13948
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
13949
|
+
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
13863
13950
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
13864
|
-
var
|
|
13951
|
+
var import_types43 = require("@neat.is/types");
|
|
13865
13952
|
var GORM_IMPORT_RE = /gorm\.io\/gorm/;
|
|
13866
|
-
var
|
|
13953
|
+
var PARSE_CHUNK12 = 16384;
|
|
13867
13954
|
function makeGoParser4() {
|
|
13868
|
-
const p = new
|
|
13955
|
+
const p = new import_tree_sitter16.default();
|
|
13869
13956
|
p.setLanguage(import_tree_sitter_go5.default);
|
|
13870
13957
|
return p;
|
|
13871
13958
|
}
|
|
13872
|
-
function
|
|
13959
|
+
function parseSource12(parser, source) {
|
|
13873
13960
|
return parser.parse(
|
|
13874
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
13961
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
|
|
13875
13962
|
);
|
|
13876
13963
|
}
|
|
13877
|
-
function
|
|
13964
|
+
function walk9(node, visit) {
|
|
13878
13965
|
visit(node);
|
|
13879
13966
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
13880
13967
|
const c = node.namedChild(i);
|
|
13881
|
-
if (c)
|
|
13968
|
+
if (c) walk9(c, visit);
|
|
13882
13969
|
}
|
|
13883
13970
|
}
|
|
13884
13971
|
var COMMON_INITIALISMS = [
|
|
@@ -14111,7 +14198,7 @@ function readField(fieldDecl) {
|
|
|
14111
14198
|
}
|
|
14112
14199
|
function collectStructs(tree) {
|
|
14113
14200
|
const structs = /* @__PURE__ */ new Map();
|
|
14114
|
-
|
|
14201
|
+
walk9(tree.rootNode, (node) => {
|
|
14115
14202
|
if (node.type !== "type_spec") return;
|
|
14116
14203
|
const nameNode = node.childForFieldName("name");
|
|
14117
14204
|
const typeNode = node.childForFieldName("type");
|
|
@@ -14160,7 +14247,7 @@ function compositeStructName(arg) {
|
|
|
14160
14247
|
}
|
|
14161
14248
|
function collectCallModels(tree) {
|
|
14162
14249
|
const models = /* @__PURE__ */ new Set();
|
|
14163
|
-
|
|
14250
|
+
walk9(tree.rootNode, (node) => {
|
|
14164
14251
|
if (node.type !== "call_expression") return;
|
|
14165
14252
|
const fn = node.childForFieldName("function");
|
|
14166
14253
|
if (fn?.type !== "selector_expression") return;
|
|
@@ -14180,7 +14267,7 @@ function collectCallModels(tree) {
|
|
|
14180
14267
|
function collectTableNameOverrides(tree) {
|
|
14181
14268
|
const overrides = /* @__PURE__ */ new Map();
|
|
14182
14269
|
const declarers = /* @__PURE__ */ new Set();
|
|
14183
|
-
|
|
14270
|
+
walk9(tree.rootNode, (node) => {
|
|
14184
14271
|
if (node.type !== "method_declaration") return;
|
|
14185
14272
|
if (node.childForFieldName("name")?.text !== "TableName") return;
|
|
14186
14273
|
const receiver = node.childForFieldName("receiver");
|
|
@@ -14197,7 +14284,7 @@ function collectTableNameOverrides(tree) {
|
|
|
14197
14284
|
const body = node.childForFieldName("body");
|
|
14198
14285
|
if (!body) return;
|
|
14199
14286
|
let literal = null;
|
|
14200
|
-
|
|
14287
|
+
walk9(body, (n) => {
|
|
14201
14288
|
if (literal !== null) return;
|
|
14202
14289
|
if (n.type !== "return_statement") return;
|
|
14203
14290
|
const exprList = n.namedChild(0);
|
|
@@ -14291,9 +14378,9 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
14291
14378
|
seen.delete(struct.name);
|
|
14292
14379
|
}
|
|
14293
14380
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
14294
|
-
if (
|
|
14381
|
+
if (import_node_path56.default.extname(file.path) !== ".go") return [];
|
|
14295
14382
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
14296
|
-
const tree =
|
|
14383
|
+
const tree = parseSource12(makeGoParser4(), file.content);
|
|
14297
14384
|
const { structs, models, tableFor } = analyze(tree);
|
|
14298
14385
|
const out = [];
|
|
14299
14386
|
const seenTables = /* @__PURE__ */ new Set();
|
|
@@ -14306,14 +14393,14 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
14306
14393
|
const columns = [];
|
|
14307
14394
|
collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
|
|
14308
14395
|
out.push({
|
|
14309
|
-
infraId: (0,
|
|
14396
|
+
infraId: (0, import_types43.infraId)("sql-table", table),
|
|
14310
14397
|
name: table,
|
|
14311
14398
|
kind: "sql-table",
|
|
14312
14399
|
edgeType: "CALLS",
|
|
14313
14400
|
confidenceKind: "structural",
|
|
14314
14401
|
...columns.length > 0 ? { columns } : {},
|
|
14315
14402
|
evidence: {
|
|
14316
|
-
file: toPosix(
|
|
14403
|
+
file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
|
|
14317
14404
|
line: struct.line,
|
|
14318
14405
|
snippet: snippet(file.content, struct.line)
|
|
14319
14406
|
}
|
|
@@ -14322,9 +14409,9 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
14322
14409
|
return out;
|
|
14323
14410
|
}
|
|
14324
14411
|
function gormForeignKeys(file, serviceDir) {
|
|
14325
|
-
if (
|
|
14412
|
+
if (import_node_path56.default.extname(file.path) !== ".go") return [];
|
|
14326
14413
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
14327
|
-
const tree =
|
|
14414
|
+
const tree = parseSource12(makeGoParser4(), file.content);
|
|
14328
14415
|
const { structs, models, tableFor } = analyze(tree);
|
|
14329
14416
|
const out = [];
|
|
14330
14417
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14337,7 +14424,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
14337
14424
|
childTable,
|
|
14338
14425
|
parentTable,
|
|
14339
14426
|
evidence: {
|
|
14340
|
-
file: toPosix(
|
|
14427
|
+
file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
|
|
14341
14428
|
line,
|
|
14342
14429
|
snippet: snippet(file.content, line)
|
|
14343
14430
|
}
|
|
@@ -14374,27 +14461,27 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
14374
14461
|
|
|
14375
14462
|
// src/extract/calls/efcore.ts
|
|
14376
14463
|
init_cjs_shims();
|
|
14377
|
-
var
|
|
14378
|
-
var
|
|
14464
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
14465
|
+
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
14379
14466
|
var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
14380
|
-
var
|
|
14467
|
+
var import_types44 = require("@neat.is/types");
|
|
14381
14468
|
var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
|
|
14382
|
-
var
|
|
14469
|
+
var PARSE_CHUNK13 = 16384;
|
|
14383
14470
|
function makeCsParser() {
|
|
14384
|
-
const p = new
|
|
14471
|
+
const p = new import_tree_sitter17.default();
|
|
14385
14472
|
p.setLanguage(import_tree_sitter_c_sharp2.default);
|
|
14386
14473
|
return p;
|
|
14387
14474
|
}
|
|
14388
|
-
function
|
|
14475
|
+
function parseSource13(parser, source) {
|
|
14389
14476
|
return parser.parse(
|
|
14390
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
14477
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK13)
|
|
14391
14478
|
);
|
|
14392
14479
|
}
|
|
14393
|
-
function
|
|
14480
|
+
function walk10(node, visit) {
|
|
14394
14481
|
visit(node);
|
|
14395
14482
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14396
14483
|
const c = node.namedChild(i);
|
|
14397
|
-
if (c)
|
|
14484
|
+
if (c) walk10(c, visit);
|
|
14398
14485
|
}
|
|
14399
14486
|
}
|
|
14400
14487
|
function firstChildOfType(node, type) {
|
|
@@ -14453,28 +14540,28 @@ function tableFromToTable(call) {
|
|
|
14453
14540
|
return value ? csStringLiteral(value) : null;
|
|
14454
14541
|
}
|
|
14455
14542
|
function efcoreEndpointsFromFile(file, serviceDir) {
|
|
14456
|
-
if (
|
|
14543
|
+
if (import_node_path57.default.extname(file.path) !== ".cs") return [];
|
|
14457
14544
|
if (!EFCORE_GATE.test(file.content)) return [];
|
|
14458
|
-
const tree =
|
|
14545
|
+
const tree = parseSource13(makeCsParser(), file.content);
|
|
14459
14546
|
const out = [];
|
|
14460
14547
|
const seen = /* @__PURE__ */ new Set();
|
|
14461
14548
|
const push = (name, line) => {
|
|
14462
14549
|
if (!name || seen.has(name)) return;
|
|
14463
14550
|
seen.add(name);
|
|
14464
14551
|
out.push({
|
|
14465
|
-
infraId: (0,
|
|
14552
|
+
infraId: (0, import_types44.infraId)("sql-table", name),
|
|
14466
14553
|
name,
|
|
14467
14554
|
kind: "sql-table",
|
|
14468
14555
|
edgeType: "CALLS",
|
|
14469
14556
|
confidenceKind: "structural",
|
|
14470
14557
|
evidence: {
|
|
14471
|
-
file: toPosix(
|
|
14558
|
+
file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
|
|
14472
14559
|
line,
|
|
14473
14560
|
snippet: snippet(file.content, line)
|
|
14474
14561
|
}
|
|
14475
14562
|
});
|
|
14476
14563
|
};
|
|
14477
|
-
|
|
14564
|
+
walk10(tree.rootNode, (node) => {
|
|
14478
14565
|
if (node.type === "attribute") {
|
|
14479
14566
|
const table = tableFromAttribute(node);
|
|
14480
14567
|
if (table) push(table, node.startPosition.row + 1);
|
|
@@ -14492,11 +14579,11 @@ function efcoreEndpointsFromFile(file, serviceDir) {
|
|
|
14492
14579
|
function edgeTypeFromEndpoint(ep) {
|
|
14493
14580
|
switch (ep.edgeType) {
|
|
14494
14581
|
case "PUBLISHES_TO":
|
|
14495
|
-
return
|
|
14582
|
+
return import_types45.EdgeType.PUBLISHES_TO;
|
|
14496
14583
|
case "CONSUMES_FROM":
|
|
14497
|
-
return
|
|
14584
|
+
return import_types45.EdgeType.CONSUMES_FROM;
|
|
14498
14585
|
default:
|
|
14499
|
-
return
|
|
14586
|
+
return import_types45.EdgeType.CALLS;
|
|
14500
14587
|
}
|
|
14501
14588
|
}
|
|
14502
14589
|
function isAwsKind(kind) {
|
|
@@ -14551,6 +14638,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
14551
14638
|
} catch (err) {
|
|
14552
14639
|
recordExtractionError("efcore data-axis extraction", file.path, err);
|
|
14553
14640
|
}
|
|
14641
|
+
try {
|
|
14642
|
+
endpoints.push(...socketEndpointsFromFile(file, service.dir));
|
|
14643
|
+
} catch (err) {
|
|
14644
|
+
recordExtractionError("python socket call extraction", file.path, err);
|
|
14645
|
+
}
|
|
14554
14646
|
}
|
|
14555
14647
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
14556
14648
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
@@ -14561,7 +14653,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
14561
14653
|
if (!graph.hasNode(ep.infraId)) {
|
|
14562
14654
|
const node = {
|
|
14563
14655
|
id: ep.infraId,
|
|
14564
|
-
type:
|
|
14656
|
+
type: import_types45.NodeType.InfraNode,
|
|
14565
14657
|
name: ep.name,
|
|
14566
14658
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
14567
14659
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -14574,21 +14666,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
14574
14666
|
}
|
|
14575
14667
|
if (ep.columns && ep.columns.length > 0) {
|
|
14576
14668
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
14577
|
-
if (node.type ===
|
|
14669
|
+
if (node.type === import_types45.NodeType.InfraNode) {
|
|
14578
14670
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
14579
14671
|
...node,
|
|
14580
14672
|
columns: foldColumns(
|
|
14581
14673
|
node.columns,
|
|
14582
14674
|
ep.columns,
|
|
14583
|
-
|
|
14584
|
-
(0,
|
|
14675
|
+
import_types45.Provenance.EXTRACTED,
|
|
14676
|
+
(0, import_types45.confidenceForExtracted)(ep.confidenceKind)
|
|
14585
14677
|
)
|
|
14586
14678
|
});
|
|
14587
14679
|
}
|
|
14588
14680
|
}
|
|
14589
14681
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
14590
14682
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
14591
|
-
if (node.type ===
|
|
14683
|
+
if (node.type === import_types45.NodeType.InfraNode) {
|
|
14592
14684
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
14593
14685
|
...node,
|
|
14594
14686
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -14596,7 +14688,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
14596
14688
|
}
|
|
14597
14689
|
}
|
|
14598
14690
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
14599
|
-
const confidence = (0,
|
|
14691
|
+
const confidence = (0, import_types45.confidenceForExtracted)(ep.confidenceKind);
|
|
14600
14692
|
const relFile = toPosix(ep.evidence.file);
|
|
14601
14693
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
14602
14694
|
graph,
|
|
@@ -14606,7 +14698,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
14606
14698
|
);
|
|
14607
14699
|
nodesAdded += n;
|
|
14608
14700
|
edgesAdded += e;
|
|
14609
|
-
if (!(0,
|
|
14701
|
+
if (!(0, import_types45.passesExtractedFloor)(confidence)) {
|
|
14610
14702
|
noteExtractedDropped({
|
|
14611
14703
|
source: fileNodeId,
|
|
14612
14704
|
target: ep.infraId,
|
|
@@ -14626,7 +14718,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
14626
14718
|
source: fileNodeId,
|
|
14627
14719
|
target: ep.infraId,
|
|
14628
14720
|
type: edgeType,
|
|
14629
|
-
provenance:
|
|
14721
|
+
provenance: import_types45.Provenance.EXTRACTED,
|
|
14630
14722
|
confidence,
|
|
14631
14723
|
evidence: ep.evidence
|
|
14632
14724
|
};
|
|
@@ -14649,7 +14741,7 @@ async function addCallEdges(graph, services) {
|
|
|
14649
14741
|
|
|
14650
14742
|
// src/extract/table-edges.ts
|
|
14651
14743
|
init_cjs_shims();
|
|
14652
|
-
var
|
|
14744
|
+
var import_types46 = require("@neat.is/types");
|
|
14653
14745
|
async function addTableEdges(graph, services) {
|
|
14654
14746
|
let nodesAdded = 0;
|
|
14655
14747
|
let edgesAdded = 0;
|
|
@@ -14677,20 +14769,20 @@ async function addTableEdges(graph, services) {
|
|
|
14677
14769
|
}
|
|
14678
14770
|
refs.push(...modelRefs);
|
|
14679
14771
|
for (const ref of refs) {
|
|
14680
|
-
const childId = (0,
|
|
14681
|
-
const parentId = (0,
|
|
14772
|
+
const childId = (0, import_types46.infraId)("sql-table", ref.childTable);
|
|
14773
|
+
const parentId = (0, import_types46.infraId)("sql-table", ref.parentTable);
|
|
14682
14774
|
if (childId === parentId) continue;
|
|
14683
14775
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
14684
14776
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
14685
|
-
const edgeId = (0,
|
|
14777
|
+
const edgeId = (0, import_types46.extractedEdgeId)(childId, parentId, import_types46.EdgeType.REFERENCES);
|
|
14686
14778
|
if (graph.hasEdge(edgeId)) continue;
|
|
14687
14779
|
const edge = {
|
|
14688
14780
|
id: edgeId,
|
|
14689
14781
|
source: childId,
|
|
14690
14782
|
target: parentId,
|
|
14691
|
-
type:
|
|
14692
|
-
provenance:
|
|
14693
|
-
confidence: (0,
|
|
14783
|
+
type: import_types46.EdgeType.REFERENCES,
|
|
14784
|
+
provenance: import_types46.Provenance.EXTRACTED,
|
|
14785
|
+
confidence: (0, import_types46.confidenceForExtracted)("structural"),
|
|
14694
14786
|
evidence: ref.evidence
|
|
14695
14787
|
};
|
|
14696
14788
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -14703,7 +14795,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
14703
14795
|
if (graph.hasNode(id)) return 0;
|
|
14704
14796
|
const node = {
|
|
14705
14797
|
id,
|
|
14706
|
-
type:
|
|
14798
|
+
type: import_types46.NodeType.InfraNode,
|
|
14707
14799
|
name,
|
|
14708
14800
|
provider: "self",
|
|
14709
14801
|
kind: "sql-table"
|
|
@@ -14717,16 +14809,16 @@ init_cjs_shims();
|
|
|
14717
14809
|
|
|
14718
14810
|
// src/extract/infra/docker-compose.ts
|
|
14719
14811
|
init_cjs_shims();
|
|
14720
|
-
var
|
|
14721
|
-
var
|
|
14812
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14813
|
+
var import_types48 = require("@neat.is/types");
|
|
14722
14814
|
|
|
14723
14815
|
// src/extract/infra/shared.ts
|
|
14724
14816
|
init_cjs_shims();
|
|
14725
|
-
var
|
|
14817
|
+
var import_types47 = require("@neat.is/types");
|
|
14726
14818
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
14727
14819
|
return {
|
|
14728
|
-
id: (0,
|
|
14729
|
-
type:
|
|
14820
|
+
id: (0, import_types47.infraId)(kind, name),
|
|
14821
|
+
type: import_types47.NodeType.InfraNode,
|
|
14730
14822
|
name,
|
|
14731
14823
|
provider,
|
|
14732
14824
|
kind,
|
|
@@ -14770,8 +14862,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
14770
14862
|
source: anchorId,
|
|
14771
14863
|
target: node.id,
|
|
14772
14864
|
type: edgeType,
|
|
14773
|
-
provenance:
|
|
14774
|
-
confidence: (0,
|
|
14865
|
+
provenance: import_types47.Provenance.EXTRACTED,
|
|
14866
|
+
confidence: (0, import_types47.confidenceForExtracted)("structural"),
|
|
14775
14867
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
14776
14868
|
};
|
|
14777
14869
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14788,7 +14880,7 @@ function dependsOnList(value) {
|
|
|
14788
14880
|
}
|
|
14789
14881
|
function serviceNameToServiceNode(name, services) {
|
|
14790
14882
|
for (const s of services) {
|
|
14791
|
-
if (s.node.name === name ||
|
|
14883
|
+
if (s.node.name === name || import_node_path58.default.basename(s.dir) === name) return s.node.id;
|
|
14792
14884
|
}
|
|
14793
14885
|
return null;
|
|
14794
14886
|
}
|
|
@@ -14797,7 +14889,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
14797
14889
|
let edgesAdded = 0;
|
|
14798
14890
|
let composePath = null;
|
|
14799
14891
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
14800
|
-
const abs =
|
|
14892
|
+
const abs = import_node_path58.default.join(scanPath, name);
|
|
14801
14893
|
if (await exists(abs)) {
|
|
14802
14894
|
composePath = abs;
|
|
14803
14895
|
break;
|
|
@@ -14810,13 +14902,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
14810
14902
|
} catch (err) {
|
|
14811
14903
|
recordExtractionError(
|
|
14812
14904
|
"infra docker-compose",
|
|
14813
|
-
|
|
14905
|
+
import_node_path58.default.relative(scanPath, composePath),
|
|
14814
14906
|
err
|
|
14815
14907
|
);
|
|
14816
14908
|
return { nodesAdded, edgesAdded };
|
|
14817
14909
|
}
|
|
14818
14910
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
14819
|
-
const evidenceFile =
|
|
14911
|
+
const evidenceFile = import_node_path58.default.relative(scanPath, composePath).split(import_node_path58.default.sep).join("/");
|
|
14820
14912
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
14821
14913
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
14822
14914
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -14838,15 +14930,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
14838
14930
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
14839
14931
|
const targetId = composeNameToNodeId.get(dep);
|
|
14840
14932
|
if (!targetId) continue;
|
|
14841
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
14933
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types48.EdgeType.DEPENDS_ON);
|
|
14842
14934
|
if (graph.hasEdge(edgeId)) continue;
|
|
14843
14935
|
const edge = {
|
|
14844
14936
|
id: edgeId,
|
|
14845
14937
|
source: sourceId,
|
|
14846
14938
|
target: targetId,
|
|
14847
|
-
type:
|
|
14848
|
-
provenance:
|
|
14849
|
-
confidence: (0,
|
|
14939
|
+
type: import_types48.EdgeType.DEPENDS_ON,
|
|
14940
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14941
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
14850
14942
|
evidence: { file: evidenceFile }
|
|
14851
14943
|
};
|
|
14852
14944
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14858,9 +14950,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
14858
14950
|
|
|
14859
14951
|
// src/extract/infra/dockerfile.ts
|
|
14860
14952
|
init_cjs_shims();
|
|
14861
|
-
var
|
|
14953
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14862
14954
|
var import_node_fs25 = require("fs");
|
|
14863
|
-
var
|
|
14955
|
+
var import_types49 = require("@neat.is/types");
|
|
14864
14956
|
function readDockerfile(content) {
|
|
14865
14957
|
let image = null;
|
|
14866
14958
|
const ports = [];
|
|
@@ -14889,7 +14981,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
14889
14981
|
let nodesAdded = 0;
|
|
14890
14982
|
let edgesAdded = 0;
|
|
14891
14983
|
for (const service of services) {
|
|
14892
|
-
const dockerfilePath =
|
|
14984
|
+
const dockerfilePath = import_node_path59.default.join(service.dir, "Dockerfile");
|
|
14893
14985
|
if (!await exists(dockerfilePath)) continue;
|
|
14894
14986
|
let content;
|
|
14895
14987
|
try {
|
|
@@ -14897,7 +14989,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
14897
14989
|
} catch (err) {
|
|
14898
14990
|
recordExtractionError(
|
|
14899
14991
|
"infra dockerfile",
|
|
14900
|
-
|
|
14992
|
+
import_node_path59.default.relative(scanPath, dockerfilePath),
|
|
14901
14993
|
err
|
|
14902
14994
|
);
|
|
14903
14995
|
continue;
|
|
@@ -14909,8 +15001,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
14909
15001
|
graph.addNode(node.id, node);
|
|
14910
15002
|
nodesAdded++;
|
|
14911
15003
|
}
|
|
14912
|
-
const relDockerfile = toPosix(
|
|
14913
|
-
const evidenceFile = toPosix(
|
|
15004
|
+
const relDockerfile = toPosix(import_node_path59.default.relative(service.dir, dockerfilePath));
|
|
15005
|
+
const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, dockerfilePath));
|
|
14914
15006
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
14915
15007
|
graph,
|
|
14916
15008
|
service.pkg.name,
|
|
@@ -14919,15 +15011,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
14919
15011
|
);
|
|
14920
15012
|
nodesAdded += fn;
|
|
14921
15013
|
edgesAdded += fe;
|
|
14922
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
15014
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types49.EdgeType.RUNS_ON);
|
|
14923
15015
|
if (!graph.hasEdge(edgeId)) {
|
|
14924
15016
|
const edge = {
|
|
14925
15017
|
id: edgeId,
|
|
14926
15018
|
source: fileNodeId,
|
|
14927
15019
|
target: node.id,
|
|
14928
|
-
type:
|
|
14929
|
-
provenance:
|
|
14930
|
-
confidence: (0,
|
|
15020
|
+
type: import_types49.EdgeType.RUNS_ON,
|
|
15021
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
15022
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
14931
15023
|
evidence: {
|
|
14932
15024
|
file: evidenceFile,
|
|
14933
15025
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -14942,15 +15034,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
14942
15034
|
graph.addNode(portNode.id, portNode);
|
|
14943
15035
|
nodesAdded++;
|
|
14944
15036
|
}
|
|
14945
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
15037
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types49.EdgeType.CONNECTS_TO);
|
|
14946
15038
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
14947
15039
|
const portEdge = {
|
|
14948
15040
|
id: portEdgeId,
|
|
14949
15041
|
source: fileNodeId,
|
|
14950
15042
|
target: portNode.id,
|
|
14951
|
-
type:
|
|
14952
|
-
provenance:
|
|
14953
|
-
confidence: (0,
|
|
15043
|
+
type: import_types49.EdgeType.CONNECTS_TO,
|
|
15044
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
15045
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
14954
15046
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
14955
15047
|
};
|
|
14956
15048
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -14963,8 +15055,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
14963
15055
|
// src/extract/infra/terraform.ts
|
|
14964
15056
|
init_cjs_shims();
|
|
14965
15057
|
var import_node_fs26 = require("fs");
|
|
14966
|
-
var
|
|
14967
|
-
var
|
|
15058
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
15059
|
+
var import_types50 = require("@neat.is/types");
|
|
14968
15060
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
14969
15061
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
14970
15062
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -14974,11 +15066,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
14974
15066
|
for (const entry2 of entries) {
|
|
14975
15067
|
if (entry2.isDirectory()) {
|
|
14976
15068
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
14977
|
-
const child =
|
|
15069
|
+
const child = import_node_path60.default.join(start, entry2.name);
|
|
14978
15070
|
if (await isPythonVenvDir(child)) continue;
|
|
14979
15071
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
14980
15072
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
14981
|
-
out.push(
|
|
15073
|
+
out.push(import_node_path60.default.join(start, entry2.name));
|
|
14982
15074
|
}
|
|
14983
15075
|
}
|
|
14984
15076
|
return out;
|
|
@@ -15010,7 +15102,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
15010
15102
|
const files = await walkTfFiles(scanPath);
|
|
15011
15103
|
for (const file of files) {
|
|
15012
15104
|
const content = await import_node_fs26.promises.readFile(file, "utf8");
|
|
15013
|
-
const evidenceFile = toPosix(
|
|
15105
|
+
const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, file));
|
|
15014
15106
|
const resources = [];
|
|
15015
15107
|
const byKey = /* @__PURE__ */ new Map();
|
|
15016
15108
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -15045,16 +15137,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
15045
15137
|
if (!target) continue;
|
|
15046
15138
|
if (seen.has(target.nodeId)) continue;
|
|
15047
15139
|
seen.add(target.nodeId);
|
|
15048
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
15140
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types50.EdgeType.DEPENDS_ON);
|
|
15049
15141
|
if (graph.hasEdge(edgeId)) continue;
|
|
15050
15142
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
15051
15143
|
const edge = {
|
|
15052
15144
|
id: edgeId,
|
|
15053
15145
|
source: resource.nodeId,
|
|
15054
15146
|
target: target.nodeId,
|
|
15055
|
-
type:
|
|
15056
|
-
provenance:
|
|
15057
|
-
confidence: (0,
|
|
15147
|
+
type: import_types50.EdgeType.DEPENDS_ON,
|
|
15148
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
15149
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
15058
15150
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
15059
15151
|
};
|
|
15060
15152
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -15068,8 +15160,9 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
15068
15160
|
// src/extract/infra/k8s.ts
|
|
15069
15161
|
init_cjs_shims();
|
|
15070
15162
|
var import_node_fs27 = require("fs");
|
|
15071
|
-
var
|
|
15163
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
15072
15164
|
var import_yaml3 = require("yaml");
|
|
15165
|
+
var import_types51 = require("@neat.is/types");
|
|
15073
15166
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
15074
15167
|
Service: "k8s-service",
|
|
15075
15168
|
Deployment: "k8s-deployment",
|
|
@@ -15079,6 +15172,7 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
15079
15172
|
Job: "k8s-job",
|
|
15080
15173
|
Ingress: "k8s-ingress"
|
|
15081
15174
|
};
|
|
15175
|
+
var WORKLOAD_KINDS = /* @__PURE__ */ new Set(["Deployment", "StatefulSet", "DaemonSet"]);
|
|
15082
15176
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
15083
15177
|
if (depth > max) return [];
|
|
15084
15178
|
const out = [];
|
|
@@ -15086,17 +15180,18 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
15086
15180
|
for (const entry2 of entries) {
|
|
15087
15181
|
if (entry2.isDirectory()) {
|
|
15088
15182
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
15089
|
-
const child =
|
|
15183
|
+
const child = import_node_path61.default.join(start, entry2.name);
|
|
15090
15184
|
if (await isPythonVenvDir(child)) continue;
|
|
15091
15185
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
15092
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
15093
|
-
out.push(
|
|
15186
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path61.default.extname(entry2.name))) {
|
|
15187
|
+
out.push(import_node_path61.default.join(start, entry2.name));
|
|
15094
15188
|
}
|
|
15095
15189
|
}
|
|
15096
15190
|
return out;
|
|
15097
15191
|
}
|
|
15098
15192
|
async function addK8sResources(graph, scanPath) {
|
|
15099
15193
|
let nodesAdded = 0;
|
|
15194
|
+
let edgesAdded = 0;
|
|
15100
15195
|
const files = await walkYamlFiles2(scanPath);
|
|
15101
15196
|
for (const file of files) {
|
|
15102
15197
|
const content = await import_node_fs27.promises.readFile(file, "utf8");
|
|
@@ -15106,31 +15201,63 @@ async function addK8sResources(graph, scanPath) {
|
|
|
15106
15201
|
} catch {
|
|
15107
15202
|
continue;
|
|
15108
15203
|
}
|
|
15204
|
+
const relFile = toPosix(import_node_path61.default.relative(scanPath, file));
|
|
15109
15205
|
for (const doc of docs) {
|
|
15110
15206
|
if (!doc?.kind || !doc.metadata?.name) continue;
|
|
15111
15207
|
const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind];
|
|
15112
15208
|
if (!infraKind) continue;
|
|
15113
|
-
const
|
|
15114
|
-
const
|
|
15209
|
+
const name = doc.metadata.name;
|
|
15210
|
+
const isWorkload = WORKLOAD_KINDS.has(doc.kind);
|
|
15211
|
+
const image = isWorkload ? doc.spec?.template?.spec?.containers?.[0]?.image : void 0;
|
|
15212
|
+
const replicas = isWorkload && typeof doc.spec?.replicas === "number" ? doc.spec.replicas : void 0;
|
|
15213
|
+
const base = makeInfraNode(infraKind, name, "kubernetes");
|
|
15214
|
+
const node = {
|
|
15215
|
+
...base,
|
|
15216
|
+
...doc.metadata.namespace ? { namespace: doc.metadata.namespace } : {},
|
|
15217
|
+
...image ? { image } : {},
|
|
15218
|
+
...replicas !== void 0 ? { replicas } : {}
|
|
15219
|
+
};
|
|
15115
15220
|
if (!graph.hasNode(node.id)) {
|
|
15116
15221
|
graph.addNode(node.id, node);
|
|
15117
15222
|
nodesAdded++;
|
|
15118
15223
|
}
|
|
15224
|
+
if (isWorkload) {
|
|
15225
|
+
const svcId = (0, import_types51.serviceId)(name);
|
|
15226
|
+
if (graph.hasNode(svcId)) {
|
|
15227
|
+
const svc = graph.getNodeAttributes(svcId);
|
|
15228
|
+
graph.replaceNodeAttributes(svcId, {
|
|
15229
|
+
...svc,
|
|
15230
|
+
platform: svc.platform ?? "kubernetes",
|
|
15231
|
+
...image ? { declaredImage: image } : {},
|
|
15232
|
+
...replicas !== void 0 ? { declaredReplicas: replicas } : {}
|
|
15233
|
+
});
|
|
15234
|
+
edgesAdded += emitPlatformResourceEdge(
|
|
15235
|
+
graph,
|
|
15236
|
+
svcId,
|
|
15237
|
+
import_types51.EdgeType.RUNS_ON,
|
|
15238
|
+
infraKind,
|
|
15239
|
+
name,
|
|
15240
|
+
"kubernetes",
|
|
15241
|
+
relFile,
|
|
15242
|
+
lineContaining(content, image)
|
|
15243
|
+
).edgesAdded;
|
|
15244
|
+
}
|
|
15245
|
+
}
|
|
15119
15246
|
}
|
|
15120
15247
|
}
|
|
15121
|
-
return { nodesAdded, edgesAdded
|
|
15248
|
+
return { nodesAdded, edgesAdded };
|
|
15122
15249
|
}
|
|
15123
15250
|
|
|
15124
15251
|
// src/extract/infra/cloudflare.ts
|
|
15125
15252
|
init_cjs_shims();
|
|
15126
15253
|
var import_node_fs28 = require("fs");
|
|
15127
|
-
var
|
|
15254
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
15128
15255
|
var import_smol_toml3 = require("smol-toml");
|
|
15129
|
-
var
|
|
15256
|
+
var import_types52 = require("@neat.is/types");
|
|
15130
15257
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
15131
15258
|
async function readWranglerConfig(dir) {
|
|
15132
15259
|
for (const filename of WRANGLER_FILENAMES) {
|
|
15133
|
-
const abs =
|
|
15260
|
+
const abs = import_node_path62.default.join(dir, filename);
|
|
15134
15261
|
if (!await exists(abs)) continue;
|
|
15135
15262
|
const raw = await import_node_fs28.promises.readFile(abs, "utf8");
|
|
15136
15263
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -15174,8 +15301,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
15174
15301
|
source: anchorId,
|
|
15175
15302
|
target: node.id,
|
|
15176
15303
|
type: edgeType,
|
|
15177
|
-
provenance:
|
|
15178
|
-
confidence: (0,
|
|
15304
|
+
provenance: import_types52.Provenance.EXTRACTED,
|
|
15305
|
+
confidence: (0, import_types52.confidenceForExtracted)("structural"),
|
|
15179
15306
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
15180
15307
|
};
|
|
15181
15308
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -15193,11 +15320,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15193
15320
|
try {
|
|
15194
15321
|
read = await readWranglerConfig(service.dir);
|
|
15195
15322
|
} catch (err) {
|
|
15196
|
-
recordExtractionError("infra cloudflare",
|
|
15323
|
+
recordExtractionError("infra cloudflare", import_node_path62.default.relative(scanPath, service.dir), err);
|
|
15197
15324
|
continue;
|
|
15198
15325
|
}
|
|
15199
15326
|
if (!read || !read.config.name) continue;
|
|
15200
|
-
const evidenceFile = toPosix(
|
|
15327
|
+
const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, read.relFile)));
|
|
15201
15328
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
15202
15329
|
}
|
|
15203
15330
|
for (const worker of discovered) {
|
|
@@ -15209,7 +15336,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15209
15336
|
}
|
|
15210
15337
|
let anchorId = service.node.id;
|
|
15211
15338
|
if (config.main) {
|
|
15212
|
-
const entryRelPath = toPosix(
|
|
15339
|
+
const entryRelPath = toPosix(import_node_path62.default.normalize(config.main));
|
|
15213
15340
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
15214
15341
|
graph,
|
|
15215
15342
|
service.pkg.name,
|
|
@@ -15236,15 +15363,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15236
15363
|
nodesAdded++;
|
|
15237
15364
|
}
|
|
15238
15365
|
if (runtimeNode.id !== anchorId) {
|
|
15239
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
15366
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types52.EdgeType.RUNS_ON);
|
|
15240
15367
|
if (!graph.hasEdge(runsOnId)) {
|
|
15241
15368
|
const edge = {
|
|
15242
15369
|
id: runsOnId,
|
|
15243
15370
|
source: anchorId,
|
|
15244
15371
|
target: runtimeNode.id,
|
|
15245
|
-
type:
|
|
15246
|
-
provenance:
|
|
15247
|
-
confidence: (0,
|
|
15372
|
+
type: import_types52.EdgeType.RUNS_ON,
|
|
15373
|
+
provenance: import_types52.Provenance.EXTRACTED,
|
|
15374
|
+
confidence: (0, import_types52.confidenceForExtracted)("structural"),
|
|
15248
15375
|
evidence: {
|
|
15249
15376
|
file: evidenceFile,
|
|
15250
15377
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -15258,7 +15385,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15258
15385
|
const result = addResourceEdge(
|
|
15259
15386
|
graph,
|
|
15260
15387
|
anchorId,
|
|
15261
|
-
|
|
15388
|
+
import_types52.EdgeType.CONNECTS_TO,
|
|
15262
15389
|
"cloudflare-route",
|
|
15263
15390
|
route,
|
|
15264
15391
|
evidenceFile,
|
|
@@ -15282,7 +15409,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15282
15409
|
const result = addResourceEdge(
|
|
15283
15410
|
graph,
|
|
15284
15411
|
anchorId,
|
|
15285
|
-
|
|
15412
|
+
import_types52.EdgeType.DEPENDS_ON,
|
|
15286
15413
|
group.kind,
|
|
15287
15414
|
name,
|
|
15288
15415
|
evidenceFile,
|
|
@@ -15296,7 +15423,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15296
15423
|
const result = addResourceEdge(
|
|
15297
15424
|
graph,
|
|
15298
15425
|
anchorId,
|
|
15299
|
-
|
|
15426
|
+
import_types52.EdgeType.DEPENDS_ON,
|
|
15300
15427
|
"cloudflare-cron",
|
|
15301
15428
|
cron,
|
|
15302
15429
|
evidenceFile,
|
|
@@ -15309,7 +15436,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15309
15436
|
const result = addResourceEdge(
|
|
15310
15437
|
graph,
|
|
15311
15438
|
anchorId,
|
|
15312
|
-
|
|
15439
|
+
import_types52.EdgeType.DEPENDS_ON,
|
|
15313
15440
|
"cloudflare-env-var",
|
|
15314
15441
|
varName,
|
|
15315
15442
|
evidenceFile,
|
|
@@ -15322,15 +15449,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15322
15449
|
if (!svc.service) continue;
|
|
15323
15450
|
const target = workerIndex.get(svc.service);
|
|
15324
15451
|
if (target && target.anchorId !== anchorId) {
|
|
15325
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
15452
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types52.EdgeType.CALLS);
|
|
15326
15453
|
if (!graph.hasEdge(edgeId)) {
|
|
15327
15454
|
const edge = {
|
|
15328
15455
|
id: edgeId,
|
|
15329
15456
|
source: anchorId,
|
|
15330
15457
|
target: target.anchorId,
|
|
15331
|
-
type:
|
|
15332
|
-
provenance:
|
|
15333
|
-
confidence: (0,
|
|
15458
|
+
type: import_types52.EdgeType.CALLS,
|
|
15459
|
+
provenance: import_types52.Provenance.EXTRACTED,
|
|
15460
|
+
confidence: (0, import_types52.confidenceForExtracted)("structural"),
|
|
15334
15461
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
15335
15462
|
};
|
|
15336
15463
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -15341,7 +15468,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15341
15468
|
const result = addResourceEdge(
|
|
15342
15469
|
graph,
|
|
15343
15470
|
anchorId,
|
|
15344
|
-
|
|
15471
|
+
import_types52.EdgeType.DEPENDS_ON,
|
|
15345
15472
|
"cloudflare-service-binding",
|
|
15346
15473
|
svc.service,
|
|
15347
15474
|
evidenceFile,
|
|
@@ -15357,12 +15484,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
15357
15484
|
// src/extract/infra/vercel.ts
|
|
15358
15485
|
init_cjs_shims();
|
|
15359
15486
|
var import_node_fs29 = require("fs");
|
|
15360
|
-
var
|
|
15361
|
-
var
|
|
15487
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
15488
|
+
var import_types53 = require("@neat.is/types");
|
|
15362
15489
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
15363
15490
|
async function readVercelConfig(dir) {
|
|
15364
15491
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
15365
|
-
const abs =
|
|
15492
|
+
const abs = import_node_path63.default.join(dir, filename);
|
|
15366
15493
|
if (!await exists(abs)) continue;
|
|
15367
15494
|
const raw = await import_node_fs29.promises.readFile(abs, "utf8");
|
|
15368
15495
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -15371,7 +15498,7 @@ async function readVercelConfig(dir) {
|
|
|
15371
15498
|
return null;
|
|
15372
15499
|
}
|
|
15373
15500
|
async function readLinkedProjectName(dir) {
|
|
15374
|
-
const abs =
|
|
15501
|
+
const abs = import_node_path63.default.join(dir, ".vercel", "project.json");
|
|
15375
15502
|
if (!await exists(abs)) return void 0;
|
|
15376
15503
|
const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
|
|
15377
15504
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -15389,7 +15516,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
15389
15516
|
read = await readVercelConfig(service.dir);
|
|
15390
15517
|
projectName = await readLinkedProjectName(service.dir);
|
|
15391
15518
|
} catch (err) {
|
|
15392
|
-
recordExtractionError("infra vercel",
|
|
15519
|
+
recordExtractionError("infra vercel", import_node_path63.default.relative(scanPath, service.dir), err);
|
|
15393
15520
|
continue;
|
|
15394
15521
|
}
|
|
15395
15522
|
if (!read && !projectName) continue;
|
|
@@ -15405,7 +15532,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
15405
15532
|
const anchorId = service.node.id;
|
|
15406
15533
|
if (!read) continue;
|
|
15407
15534
|
const { config, relFile, raw } = read;
|
|
15408
|
-
const evidenceFile = toPosix(
|
|
15535
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
|
|
15409
15536
|
const add = (edgeType, kind, name) => {
|
|
15410
15537
|
if (!name) return;
|
|
15411
15538
|
const result = emitPlatformResourceEdge(
|
|
@@ -15421,12 +15548,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
15421
15548
|
nodesAdded += result.nodesAdded;
|
|
15422
15549
|
edgesAdded += result.edgesAdded;
|
|
15423
15550
|
};
|
|
15424
|
-
add(
|
|
15425
|
-
for (const cron of config.crons ?? []) add(
|
|
15426
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
15427
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
15551
|
+
add(import_types53.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
15552
|
+
for (const cron of config.crons ?? []) add(import_types53.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
15553
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
15554
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
15428
15555
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
15429
|
-
add(
|
|
15556
|
+
add(import_types53.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
15430
15557
|
}
|
|
15431
15558
|
}
|
|
15432
15559
|
return { nodesAdded, edgesAdded };
|
|
@@ -15435,13 +15562,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
15435
15562
|
// src/extract/infra/railway.ts
|
|
15436
15563
|
init_cjs_shims();
|
|
15437
15564
|
var import_node_fs30 = require("fs");
|
|
15438
|
-
var
|
|
15565
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
15439
15566
|
var import_smol_toml4 = require("smol-toml");
|
|
15440
|
-
var
|
|
15567
|
+
var import_types54 = require("@neat.is/types");
|
|
15441
15568
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
15442
15569
|
async function readRailwayConfig(dir) {
|
|
15443
15570
|
for (const filename of RAILWAY_FILENAMES) {
|
|
15444
|
-
const abs =
|
|
15571
|
+
const abs = import_node_path64.default.join(dir, filename);
|
|
15445
15572
|
if (!await exists(abs)) continue;
|
|
15446
15573
|
const raw = await import_node_fs30.promises.readFile(abs, "utf8");
|
|
15447
15574
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -15457,7 +15584,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
15457
15584
|
try {
|
|
15458
15585
|
read = await readRailwayConfig(service.dir);
|
|
15459
15586
|
} catch (err) {
|
|
15460
|
-
recordExtractionError("infra railway",
|
|
15587
|
+
recordExtractionError("infra railway", import_node_path64.default.relative(scanPath, service.dir), err);
|
|
15461
15588
|
continue;
|
|
15462
15589
|
}
|
|
15463
15590
|
if (!read) continue;
|
|
@@ -15467,7 +15594,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
15467
15594
|
}
|
|
15468
15595
|
const anchorId = service.node.id;
|
|
15469
15596
|
const { config, relFile, raw } = read;
|
|
15470
|
-
const evidenceFile = toPosix(
|
|
15597
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
|
|
15471
15598
|
const add = (edgeType, kind, name) => {
|
|
15472
15599
|
if (!name) return;
|
|
15473
15600
|
const result = emitPlatformResourceEdge(
|
|
@@ -15483,9 +15610,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
15483
15610
|
nodesAdded += result.nodesAdded;
|
|
15484
15611
|
edgesAdded += result.edgesAdded;
|
|
15485
15612
|
};
|
|
15486
|
-
add(
|
|
15487
|
-
add(
|
|
15488
|
-
add(
|
|
15613
|
+
add(import_types54.EdgeType.RUNS_ON, "railway", "railway");
|
|
15614
|
+
add(import_types54.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
15615
|
+
add(import_types54.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
15489
15616
|
}
|
|
15490
15617
|
return { nodesAdded, edgesAdded };
|
|
15491
15618
|
}
|
|
@@ -15493,12 +15620,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
15493
15620
|
// src/extract/infra/supabase.ts
|
|
15494
15621
|
init_cjs_shims();
|
|
15495
15622
|
var import_node_fs31 = require("fs");
|
|
15496
|
-
var
|
|
15623
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15497
15624
|
var import_smol_toml5 = require("smol-toml");
|
|
15498
|
-
var
|
|
15625
|
+
var import_types55 = require("@neat.is/types");
|
|
15499
15626
|
async function readSupabaseConfig(dir) {
|
|
15500
|
-
const relFile =
|
|
15501
|
-
const abs =
|
|
15627
|
+
const relFile = import_node_path65.default.join("supabase", "config.toml");
|
|
15628
|
+
const abs = import_node_path65.default.join(dir, relFile);
|
|
15502
15629
|
if (!await exists(abs)) return null;
|
|
15503
15630
|
const raw = await import_node_fs31.promises.readFile(abs, "utf8");
|
|
15504
15631
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
@@ -15512,7 +15639,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
15512
15639
|
try {
|
|
15513
15640
|
read = await readSupabaseConfig(service.dir);
|
|
15514
15641
|
} catch (err) {
|
|
15515
|
-
recordExtractionError("infra supabase",
|
|
15642
|
+
recordExtractionError("infra supabase", import_node_path65.default.relative(scanPath, service.dir), err);
|
|
15516
15643
|
continue;
|
|
15517
15644
|
}
|
|
15518
15645
|
if (!read) continue;
|
|
@@ -15527,7 +15654,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
15527
15654
|
});
|
|
15528
15655
|
}
|
|
15529
15656
|
const anchorId = service.node.id;
|
|
15530
|
-
const evidenceFile = toPosix(
|
|
15657
|
+
const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
|
|
15531
15658
|
const add = (edgeType, kind, name) => {
|
|
15532
15659
|
if (!name) return;
|
|
15533
15660
|
const result = emitPlatformResourceEdge(
|
|
@@ -15543,10 +15670,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
15543
15670
|
nodesAdded += result.nodesAdded;
|
|
15544
15671
|
edgesAdded += result.edgesAdded;
|
|
15545
15672
|
};
|
|
15546
|
-
add(
|
|
15547
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
15548
|
-
if (config.storage) add(
|
|
15549
|
-
if (config.auth) add(
|
|
15673
|
+
add(import_types55.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
15674
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types55.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
15675
|
+
if (config.storage) add(import_types55.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
15676
|
+
if (config.auth) add(import_types55.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
15550
15677
|
}
|
|
15551
15678
|
return { nodesAdded, edgesAdded };
|
|
15552
15679
|
}
|
|
@@ -15569,18 +15696,18 @@ async function addInfra(graph, scanPath, services) {
|
|
|
15569
15696
|
|
|
15570
15697
|
// src/extract/zod-shapes.ts
|
|
15571
15698
|
init_cjs_shims();
|
|
15572
|
-
var
|
|
15573
|
-
var
|
|
15699
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
15700
|
+
var import_tree_sitter18 = __toESM(require("tree-sitter"), 1);
|
|
15574
15701
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
15575
|
-
var
|
|
15702
|
+
var import_types56 = require("@neat.is/types");
|
|
15576
15703
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
15577
15704
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
15578
15705
|
function parserForExt4(ext) {
|
|
15579
|
-
const p = new
|
|
15706
|
+
const p = new import_tree_sitter18.default();
|
|
15580
15707
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
15581
15708
|
return p;
|
|
15582
15709
|
}
|
|
15583
|
-
function
|
|
15710
|
+
function namedChildren6(node) {
|
|
15584
15711
|
const out = [];
|
|
15585
15712
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
15586
15713
|
const c = node.namedChild(i);
|
|
@@ -15590,7 +15717,7 @@ function namedChildren5(node) {
|
|
|
15590
15717
|
}
|
|
15591
15718
|
function stringLiteralText5(node) {
|
|
15592
15719
|
if (!node || node.type !== "string") return null;
|
|
15593
|
-
for (const child of
|
|
15720
|
+
for (const child of namedChildren6(node)) {
|
|
15594
15721
|
if (child.type === "string_fragment") return child.text;
|
|
15595
15722
|
}
|
|
15596
15723
|
return "";
|
|
@@ -15615,12 +15742,12 @@ function zodCallKind(call) {
|
|
|
15615
15742
|
function firstArg(call) {
|
|
15616
15743
|
const args = call.childForFieldName("arguments");
|
|
15617
15744
|
if (!args) return null;
|
|
15618
|
-
return
|
|
15745
|
+
return namedChildren6(args)[0] ?? null;
|
|
15619
15746
|
}
|
|
15620
15747
|
function fieldsFromObject(obj) {
|
|
15621
15748
|
const out = [];
|
|
15622
15749
|
const seen = /* @__PURE__ */ new Set();
|
|
15623
|
-
for (const child of
|
|
15750
|
+
for (const child of namedChildren6(obj)) {
|
|
15624
15751
|
if (child.type === "spread_element") return null;
|
|
15625
15752
|
if (child.type !== "pair") continue;
|
|
15626
15753
|
const key = child.childForFieldName("key");
|
|
@@ -15636,7 +15763,7 @@ function fieldsFromObject(obj) {
|
|
|
15636
15763
|
function membersFromEnum(arr) {
|
|
15637
15764
|
const out = [];
|
|
15638
15765
|
const seen = /* @__PURE__ */ new Set();
|
|
15639
|
-
for (const child of
|
|
15766
|
+
for (const child of namedChildren6(arr)) {
|
|
15640
15767
|
if (child.type !== "string") return null;
|
|
15641
15768
|
const value = stringLiteralText5(child);
|
|
15642
15769
|
if (value !== null && value !== "" && !seen.has(value)) {
|
|
@@ -15648,10 +15775,10 @@ function membersFromEnum(arr) {
|
|
|
15648
15775
|
}
|
|
15649
15776
|
function topLevelSchemas(root) {
|
|
15650
15777
|
const out = [];
|
|
15651
|
-
for (const stmt of
|
|
15778
|
+
for (const stmt of namedChildren6(root)) {
|
|
15652
15779
|
const decl = stmt.type === "export_statement" ? stmt.childForFieldName("declaration") : stmt;
|
|
15653
15780
|
if (decl?.type !== "lexical_declaration" && decl?.type !== "variable_declaration") continue;
|
|
15654
|
-
for (const declarator of
|
|
15781
|
+
for (const declarator of namedChildren6(decl)) {
|
|
15655
15782
|
if (declarator.type !== "variable_declarator") continue;
|
|
15656
15783
|
const name = declarator.childForFieldName("name");
|
|
15657
15784
|
const value = declarator.childForFieldName("value");
|
|
@@ -15664,7 +15791,7 @@ function topLevelSchemas(root) {
|
|
|
15664
15791
|
}
|
|
15665
15792
|
function zodShapesFromFile(file, serviceDir) {
|
|
15666
15793
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
15667
|
-
const tree = parseSource3(parserForExt4(
|
|
15794
|
+
const tree = parseSource3(parserForExt4(import_node_path66.default.extname(file.path)), file.content);
|
|
15668
15795
|
const out = [];
|
|
15669
15796
|
const seen = /* @__PURE__ */ new Set();
|
|
15670
15797
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -15678,11 +15805,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
15678
15805
|
seen.add(name);
|
|
15679
15806
|
const line = call.startPosition.row + 1;
|
|
15680
15807
|
out.push({
|
|
15681
|
-
infraId: (0,
|
|
15808
|
+
infraId: (0, import_types56.infraId)("zod-schema", name),
|
|
15682
15809
|
name,
|
|
15683
15810
|
fields,
|
|
15684
15811
|
evidence: {
|
|
15685
|
-
file:
|
|
15812
|
+
file: import_node_path66.default.relative(serviceDir, file.path),
|
|
15686
15813
|
line,
|
|
15687
15814
|
snippet: snippet(file.content, line)
|
|
15688
15815
|
}
|
|
@@ -15713,7 +15840,7 @@ async function addZodShapes(graph, services) {
|
|
|
15713
15840
|
if (!graph.hasNode(shape.infraId)) {
|
|
15714
15841
|
const node = {
|
|
15715
15842
|
id: shape.infraId,
|
|
15716
|
-
type:
|
|
15843
|
+
type: import_types56.NodeType.InfraNode,
|
|
15717
15844
|
name: shape.name,
|
|
15718
15845
|
provider: "self",
|
|
15719
15846
|
kind: "zod-schema"
|
|
@@ -15723,14 +15850,14 @@ async function addZodShapes(graph, services) {
|
|
|
15723
15850
|
}
|
|
15724
15851
|
if (shape.fields.length > 0) {
|
|
15725
15852
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
15726
|
-
if (node.type ===
|
|
15853
|
+
if (node.type === import_types56.NodeType.InfraNode) {
|
|
15727
15854
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
15728
15855
|
...node,
|
|
15729
15856
|
columns: foldColumns(
|
|
15730
15857
|
node.columns,
|
|
15731
15858
|
shape.fields,
|
|
15732
|
-
|
|
15733
|
-
(0,
|
|
15859
|
+
import_types56.Provenance.EXTRACTED,
|
|
15860
|
+
(0, import_types56.confidenceForExtracted)("structural")
|
|
15734
15861
|
)
|
|
15735
15862
|
});
|
|
15736
15863
|
}
|
|
@@ -15744,15 +15871,15 @@ async function addZodShapes(graph, services) {
|
|
|
15744
15871
|
);
|
|
15745
15872
|
nodesAdded += n;
|
|
15746
15873
|
edgesAdded += e;
|
|
15747
|
-
const edgeId = (0,
|
|
15874
|
+
const edgeId = (0, import_types56.extractedEdgeId)(fileNodeId, shape.infraId, import_types56.EdgeType.CONTAINS);
|
|
15748
15875
|
if (!graph.hasEdge(edgeId)) {
|
|
15749
15876
|
const edge = {
|
|
15750
15877
|
id: edgeId,
|
|
15751
15878
|
source: fileNodeId,
|
|
15752
15879
|
target: shape.infraId,
|
|
15753
|
-
type:
|
|
15754
|
-
provenance:
|
|
15755
|
-
confidence: (0,
|
|
15880
|
+
type: import_types56.EdgeType.CONTAINS,
|
|
15881
|
+
provenance: import_types56.Provenance.EXTRACTED,
|
|
15882
|
+
confidence: (0, import_types56.confidenceForExtracted)("structural"),
|
|
15756
15883
|
evidence: shape.evidence
|
|
15757
15884
|
};
|
|
15758
15885
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -15766,7 +15893,7 @@ async function addZodShapes(graph, services) {
|
|
|
15766
15893
|
|
|
15767
15894
|
// src/extract/firestore-rules.ts
|
|
15768
15895
|
init_cjs_shims();
|
|
15769
|
-
var
|
|
15896
|
+
var import_types57 = require("@neat.is/types");
|
|
15770
15897
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
15771
15898
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
15772
15899
|
function stripComments(src) {
|
|
@@ -15906,7 +16033,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
15906
16033
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
15907
16034
|
graph.forEachNode((id, attrs) => {
|
|
15908
16035
|
const node = attrs;
|
|
15909
|
-
if (node.type !==
|
|
16036
|
+
if (node.type !== import_types57.NodeType.InfraNode) return;
|
|
15910
16037
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
15911
16038
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
15912
16039
|
if (!fields || fields.size === 0) return;
|
|
@@ -15919,17 +16046,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
15919
16046
|
}
|
|
15920
16047
|
|
|
15921
16048
|
// src/extract/index.ts
|
|
15922
|
-
var
|
|
16049
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
15923
16050
|
|
|
15924
16051
|
// src/extract/retire.ts
|
|
15925
16052
|
init_cjs_shims();
|
|
15926
16053
|
var import_node_fs32 = require("fs");
|
|
15927
|
-
var
|
|
15928
|
-
var
|
|
16054
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
16055
|
+
var import_types58 = require("@neat.is/types");
|
|
15929
16056
|
function dropOrphanedFileNodes(graph) {
|
|
15930
16057
|
const orphans = [];
|
|
15931
16058
|
graph.forEachNode((id, attrs) => {
|
|
15932
|
-
if (attrs.type !==
|
|
16059
|
+
if (attrs.type !== import_types58.NodeType.FileNode) return;
|
|
15933
16060
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
15934
16061
|
orphans.push(id);
|
|
15935
16062
|
}
|
|
@@ -15942,14 +16069,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
15942
16069
|
const bases = [scanPath, ...serviceDirs];
|
|
15943
16070
|
graph.forEachEdge((id, attrs) => {
|
|
15944
16071
|
const edge = attrs;
|
|
15945
|
-
if (edge.provenance !==
|
|
16072
|
+
if (edge.provenance !== import_types58.Provenance.EXTRACTED) return;
|
|
15946
16073
|
const evidenceFile = edge.evidence?.file;
|
|
15947
16074
|
if (!evidenceFile) return;
|
|
15948
|
-
if (
|
|
16075
|
+
if (import_node_path67.default.isAbsolute(evidenceFile)) {
|
|
15949
16076
|
if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
|
|
15950
16077
|
return;
|
|
15951
16078
|
}
|
|
15952
|
-
const found = bases.some((base) => (0, import_node_fs32.existsSync)(
|
|
16079
|
+
const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path67.default.join(base, evidenceFile)));
|
|
15953
16080
|
if (!found) toDrop.push(id);
|
|
15954
16081
|
});
|
|
15955
16082
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -16006,7 +16133,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
16006
16133
|
}
|
|
16007
16134
|
const droppedEntries = drainDroppedExtracted();
|
|
16008
16135
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
16009
|
-
const rejectedPath =
|
|
16136
|
+
const rejectedPath = import_node_path68.default.join(import_node_path68.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
16010
16137
|
try {
|
|
16011
16138
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
16012
16139
|
} catch (err) {
|
|
@@ -16041,8 +16168,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
16041
16168
|
// src/persist.ts
|
|
16042
16169
|
init_cjs_shims();
|
|
16043
16170
|
var import_node_fs33 = require("fs");
|
|
16044
|
-
var
|
|
16045
|
-
var
|
|
16171
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
16172
|
+
var import_types59 = require("@neat.is/types");
|
|
16046
16173
|
var SCHEMA_VERSION = 7;
|
|
16047
16174
|
function migrateV1ToV2(payload) {
|
|
16048
16175
|
const nodes = payload.graph.nodes;
|
|
@@ -16066,7 +16193,7 @@ function migrateV5ToV6(payload) {
|
|
|
16066
16193
|
if (Array.isArray(nodes)) {
|
|
16067
16194
|
for (const node of nodes) {
|
|
16068
16195
|
const attrs = node.attributes;
|
|
16069
|
-
if (!attrs || attrs.type !==
|
|
16196
|
+
if (!attrs || attrs.type !== import_types59.NodeType.InfraNode) continue;
|
|
16070
16197
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
16071
16198
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
16072
16199
|
}
|
|
@@ -16082,12 +16209,12 @@ function migrateV2ToV3(payload) {
|
|
|
16082
16209
|
for (const edge of edges) {
|
|
16083
16210
|
const attrs = edge.attributes;
|
|
16084
16211
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
16085
|
-
attrs.provenance =
|
|
16212
|
+
attrs.provenance = import_types59.Provenance.OBSERVED;
|
|
16086
16213
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
16087
16214
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
16088
16215
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
16089
16216
|
if (type && source && target) {
|
|
16090
|
-
const newId = (0,
|
|
16217
|
+
const newId = (0, import_types59.observedEdgeId)(source, target, type);
|
|
16091
16218
|
attrs.id = newId;
|
|
16092
16219
|
if (edge.key) edge.key = newId;
|
|
16093
16220
|
}
|
|
@@ -16096,7 +16223,7 @@ function migrateV2ToV3(payload) {
|
|
|
16096
16223
|
return { ...payload, schemaVersion: 3 };
|
|
16097
16224
|
}
|
|
16098
16225
|
async function ensureDir(filePath) {
|
|
16099
|
-
await import_node_fs33.promises.mkdir(
|
|
16226
|
+
await import_node_fs33.promises.mkdir(import_node_path69.default.dirname(filePath), { recursive: true });
|
|
16100
16227
|
}
|
|
16101
16228
|
async function saveGraphToDisk(graph, outPath) {
|
|
16102
16229
|
await ensureDir(outPath);
|
|
@@ -16186,23 +16313,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
16186
16313
|
|
|
16187
16314
|
// src/projects.ts
|
|
16188
16315
|
init_cjs_shims();
|
|
16189
|
-
var
|
|
16316
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
16190
16317
|
function pathsForProject(project, baseDir) {
|
|
16191
16318
|
if (project === DEFAULT_PROJECT) {
|
|
16192
16319
|
return {
|
|
16193
|
-
snapshotPath:
|
|
16194
|
-
errorsPath:
|
|
16195
|
-
staleEventsPath:
|
|
16196
|
-
embeddingsCachePath:
|
|
16197
|
-
policyViolationsPath:
|
|
16320
|
+
snapshotPath: import_node_path70.default.join(baseDir, "graph.json"),
|
|
16321
|
+
errorsPath: import_node_path70.default.join(baseDir, "errors.ndjson"),
|
|
16322
|
+
staleEventsPath: import_node_path70.default.join(baseDir, "stale-events.ndjson"),
|
|
16323
|
+
embeddingsCachePath: import_node_path70.default.join(baseDir, "embeddings.json"),
|
|
16324
|
+
policyViolationsPath: import_node_path70.default.join(baseDir, "policy-violations.ndjson")
|
|
16198
16325
|
};
|
|
16199
16326
|
}
|
|
16200
16327
|
return {
|
|
16201
|
-
snapshotPath:
|
|
16202
|
-
errorsPath:
|
|
16203
|
-
staleEventsPath:
|
|
16204
|
-
embeddingsCachePath:
|
|
16205
|
-
policyViolationsPath:
|
|
16328
|
+
snapshotPath: import_node_path70.default.join(baseDir, `${project}.json`),
|
|
16329
|
+
errorsPath: import_node_path70.default.join(baseDir, `errors.${project}.ndjson`),
|
|
16330
|
+
staleEventsPath: import_node_path70.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
16331
|
+
embeddingsCachePath: import_node_path70.default.join(baseDir, `embeddings.${project}.json`),
|
|
16332
|
+
policyViolationsPath: import_node_path70.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
16206
16333
|
};
|
|
16207
16334
|
}
|
|
16208
16335
|
var Projects = class {
|
|
@@ -16240,19 +16367,19 @@ var Projects = class {
|
|
|
16240
16367
|
init_cjs_shims();
|
|
16241
16368
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
16242
16369
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
16243
|
-
var
|
|
16370
|
+
var import_types100 = require("@neat.is/types");
|
|
16244
16371
|
|
|
16245
16372
|
// src/extend/index.ts
|
|
16246
16373
|
init_cjs_shims();
|
|
16247
16374
|
var import_node_fs35 = require("fs");
|
|
16248
|
-
var
|
|
16375
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
16249
16376
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
16250
16377
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
16251
16378
|
|
|
16252
16379
|
// src/installers/package-manager.ts
|
|
16253
16380
|
init_cjs_shims();
|
|
16254
16381
|
var import_node_fs34 = require("fs");
|
|
16255
|
-
var
|
|
16382
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
16256
16383
|
var import_node_child_process = require("child_process");
|
|
16257
16384
|
var LOCKFILE_PRIORITY = [
|
|
16258
16385
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -16274,22 +16401,22 @@ async function exists2(p) {
|
|
|
16274
16401
|
}
|
|
16275
16402
|
}
|
|
16276
16403
|
async function detectPackageManager(serviceDir) {
|
|
16277
|
-
let dir =
|
|
16404
|
+
let dir = import_node_path71.default.resolve(serviceDir);
|
|
16278
16405
|
const stops = /* @__PURE__ */ new Set();
|
|
16279
16406
|
for (let i = 0; i < 64; i++) {
|
|
16280
16407
|
if (stops.has(dir)) break;
|
|
16281
16408
|
stops.add(dir);
|
|
16282
16409
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
16283
|
-
const lockPath =
|
|
16410
|
+
const lockPath = import_node_path71.default.join(dir, candidate.lockfile);
|
|
16284
16411
|
if (await exists2(lockPath)) {
|
|
16285
16412
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
16286
16413
|
}
|
|
16287
16414
|
}
|
|
16288
|
-
const parent =
|
|
16415
|
+
const parent = import_node_path71.default.dirname(dir);
|
|
16289
16416
|
if (parent === dir) break;
|
|
16290
16417
|
dir = parent;
|
|
16291
16418
|
}
|
|
16292
|
-
return { pm: "npm", cwd:
|
|
16419
|
+
return { pm: "npm", cwd: import_node_path71.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
16293
16420
|
}
|
|
16294
16421
|
async function runPackageManagerInstall(cmd) {
|
|
16295
16422
|
return new Promise((resolve) => {
|
|
@@ -16338,7 +16465,7 @@ async function fileExists2(p) {
|
|
|
16338
16465
|
}
|
|
16339
16466
|
}
|
|
16340
16467
|
async function readPackageJson(scanPath) {
|
|
16341
|
-
const pkgPath =
|
|
16468
|
+
const pkgPath = import_node_path72.default.join(scanPath, "package.json");
|
|
16342
16469
|
const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
|
|
16343
16470
|
return JSON.parse(raw);
|
|
16344
16471
|
}
|
|
@@ -16352,27 +16479,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
16352
16479
|
]);
|
|
16353
16480
|
async function findHookFiles(scanPath) {
|
|
16354
16481
|
const found = [];
|
|
16355
|
-
const
|
|
16482
|
+
const walk11 = async (dir) => {
|
|
16356
16483
|
const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
16357
16484
|
for (const entry2 of entries) {
|
|
16358
16485
|
if (entry2.isDirectory()) {
|
|
16359
16486
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
16360
|
-
await
|
|
16487
|
+
await walk11(import_node_path72.default.join(dir, entry2.name));
|
|
16361
16488
|
} else if (entry2.isFile()) {
|
|
16362
16489
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
16363
|
-
const rel =
|
|
16364
|
-
found.push(rel.split(
|
|
16490
|
+
const rel = import_node_path72.default.relative(scanPath, import_node_path72.default.join(dir, entry2.name));
|
|
16491
|
+
found.push(rel.split(import_node_path72.default.sep).join("/"));
|
|
16365
16492
|
}
|
|
16366
16493
|
}
|
|
16367
16494
|
}
|
|
16368
16495
|
};
|
|
16369
|
-
await
|
|
16496
|
+
await walk11(scanPath);
|
|
16370
16497
|
return found.sort();
|
|
16371
16498
|
}
|
|
16372
16499
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
16373
16500
|
let fallback = null;
|
|
16374
16501
|
for (const file of hookFiles) {
|
|
16375
|
-
const content = await import_node_fs35.promises.readFile(
|
|
16502
|
+
const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(scanPath, file), "utf8");
|
|
16376
16503
|
const patched = splicedContent(content, snippet2);
|
|
16377
16504
|
if (patched !== null) return { file, content, patched };
|
|
16378
16505
|
if (fallback === null) fallback = { file, content };
|
|
@@ -16380,11 +16507,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
16380
16507
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
16381
16508
|
}
|
|
16382
16509
|
function extendLogPath() {
|
|
16383
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
16510
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path72.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
16384
16511
|
}
|
|
16385
16512
|
async function appendExtendLog(entry2) {
|
|
16386
16513
|
const logPath = extendLogPath();
|
|
16387
|
-
await import_node_fs35.promises.mkdir(
|
|
16514
|
+
await import_node_fs35.promises.mkdir(import_node_path72.default.dirname(logPath), { recursive: true });
|
|
16388
16515
|
await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
16389
16516
|
}
|
|
16390
16517
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -16443,7 +16570,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
16443
16570
|
}
|
|
16444
16571
|
async function describeProjectInstrumentation(ctx) {
|
|
16445
16572
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
16446
|
-
const envNeat = await fileExists2(
|
|
16573
|
+
const envNeat = await fileExists2(import_node_path72.default.join(ctx.scanPath, ".env.neat"));
|
|
16447
16574
|
const registryInstrPackages = new Set(
|
|
16448
16575
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
16449
16576
|
);
|
|
@@ -16465,7 +16592,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
16465
16592
|
);
|
|
16466
16593
|
}
|
|
16467
16594
|
for (const file of hookFiles) {
|
|
16468
|
-
const content = await import_node_fs35.promises.readFile(
|
|
16595
|
+
const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
|
|
16469
16596
|
if (content.includes(args.registration_snippet)) {
|
|
16470
16597
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
16471
16598
|
}
|
|
@@ -16477,10 +16604,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
16477
16604
|
);
|
|
16478
16605
|
}
|
|
16479
16606
|
const primaryFile = primary.file;
|
|
16480
|
-
const primaryPath =
|
|
16607
|
+
const primaryPath = import_node_path72.default.join(ctx.scanPath, primaryFile);
|
|
16481
16608
|
const filesTouched = [];
|
|
16482
16609
|
const depsAdded = [];
|
|
16483
|
-
const pkgPath =
|
|
16610
|
+
const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
|
|
16484
16611
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
16485
16612
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
16486
16613
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -16519,7 +16646,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
16519
16646
|
};
|
|
16520
16647
|
}
|
|
16521
16648
|
for (const file of hookFiles) {
|
|
16522
|
-
const content = await import_node_fs35.promises.readFile(
|
|
16649
|
+
const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
|
|
16523
16650
|
if (content.includes(args.registration_snippet)) {
|
|
16524
16651
|
return {
|
|
16525
16652
|
library: args.library,
|
|
@@ -16560,7 +16687,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
16560
16687
|
if (!match) {
|
|
16561
16688
|
return { undone: false, message: "no apply found for library" };
|
|
16562
16689
|
}
|
|
16563
|
-
const pkgPath =
|
|
16690
|
+
const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
|
|
16564
16691
|
if (await fileExists2(pkgPath)) {
|
|
16565
16692
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
16566
16693
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -16571,7 +16698,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
16571
16698
|
}
|
|
16572
16699
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
16573
16700
|
for (const file of hookFiles) {
|
|
16574
|
-
const filePath =
|
|
16701
|
+
const filePath = import_node_path72.default.join(ctx.scanPath, file);
|
|
16575
16702
|
const content = await import_node_fs35.promises.readFile(filePath, "utf8");
|
|
16576
16703
|
if (content.includes(match.registration_snippet)) {
|
|
16577
16704
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -16587,39 +16714,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
16587
16714
|
|
|
16588
16715
|
// src/divergences.ts
|
|
16589
16716
|
init_cjs_shims();
|
|
16590
|
-
var
|
|
16717
|
+
var import_types60 = require("@neat.is/types");
|
|
16591
16718
|
function bucketKey(source, target, type) {
|
|
16592
16719
|
return `${type}|${source}|${target}`;
|
|
16593
16720
|
}
|
|
16594
16721
|
function bucketSourceFor(graph, edge) {
|
|
16595
|
-
if (edge.type !==
|
|
16596
|
-
const parsed = (0,
|
|
16722
|
+
if (edge.type !== import_types60.EdgeType.CONNECTS_TO) return edge.source;
|
|
16723
|
+
const parsed = (0, import_types60.parseFileId)(edge.source);
|
|
16597
16724
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
16598
16725
|
const target = graph.getNodeAttributes(edge.target);
|
|
16599
|
-
if (target.type !==
|
|
16600
|
-
return (0,
|
|
16726
|
+
if (target.type !== import_types60.NodeType.DatabaseNode) return edge.source;
|
|
16727
|
+
return (0, import_types60.serviceId)(parsed.service);
|
|
16601
16728
|
}
|
|
16602
16729
|
function bucketEdges(graph) {
|
|
16603
16730
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
16604
16731
|
graph.forEachEdge((id, attrs) => {
|
|
16605
16732
|
const e = attrs;
|
|
16606
|
-
const parsed = (0,
|
|
16733
|
+
const parsed = (0, import_types60.parseEdgeId)(id);
|
|
16607
16734
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
16608
16735
|
const source = bucketSourceFor(graph, e);
|
|
16609
16736
|
const key = bucketKey(source, e.target, e.type);
|
|
16610
16737
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
16611
16738
|
switch (provenance) {
|
|
16612
|
-
case
|
|
16739
|
+
case import_types60.Provenance.EXTRACTED:
|
|
16613
16740
|
cur.extracted = e;
|
|
16614
16741
|
break;
|
|
16615
|
-
case
|
|
16742
|
+
case import_types60.Provenance.OBSERVED:
|
|
16616
16743
|
cur.observed = e;
|
|
16617
16744
|
break;
|
|
16618
|
-
case
|
|
16745
|
+
case import_types60.Provenance.INFERRED:
|
|
16619
16746
|
cur.inferred = e;
|
|
16620
16747
|
break;
|
|
16621
16748
|
default:
|
|
16622
|
-
if (e.provenance ===
|
|
16749
|
+
if (e.provenance === import_types60.Provenance.STALE) cur.stale = e;
|
|
16623
16750
|
}
|
|
16624
16751
|
buckets2.set(key, cur);
|
|
16625
16752
|
});
|
|
@@ -16628,22 +16755,22 @@ function bucketEdges(graph) {
|
|
|
16628
16755
|
function nodeIsFrontier(graph, nodeId) {
|
|
16629
16756
|
if (!graph.hasNode(nodeId)) return false;
|
|
16630
16757
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
16631
|
-
return attrs.type ===
|
|
16758
|
+
return attrs.type === import_types60.NodeType.FrontierNode;
|
|
16632
16759
|
}
|
|
16633
16760
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
16634
16761
|
if (!graph.hasNode(nodeId)) return false;
|
|
16635
16762
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
16636
|
-
return attrs.type ===
|
|
16763
|
+
return attrs.type === import_types60.NodeType.WebSocketChannelNode;
|
|
16637
16764
|
}
|
|
16638
16765
|
function nodeIsServerAction(graph, nodeId) {
|
|
16639
16766
|
if (!graph.hasNode(nodeId)) return false;
|
|
16640
16767
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
16641
|
-
return attrs.type ===
|
|
16768
|
+
return attrs.type === import_types60.NodeType.ServerActionNode;
|
|
16642
16769
|
}
|
|
16643
16770
|
function nodeIsSymbol(graph, nodeId) {
|
|
16644
16771
|
if (!graph.hasNode(nodeId)) return false;
|
|
16645
16772
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
16646
|
-
return attrs.type ===
|
|
16773
|
+
return attrs.type === import_types60.NodeType.SymbolNode;
|
|
16647
16774
|
}
|
|
16648
16775
|
function clampConfidence(n) {
|
|
16649
16776
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -16663,14 +16790,14 @@ function gradedConfidence(edge) {
|
|
|
16663
16790
|
return clampConfidence(confidenceForEdge(edge));
|
|
16664
16791
|
}
|
|
16665
16792
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
16793
|
+
import_types60.EdgeType.CALLS,
|
|
16794
|
+
import_types60.EdgeType.CONNECTS_TO,
|
|
16795
|
+
import_types60.EdgeType.PUBLISHES_TO,
|
|
16796
|
+
import_types60.EdgeType.CONSUMES_FROM
|
|
16670
16797
|
]);
|
|
16671
16798
|
function detectMissingDivergences(graph, bucket) {
|
|
16672
16799
|
const out = [];
|
|
16673
|
-
if (bucket.type ===
|
|
16800
|
+
if (bucket.type === import_types60.EdgeType.CONTAINS) return out;
|
|
16674
16801
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
16675
16802
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
16676
16803
|
if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
|
|
@@ -16712,7 +16839,7 @@ function declaredHostFor(svc) {
|
|
|
16712
16839
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
16713
16840
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
16714
16841
|
const e = graph.getEdgeAttributes(edgeId);
|
|
16715
|
-
if (e.type ===
|
|
16842
|
+
if (e.type === import_types60.EdgeType.CONFIGURED_BY && e.provenance === import_types60.Provenance.EXTRACTED) {
|
|
16716
16843
|
return true;
|
|
16717
16844
|
}
|
|
16718
16845
|
}
|
|
@@ -16725,10 +16852,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
16725
16852
|
const out = [];
|
|
16726
16853
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
16727
16854
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16728
|
-
if (edge.type !==
|
|
16729
|
-
if (edge.provenance !==
|
|
16855
|
+
if (edge.type !== import_types60.EdgeType.CONNECTS_TO) continue;
|
|
16856
|
+
if (edge.provenance !== import_types60.Provenance.OBSERVED) continue;
|
|
16730
16857
|
const target = graph.getNodeAttributes(edge.target);
|
|
16731
|
-
if (target.type !==
|
|
16858
|
+
if (target.type !== import_types60.NodeType.DatabaseNode) continue;
|
|
16732
16859
|
const observedHost = target.host?.trim();
|
|
16733
16860
|
if (!observedHost) continue;
|
|
16734
16861
|
if (observedHost === declaredHost) continue;
|
|
@@ -16750,10 +16877,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
16750
16877
|
const deps = svc.dependencies ?? {};
|
|
16751
16878
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
16752
16879
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16753
|
-
if (edge.type !==
|
|
16754
|
-
if (edge.provenance !==
|
|
16880
|
+
if (edge.type !== import_types60.EdgeType.CONNECTS_TO) continue;
|
|
16881
|
+
if (edge.provenance !== import_types60.Provenance.OBSERVED) continue;
|
|
16755
16882
|
const target = graph.getNodeAttributes(edge.target);
|
|
16756
|
-
if (target.type !==
|
|
16883
|
+
if (target.type !== import_types60.NodeType.DatabaseNode) continue;
|
|
16757
16884
|
for (const pair of compatPairs()) {
|
|
16758
16885
|
if (pair.engine !== target.engine) continue;
|
|
16759
16886
|
const declared = deps[pair.driver];
|
|
@@ -16897,11 +17024,11 @@ function symbolLocus(graph, ev) {
|
|
|
16897
17024
|
const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
|
|
16898
17025
|
const affected = ev.affectedNode;
|
|
16899
17026
|
const affectedInGraph = affected.length > 0 && graph.hasNode(affected);
|
|
16900
|
-
const affectedIsCode = affectedInGraph && ((0,
|
|
17027
|
+
const affectedIsCode = affectedInGraph && ((0, import_types60.parseSymbolId)(affected) !== null || (0, import_types60.parseFileId)(affected) !== null);
|
|
16901
17028
|
if (affectedIsCode) return { node: affected, ...location ? { location } : {} };
|
|
16902
17029
|
if (!location) return null;
|
|
16903
17030
|
if (affectedInGraph) return { node: affected, location };
|
|
16904
|
-
const svc = (0,
|
|
17031
|
+
const svc = (0, import_types60.serviceId)(ev.service);
|
|
16905
17032
|
if (graph.hasNode(svc)) return { node: svc, location };
|
|
16906
17033
|
return null;
|
|
16907
17034
|
}
|
|
@@ -16944,7 +17071,7 @@ function detectSymbolMismatches(graph, incidents) {
|
|
|
16944
17071
|
mismatchKind: g.kind,
|
|
16945
17072
|
...g.symbol ? { symbol: g.symbol } : {},
|
|
16946
17073
|
...g.location ? { location: g.location } : {},
|
|
16947
|
-
provenance:
|
|
17074
|
+
provenance: import_types60.Provenance.INFERRED,
|
|
16948
17075
|
incidentId: g.latest.id,
|
|
16949
17076
|
errorMessage: g.latest.errorMessage,
|
|
16950
17077
|
incidentCount: g.count,
|
|
@@ -16961,7 +17088,7 @@ var OBSERVED_FAILING_CONFIDENCE = 0.6;
|
|
|
16961
17088
|
function detectObservedFailingEdge(graph, bucket) {
|
|
16962
17089
|
if (!bucket.extracted || !bucket.observed) return [];
|
|
16963
17090
|
if (!OBSERVABLE_EDGE_TYPES.has(bucket.type)) return [];
|
|
16964
|
-
if (bucket.observed.provenance !==
|
|
17091
|
+
if (bucket.observed.provenance !== import_types60.Provenance.OBSERVED) return [];
|
|
16965
17092
|
const signal = bucket.observed.signal;
|
|
16966
17093
|
if (!signal) return [];
|
|
16967
17094
|
const { spanCount, errorCount } = signal;
|
|
@@ -16975,7 +17102,7 @@ function detectObservedFailingEdge(graph, bucket) {
|
|
|
16975
17102
|
source: bucket.source,
|
|
16976
17103
|
target: bucket.target,
|
|
16977
17104
|
failureKind: "error-rate",
|
|
16978
|
-
provenance:
|
|
17105
|
+
provenance: import_types60.Provenance.INFERRED,
|
|
16979
17106
|
edgeType: bucket.type,
|
|
16980
17107
|
observed: bucket.observed,
|
|
16981
17108
|
spanCount,
|
|
@@ -17075,7 +17202,7 @@ function detectObservedFailingIncidents(graph, incidents) {
|
|
|
17075
17202
|
source: g.node,
|
|
17076
17203
|
target: g.node,
|
|
17077
17204
|
failureKind: g.kind,
|
|
17078
|
-
provenance:
|
|
17205
|
+
provenance: import_types60.Provenance.INFERRED,
|
|
17079
17206
|
...g.location ? { location: g.location } : {},
|
|
17080
17207
|
incidentId: g.latest.id,
|
|
17081
17208
|
errorMessage: g.latest.errorMessage,
|
|
@@ -17094,24 +17221,24 @@ function involvesNode(d, nodeId) {
|
|
|
17094
17221
|
function datastoreEverObserved(graph, nodeId) {
|
|
17095
17222
|
if (!graph.hasNode(nodeId)) return false;
|
|
17096
17223
|
const n = graph.getNodeAttributes(nodeId);
|
|
17097
|
-
if (n.type ===
|
|
17224
|
+
if (n.type === import_types60.NodeType.DatabaseNode) {
|
|
17098
17225
|
const via = n.discoveredVia;
|
|
17099
17226
|
if (via === "otel" || via === "merged") return true;
|
|
17100
17227
|
}
|
|
17101
17228
|
for (const edgeId of graph.inboundEdges(nodeId)) {
|
|
17102
17229
|
const e = graph.getEdgeAttributes(edgeId);
|
|
17103
|
-
if (e.provenance ===
|
|
17230
|
+
if (e.provenance === import_types60.Provenance.OBSERVED || e.provenance === import_types60.Provenance.STALE) return true;
|
|
17104
17231
|
}
|
|
17105
17232
|
return false;
|
|
17106
17233
|
}
|
|
17107
|
-
function serviceHasObservedSameEngineStore(graph, buckets2,
|
|
17234
|
+
function serviceHasObservedSameEngineStore(graph, buckets2, serviceId17, engine, excludeTarget) {
|
|
17108
17235
|
for (const bucket of buckets2.values()) {
|
|
17109
|
-
if (bucket.type !==
|
|
17110
|
-
if (bucket.source !==
|
|
17236
|
+
if (bucket.type !== import_types60.EdgeType.CONNECTS_TO) continue;
|
|
17237
|
+
if (bucket.source !== serviceId17) continue;
|
|
17111
17238
|
if (bucket.target === excludeTarget) continue;
|
|
17112
17239
|
if (!graph.hasNode(bucket.target)) continue;
|
|
17113
17240
|
const target = graph.getNodeAttributes(bucket.target);
|
|
17114
|
-
if (target.type !==
|
|
17241
|
+
if (target.type !== import_types60.NodeType.DatabaseNode) continue;
|
|
17115
17242
|
if (target.engine !== engine) continue;
|
|
17116
17243
|
if (bucket.observed || datastoreEverObserved(graph, bucket.target)) return true;
|
|
17117
17244
|
}
|
|
@@ -17121,10 +17248,10 @@ var DEAD_CODE_PROBE_CONFIDENCE = 0.1;
|
|
|
17121
17248
|
function dampenDeadCodeProbes(graph, buckets2, all) {
|
|
17122
17249
|
return all.map((d) => {
|
|
17123
17250
|
if (d.type !== "missing-observed") return d;
|
|
17124
|
-
if (!d.extracted || d.edgeType !==
|
|
17251
|
+
if (!d.extracted || d.edgeType !== import_types60.EdgeType.CONNECTS_TO) return d;
|
|
17125
17252
|
if (!graph.hasNode(d.target)) return d;
|
|
17126
17253
|
const target = graph.getNodeAttributes(d.target);
|
|
17127
|
-
if (target.type !==
|
|
17254
|
+
if (target.type !== import_types60.NodeType.DatabaseNode) return d;
|
|
17128
17255
|
if (d.extracted.evidence?.hostSource !== "literal") return d;
|
|
17129
17256
|
if (datastoreEverObserved(graph, d.target)) return d;
|
|
17130
17257
|
const engine = target.engine;
|
|
@@ -17144,7 +17271,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
17144
17271
|
for (const d of all) {
|
|
17145
17272
|
if (d.type !== "host-mismatch") continue;
|
|
17146
17273
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
17147
|
-
declaredHalf.add((0,
|
|
17274
|
+
declaredHalf.add((0, import_types60.databaseId)(d.extractedHost));
|
|
17148
17275
|
}
|
|
17149
17276
|
if (observedHalf.size === 0) return all;
|
|
17150
17277
|
return all.filter((d) => {
|
|
@@ -17164,13 +17291,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
17164
17291
|
}
|
|
17165
17292
|
graph.forEachNode((nodeId, attrs) => {
|
|
17166
17293
|
const n = attrs;
|
|
17167
|
-
if (n.type ===
|
|
17294
|
+
if (n.type === import_types60.NodeType.ServiceNode) {
|
|
17168
17295
|
const svc = n;
|
|
17169
17296
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
17170
17297
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
17171
17298
|
return;
|
|
17172
17299
|
}
|
|
17173
|
-
if (n.type ===
|
|
17300
|
+
if (n.type === import_types60.NodeType.InfraNode && n.kind === "sql-table") {
|
|
17174
17301
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
17175
17302
|
}
|
|
17176
17303
|
});
|
|
@@ -17230,7 +17357,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
17230
17357
|
const bloc = "location" in b && b.location ? b.location : "";
|
|
17231
17358
|
return aloc.localeCompare(bloc);
|
|
17232
17359
|
});
|
|
17233
|
-
return
|
|
17360
|
+
return import_types60.DivergenceResultSchema.parse({
|
|
17234
17361
|
divergences: filtered,
|
|
17235
17362
|
totalAffected: filtered.length,
|
|
17236
17363
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -17284,7 +17411,7 @@ function queryLogEntries(opts) {
|
|
|
17284
17411
|
|
|
17285
17412
|
// src/goodybag.ts
|
|
17286
17413
|
init_cjs_shims();
|
|
17287
|
-
var
|
|
17414
|
+
var import_types61 = require("@neat.is/types");
|
|
17288
17415
|
var CODE_FILEPATH_ATTR2 = "code.filepath";
|
|
17289
17416
|
var CODE_LINENO_ATTR2 = "code.lineno";
|
|
17290
17417
|
var BLAST_NEAREST_LIMIT = 5;
|
|
@@ -17309,7 +17436,7 @@ function locusOf(graph, ev) {
|
|
|
17309
17436
|
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
17310
17437
|
...node?.name ? { symbol: node.name } : {},
|
|
17311
17438
|
service: node?.service ?? ev.service,
|
|
17312
|
-
provenance:
|
|
17439
|
+
provenance: import_types61.Provenance.OBSERVED
|
|
17313
17440
|
};
|
|
17314
17441
|
}
|
|
17315
17442
|
function locusFromNode(graph, nodeId) {
|
|
@@ -17325,7 +17452,7 @@ function locusFromNode(graph, nodeId) {
|
|
|
17325
17452
|
...typeof end === "number" ? { lineEnd: end } : {},
|
|
17326
17453
|
...n.qualname ? { symbol: shortLabel(graph, nodeId) } : {},
|
|
17327
17454
|
...n.service ? { service: n.service } : {},
|
|
17328
|
-
provenance:
|
|
17455
|
+
provenance: import_types61.Provenance.INFERRED
|
|
17329
17456
|
};
|
|
17330
17457
|
}
|
|
17331
17458
|
function promoteCauseLocus(graph, causeNode, incidents) {
|
|
@@ -17334,7 +17461,7 @@ function promoteCauseLocus(graph, causeNode, incidents) {
|
|
|
17334
17461
|
);
|
|
17335
17462
|
if (native) {
|
|
17336
17463
|
const l = locusOf(graph, native);
|
|
17337
|
-
if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance:
|
|
17464
|
+
if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types61.Provenance.INFERRED };
|
|
17338
17465
|
}
|
|
17339
17466
|
return locusFromNode(graph, causeNode);
|
|
17340
17467
|
}
|
|
@@ -17383,7 +17510,7 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
17383
17510
|
const chain = (rc.traversalPath ?? []).map((node, i) => ({
|
|
17384
17511
|
node,
|
|
17385
17512
|
grain: grainOf2(graph, node),
|
|
17386
|
-
provenance: provs[i] ?? provs[provs.length - 1] ??
|
|
17513
|
+
provenance: provs[i] ?? provs[provs.length - 1] ?? import_types61.Provenance.INFERRED
|
|
17387
17514
|
}));
|
|
17388
17515
|
rootCause = {
|
|
17389
17516
|
node: rc.rootCauseNode,
|
|
@@ -17413,7 +17540,7 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
17413
17540
|
kind: "incident",
|
|
17414
17541
|
id: errorEvent.id,
|
|
17415
17542
|
at: errorEvent.timestamp,
|
|
17416
|
-
incidentKind: (0,
|
|
17543
|
+
incidentKind: (0, import_types61.incidentKindOf)(errorEvent),
|
|
17417
17544
|
service: errorEvent.service,
|
|
17418
17545
|
affectedNode: affected,
|
|
17419
17546
|
message: errorEvent.errorMessage,
|
|
@@ -17430,12 +17557,12 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
17430
17557
|
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
17431
17558
|
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
17432
17559
|
};
|
|
17433
|
-
return
|
|
17560
|
+
return import_types61.IncidentCardSchema.parse(card);
|
|
17434
17561
|
}
|
|
17435
17562
|
|
|
17436
17563
|
// src/ask.ts
|
|
17437
17564
|
init_cjs_shims();
|
|
17438
|
-
var
|
|
17565
|
+
var import_types62 = require("@neat.is/types");
|
|
17439
17566
|
var DEFAULT_MAX_NODES = 3;
|
|
17440
17567
|
var MAX_FACTS_PER_SECTION = 6;
|
|
17441
17568
|
var INTENT_RULES = [
|
|
@@ -17663,18 +17790,18 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17663
17790
|
};
|
|
17664
17791
|
graph.forEachNode((id, attrs) => {
|
|
17665
17792
|
const node = attrs;
|
|
17666
|
-
if (node.type ===
|
|
17793
|
+
if (node.type === import_types62.NodeType.FrontierNode) return;
|
|
17667
17794
|
const name = nodeName(node);
|
|
17668
17795
|
const body = idBody(id);
|
|
17669
17796
|
const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
|
|
17670
17797
|
if (labelTokens.size === 0) return;
|
|
17671
17798
|
let matched = 0;
|
|
17672
17799
|
for (const t of qTokens) if (labelTokens.has(t)) matched += 1;
|
|
17673
|
-
const
|
|
17674
|
-
if (matched === 0 && !
|
|
17800
|
+
const named2 = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
|
|
17801
|
+
if (matched === 0 && !named2) return;
|
|
17675
17802
|
const coverage = qTokens.length > 0 ? matched / qTokens.length : 0;
|
|
17676
|
-
const via =
|
|
17677
|
-
const score =
|
|
17803
|
+
const via = named2 ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
|
|
17804
|
+
const score = named2 ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
|
|
17678
17805
|
consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
|
|
17679
17806
|
});
|
|
17680
17807
|
if (searchIndex) {
|
|
@@ -17682,7 +17809,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17682
17809
|
const res = await searchIndex.search(question, 10);
|
|
17683
17810
|
if (res.provider !== "substring") {
|
|
17684
17811
|
for (const m of res.matches) {
|
|
17685
|
-
if (m.node.type ===
|
|
17812
|
+
if (m.node.type === import_types62.NodeType.FrontierNode) continue;
|
|
17686
17813
|
const already = best.get(m.node.id);
|
|
17687
17814
|
if (!already && m.score < EMBED_MIN_SCORE) continue;
|
|
17688
17815
|
consider({
|
|
@@ -17720,7 +17847,7 @@ function edgeSignalNote(e) {
|
|
|
17720
17847
|
function buildRootCauseSection(graph, node, incidents, now) {
|
|
17721
17848
|
const result = getRootCause(graph, node, void 0, incidents, { now });
|
|
17722
17849
|
if (!result) return null;
|
|
17723
|
-
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ??
|
|
17850
|
+
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types62.Provenance.OBSERVED;
|
|
17724
17851
|
const facts = [
|
|
17725
17852
|
{
|
|
17726
17853
|
text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
|
|
@@ -17770,7 +17897,7 @@ function buildObservedSection(graph, node) {
|
|
|
17770
17897
|
if (result.observed && result.inboundObservedCount > 0) {
|
|
17771
17898
|
facts.push({
|
|
17772
17899
|
text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
|
|
17773
|
-
provenance:
|
|
17900
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
17774
17901
|
});
|
|
17775
17902
|
} else {
|
|
17776
17903
|
return null;
|
|
@@ -17798,7 +17925,7 @@ function buildIncidentsSection(node, incidents) {
|
|
|
17798
17925
|
const facts = ordered.map((ev) => ({
|
|
17799
17926
|
text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
|
|
17800
17927
|
// ErrorEvents are observation records — OBSERVED by definition.
|
|
17801
|
-
provenance:
|
|
17928
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
17802
17929
|
}));
|
|
17803
17930
|
return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
|
|
17804
17931
|
}
|
|
@@ -17858,7 +17985,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17858
17985
|
}
|
|
17859
17986
|
const byKey = /* @__PURE__ */ new Map();
|
|
17860
17987
|
for (const ev of incidents) {
|
|
17861
|
-
const key = ev.affectedNode || (0,
|
|
17988
|
+
const key = ev.affectedNode || (0, import_types62.serviceId)(ev.service);
|
|
17862
17989
|
const cur = byKey.get(key);
|
|
17863
17990
|
if (!cur) {
|
|
17864
17991
|
byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
|
|
@@ -17873,7 +18000,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17873
18000
|
const rows = [...byKey.values()].sort((a, b) => b.count - a.count || b.latest.localeCompare(a.latest) || a.key.localeCompare(b.key)).slice(0, MAX_FACTS_PER_SECTION);
|
|
17874
18001
|
const facts = rows.map((r) => ({
|
|
17875
18002
|
text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
|
|
17876
|
-
provenance:
|
|
18003
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
17877
18004
|
}));
|
|
17878
18005
|
return {
|
|
17879
18006
|
heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
|
|
@@ -17886,7 +18013,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17886
18013
|
graph.forEachNode((_id, attrs) => {
|
|
17887
18014
|
const node = attrs;
|
|
17888
18015
|
nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
|
|
17889
|
-
if (node.type ===
|
|
18016
|
+
if (node.type === import_types62.NodeType.ServiceNode) services.push(node.id);
|
|
17890
18017
|
});
|
|
17891
18018
|
const edgeByProv = /* @__PURE__ */ new Map();
|
|
17892
18019
|
graph.forEachEdge((_id, attrs) => {
|
|
@@ -17898,10 +18025,10 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17898
18025
|
const shapeFacts = [
|
|
17899
18026
|
{ text: `${graph.order} nodes, ${graph.size} edges` },
|
|
17900
18027
|
{
|
|
17901
|
-
text: `${count(
|
|
18028
|
+
text: `${count(import_types62.NodeType.ServiceNode)} services, ${count(import_types62.NodeType.FileNode)} files, ${count(import_types62.NodeType.SymbolNode)} symbols, ${count(import_types62.NodeType.DatabaseNode)} databases`
|
|
17902
18029
|
}
|
|
17903
18030
|
];
|
|
17904
|
-
for (const p of [
|
|
18031
|
+
for (const p of [import_types62.Provenance.EXTRACTED, import_types62.Provenance.OBSERVED, import_types62.Provenance.INFERRED, import_types62.Provenance.STALE]) {
|
|
17905
18032
|
const n = edgeByProv.get(p) ?? 0;
|
|
17906
18033
|
if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
|
|
17907
18034
|
}
|
|
@@ -17918,7 +18045,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17918
18045
|
if (incidents && incidents.length > 0) {
|
|
17919
18046
|
const incCount = /* @__PURE__ */ new Map();
|
|
17920
18047
|
for (const ev of incidents) {
|
|
17921
|
-
const key = ev.affectedNode || (0,
|
|
18048
|
+
const key = ev.affectedNode || (0, import_types62.serviceId)(ev.service);
|
|
17922
18049
|
incCount.set(key, (incCount.get(key) ?? 0) + 1);
|
|
17923
18050
|
}
|
|
17924
18051
|
const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
|
|
@@ -17926,7 +18053,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17926
18053
|
heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
|
|
17927
18054
|
facts: top.map(([k, n]) => ({
|
|
17928
18055
|
text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
|
|
17929
|
-
provenance:
|
|
18056
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
17930
18057
|
}))
|
|
17931
18058
|
});
|
|
17932
18059
|
}
|
|
@@ -18062,7 +18189,7 @@ async function askGraph(graph, question, opts = {}) {
|
|
|
18062
18189
|
const provSet = /* @__PURE__ */ new Set();
|
|
18063
18190
|
for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
|
|
18064
18191
|
const confidence = sections[0]?.facts[0]?.confidence;
|
|
18065
|
-
return
|
|
18192
|
+
return import_types62.AskResultSchema.parse({
|
|
18066
18193
|
question,
|
|
18067
18194
|
intent,
|
|
18068
18195
|
matched,
|
|
@@ -18156,26 +18283,26 @@ function canonicalJson(value) {
|
|
|
18156
18283
|
init_cjs_shims();
|
|
18157
18284
|
var import_node_fs37 = require("fs");
|
|
18158
18285
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
18159
|
-
var
|
|
18160
|
-
var
|
|
18286
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
18287
|
+
var import_types63 = require("@neat.is/types");
|
|
18161
18288
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
18162
18289
|
var LOCK_RETRY_MS = 50;
|
|
18163
18290
|
function neatHome() {
|
|
18164
18291
|
const override = process.env.NEAT_HOME;
|
|
18165
|
-
if (override && override.length > 0) return
|
|
18166
|
-
return
|
|
18292
|
+
if (override && override.length > 0) return import_node_path73.default.resolve(override);
|
|
18293
|
+
return import_node_path73.default.join(import_node_os3.default.homedir(), ".neat");
|
|
18167
18294
|
}
|
|
18168
18295
|
function registryPath() {
|
|
18169
|
-
return
|
|
18296
|
+
return import_node_path73.default.join(neatHome(), "projects.json");
|
|
18170
18297
|
}
|
|
18171
18298
|
function registryLockPath() {
|
|
18172
|
-
return
|
|
18299
|
+
return import_node_path73.default.join(neatHome(), "projects.json.lock");
|
|
18173
18300
|
}
|
|
18174
18301
|
function daemonPidPath() {
|
|
18175
|
-
return
|
|
18302
|
+
return import_node_path73.default.join(neatHome(), "neatd.pid");
|
|
18176
18303
|
}
|
|
18177
18304
|
function daemonsDir() {
|
|
18178
|
-
return
|
|
18305
|
+
return import_node_path73.default.join(neatHome(), "daemons");
|
|
18179
18306
|
}
|
|
18180
18307
|
function isFiniteInt(v) {
|
|
18181
18308
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -18216,7 +18343,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
18216
18343
|
const out = [];
|
|
18217
18344
|
for (const name of names) {
|
|
18218
18345
|
if (!name.endsWith(".json")) continue;
|
|
18219
|
-
const file =
|
|
18346
|
+
const file = import_node_path73.default.join(dir, name);
|
|
18220
18347
|
let raw;
|
|
18221
18348
|
try {
|
|
18222
18349
|
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
@@ -18293,7 +18420,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
18293
18420
|
}
|
|
18294
18421
|
}
|
|
18295
18422
|
async function writeAtomically(target, contents) {
|
|
18296
|
-
await import_node_fs37.promises.mkdir(
|
|
18423
|
+
await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(target), { recursive: true });
|
|
18297
18424
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
18298
18425
|
const fd = await import_node_fs37.promises.open(tmp, "w");
|
|
18299
18426
|
try {
|
|
@@ -18306,7 +18433,7 @@ async function writeAtomically(target, contents) {
|
|
|
18306
18433
|
}
|
|
18307
18434
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
18308
18435
|
const deadline = Date.now() + timeoutMs;
|
|
18309
|
-
await import_node_fs37.promises.mkdir(
|
|
18436
|
+
await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(lockPath), { recursive: true });
|
|
18310
18437
|
let probedHolder = false;
|
|
18311
18438
|
while (true) {
|
|
18312
18439
|
try {
|
|
@@ -18359,10 +18486,10 @@ async function readRegistry() {
|
|
|
18359
18486
|
throw err;
|
|
18360
18487
|
}
|
|
18361
18488
|
const parsed = JSON.parse(raw);
|
|
18362
|
-
return
|
|
18489
|
+
return import_types63.RegistryFileSchema.parse(parsed);
|
|
18363
18490
|
}
|
|
18364
18491
|
async function writeRegistry(reg) {
|
|
18365
|
-
const validated =
|
|
18492
|
+
const validated = import_types63.RegistryFileSchema.parse(reg);
|
|
18366
18493
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
18367
18494
|
}
|
|
18368
18495
|
async function getProject(name) {
|
|
@@ -18507,7 +18634,7 @@ init_auth();
|
|
|
18507
18634
|
// src/connectors-config.ts
|
|
18508
18635
|
init_cjs_shims();
|
|
18509
18636
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
18510
|
-
var
|
|
18637
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
18511
18638
|
var import_node_fs38 = require("fs");
|
|
18512
18639
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
18513
18640
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -18522,11 +18649,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
18522
18649
|
};
|
|
18523
18650
|
function neatHome2() {
|
|
18524
18651
|
const override = process.env.NEAT_HOME;
|
|
18525
|
-
if (override && override.length > 0) return
|
|
18526
|
-
return
|
|
18652
|
+
if (override && override.length > 0) return import_node_path74.default.resolve(override);
|
|
18653
|
+
return import_node_path74.default.join(import_node_os4.default.homedir(), ".neat");
|
|
18527
18654
|
}
|
|
18528
18655
|
function connectorsConfigPath(home = neatHome2()) {
|
|
18529
|
-
return
|
|
18656
|
+
return import_node_path74.default.join(home, "connectors.json");
|
|
18530
18657
|
}
|
|
18531
18658
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
18532
18659
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -18713,15 +18840,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
18713
18840
|
|
|
18714
18841
|
// src/connectors/index.ts
|
|
18715
18842
|
init_cjs_shims();
|
|
18716
|
-
var
|
|
18843
|
+
var import_types64 = require("@neat.is/types");
|
|
18717
18844
|
var NO_ENV = "unknown";
|
|
18718
18845
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
18719
18846
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
18720
18847
|
const sites = [];
|
|
18721
18848
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
18722
18849
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
18723
|
-
if (edge.provenance !==
|
|
18724
|
-
const parsed = (0,
|
|
18850
|
+
if (edge.provenance !== import_types64.Provenance.EXTRACTED) continue;
|
|
18851
|
+
const parsed = (0, import_types64.parseFileId)(edge.source);
|
|
18725
18852
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
18726
18853
|
const site = { relPath: edge.evidence.file };
|
|
18727
18854
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -18732,7 +18859,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
18732
18859
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
18733
18860
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
18734
18861
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
18735
|
-
if (attrs.type !==
|
|
18862
|
+
if (attrs.type !== import_types64.NodeType.RouteNode || !attrs.path) return void 0;
|
|
18736
18863
|
const site = { relPath: attrs.path };
|
|
18737
18864
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
18738
18865
|
return site;
|
|
@@ -19230,10 +19357,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
19230
19357
|
// src/connectors/supabase/map.ts
|
|
19231
19358
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
19232
19359
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
19233
|
-
function targetFromRestPath(
|
|
19234
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
19360
|
+
function targetFromRestPath(path80) {
|
|
19361
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path80);
|
|
19235
19362
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
19236
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
19363
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path80);
|
|
19237
19364
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
19238
19365
|
return null;
|
|
19239
19366
|
}
|
|
@@ -19281,7 +19408,7 @@ function tableNameFromQueryText(query) {
|
|
|
19281
19408
|
if (SYSTEM_SCHEMA_PREFIXES.some((prefix) => lower.startsWith(prefix))) return null;
|
|
19282
19409
|
return name;
|
|
19283
19410
|
}
|
|
19284
|
-
function diffPgStatStatementsToSignals(rows, previous,
|
|
19411
|
+
function diffPgStatStatementsToSignals(rows, previous, nowIso3) {
|
|
19285
19412
|
const signals = [];
|
|
19286
19413
|
const seen = /* @__PURE__ */ new Set();
|
|
19287
19414
|
if (!Array.isArray(rows)) return signals;
|
|
@@ -19303,7 +19430,7 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
|
|
|
19303
19430
|
targetName: table,
|
|
19304
19431
|
callCount: delta,
|
|
19305
19432
|
errorCount: 0,
|
|
19306
|
-
lastObservedIso:
|
|
19433
|
+
lastObservedIso: nowIso3,
|
|
19307
19434
|
...columns.length > 0 ? { columns } : {}
|
|
19308
19435
|
});
|
|
19309
19436
|
}
|
|
@@ -19344,23 +19471,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
19344
19471
|
|
|
19345
19472
|
// src/connectors/supabase/resolve.ts
|
|
19346
19473
|
init_cjs_shims();
|
|
19347
|
-
var
|
|
19474
|
+
var import_types66 = require("@neat.is/types");
|
|
19348
19475
|
function createSupabaseResolveTarget(graph, config) {
|
|
19349
19476
|
return (signal, _ctx) => {
|
|
19350
19477
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
19351
19478
|
return null;
|
|
19352
19479
|
}
|
|
19353
|
-
const subResourceId = (0,
|
|
19480
|
+
const subResourceId = (0, import_types66.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
19354
19481
|
if (graph.hasNode(subResourceId)) {
|
|
19355
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
19482
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
19356
19483
|
}
|
|
19357
|
-
const bareResourceId = (0,
|
|
19484
|
+
const bareResourceId = (0, import_types66.infraId)(signal.targetKind, signal.targetName);
|
|
19358
19485
|
if (graph.hasNode(bareResourceId)) {
|
|
19359
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
19486
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
19360
19487
|
}
|
|
19361
|
-
const projectLevelId = (0,
|
|
19488
|
+
const projectLevelId = (0, import_types66.infraId)("supabase", config.nodeRef);
|
|
19362
19489
|
if (graph.hasNode(projectLevelId)) {
|
|
19363
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
19490
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
19364
19491
|
}
|
|
19365
19492
|
return null;
|
|
19366
19493
|
};
|
|
@@ -19453,7 +19580,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
19453
19580
|
|
|
19454
19581
|
// src/connectors/railway/index.ts
|
|
19455
19582
|
init_cjs_shims();
|
|
19456
|
-
var
|
|
19583
|
+
var import_types70 = require("@neat.is/types");
|
|
19457
19584
|
|
|
19458
19585
|
// src/connectors/railway/client.ts
|
|
19459
19586
|
init_cjs_shims();
|
|
@@ -19604,7 +19731,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
19604
19731
|
const out = [];
|
|
19605
19732
|
graph.forEachNode((_id, attrs) => {
|
|
19606
19733
|
const node = attrs;
|
|
19607
|
-
if (node.type !==
|
|
19734
|
+
if (node.type !== import_types70.NodeType.RouteNode) return;
|
|
19608
19735
|
const route = attrs;
|
|
19609
19736
|
if (route.service !== serviceName) return;
|
|
19610
19737
|
out.push({
|
|
@@ -19708,12 +19835,12 @@ function createRailwayResolveTarget(config) {
|
|
|
19708
19835
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
19709
19836
|
if (!serviceName) return null;
|
|
19710
19837
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
19711
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
19838
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types70.EdgeType.CALLS };
|
|
19712
19839
|
}
|
|
19713
19840
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
19714
19841
|
const peerName = config.serviceNameById[signal.targetName];
|
|
19715
19842
|
if (!peerName) return null;
|
|
19716
|
-
return { targetNodeId: (0,
|
|
19843
|
+
return { targetNodeId: (0, import_types70.serviceId)(peerName), serviceName, edgeType: import_types70.EdgeType.CONNECTS_TO };
|
|
19717
19844
|
}
|
|
19718
19845
|
return null;
|
|
19719
19846
|
};
|
|
@@ -19837,9 +19964,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
19837
19964
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
19838
19965
|
if (secondSep === -1) return null;
|
|
19839
19966
|
const method = rest.slice(0, secondSep);
|
|
19840
|
-
const
|
|
19841
|
-
if (!resourceName || !method || !
|
|
19842
|
-
return { resourceName, method, path:
|
|
19967
|
+
const path80 = rest.slice(secondSep + 1);
|
|
19968
|
+
if (!resourceName || !method || !path80) return null;
|
|
19969
|
+
return { resourceName, method, path: path80 };
|
|
19843
19970
|
}
|
|
19844
19971
|
function resourceNameFor(type, labels) {
|
|
19845
19972
|
if (!labels) return null;
|
|
@@ -19877,14 +20004,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
19877
20004
|
if (!req2) return null;
|
|
19878
20005
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
19879
20006
|
const method = req2.requestMethod.toUpperCase();
|
|
19880
|
-
const
|
|
19881
|
-
if (
|
|
20007
|
+
const path80 = pathFromRequestUrl(req2.requestUrl);
|
|
20008
|
+
if (path80 === null) return null;
|
|
19882
20009
|
const timestamp = entry2.timestamp;
|
|
19883
20010
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
19884
20011
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
19885
20012
|
return {
|
|
19886
20013
|
targetKind: resourceType,
|
|
19887
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
20014
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path80 }),
|
|
19888
20015
|
callCount: 1,
|
|
19889
20016
|
errorCount: isError ? 1 : 0,
|
|
19890
20017
|
lastObservedIso: timestamp
|
|
@@ -19901,7 +20028,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
19901
20028
|
|
|
19902
20029
|
// src/connectors/firebase/resolve.ts
|
|
19903
20030
|
init_cjs_shims();
|
|
19904
|
-
var
|
|
20031
|
+
var import_types71 = require("@neat.is/types");
|
|
19905
20032
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
19906
20033
|
switch (resourceType) {
|
|
19907
20034
|
case "cloud_function":
|
|
@@ -19916,7 +20043,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
19916
20043
|
const entries = [];
|
|
19917
20044
|
graph.forEachNode((_id, attrs) => {
|
|
19918
20045
|
const node = attrs;
|
|
19919
|
-
if (node.type !==
|
|
20046
|
+
if (node.type !== import_types71.NodeType.RouteNode) return;
|
|
19920
20047
|
const route = attrs;
|
|
19921
20048
|
if (route.service !== serviceName) return;
|
|
19922
20049
|
entries.push({
|
|
@@ -19948,7 +20075,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
19948
20075
|
return {
|
|
19949
20076
|
targetNodeId: match.routeNodeId,
|
|
19950
20077
|
serviceName,
|
|
19951
|
-
edgeType:
|
|
20078
|
+
edgeType: import_types71.EdgeType.CALLS
|
|
19952
20079
|
};
|
|
19953
20080
|
};
|
|
19954
20081
|
}
|
|
@@ -19975,7 +20102,7 @@ init_cjs_shims();
|
|
|
19975
20102
|
|
|
19976
20103
|
// src/connectors/cloudflare/connector.ts
|
|
19977
20104
|
init_cjs_shims();
|
|
19978
|
-
var
|
|
20105
|
+
var import_types73 = require("@neat.is/types");
|
|
19979
20106
|
|
|
19980
20107
|
// src/connectors/cloudflare/client.ts
|
|
19981
20108
|
init_cjs_shims();
|
|
@@ -20091,7 +20218,7 @@ function mapEventToSignal(event) {
|
|
|
20091
20218
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
20092
20219
|
const statusCode = metadata?.statusCode;
|
|
20093
20220
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
20094
|
-
const
|
|
20221
|
+
const path80 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
20095
20222
|
return {
|
|
20096
20223
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
20097
20224
|
targetName: scriptName,
|
|
@@ -20099,7 +20226,7 @@ function mapEventToSignal(event) {
|
|
|
20099
20226
|
errorCount: isError ? 1 : 0,
|
|
20100
20227
|
lastObservedIso: observedAt.toISOString(),
|
|
20101
20228
|
method,
|
|
20102
|
-
...
|
|
20229
|
+
...path80 ? { path: path80 } : {},
|
|
20103
20230
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
20104
20231
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
20105
20232
|
};
|
|
@@ -20139,19 +20266,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
20139
20266
|
graph.forEachNode((id, attrs) => {
|
|
20140
20267
|
if (found) return;
|
|
20141
20268
|
const a = attrs;
|
|
20142
|
-
if (a.type ===
|
|
20269
|
+
if (a.type === import_types73.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
20143
20270
|
found = id;
|
|
20144
20271
|
}
|
|
20145
20272
|
});
|
|
20146
20273
|
return found;
|
|
20147
20274
|
}
|
|
20148
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
20149
|
-
const normalizedPath = normalizePathTemplate(
|
|
20275
|
+
function findMatchingRouteNode(graph, serviceName, method, path80) {
|
|
20276
|
+
const normalizedPath = normalizePathTemplate(path80);
|
|
20150
20277
|
let found = null;
|
|
20151
20278
|
graph.forEachNode((id, attrs) => {
|
|
20152
20279
|
if (found) return;
|
|
20153
20280
|
const a = attrs;
|
|
20154
|
-
if (a.type !==
|
|
20281
|
+
if (a.type !== import_types73.NodeType.RouteNode || a.service !== serviceName) return;
|
|
20155
20282
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
20156
20283
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
20157
20284
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -20163,18 +20290,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20163
20290
|
return (signal) => {
|
|
20164
20291
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
20165
20292
|
const scriptName = signal.targetName;
|
|
20166
|
-
const { method, path:
|
|
20293
|
+
const { method, path: path80 } = signal;
|
|
20167
20294
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
20168
|
-
if (!method || !
|
|
20169
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
20295
|
+
if (!method || !path80) return wholeFileId;
|
|
20296
|
+
return findMatchingRouteNode(graph, serviceName, method, path80) ?? wholeFileId;
|
|
20170
20297
|
};
|
|
20171
20298
|
const mapping = config.workers?.[scriptName];
|
|
20172
20299
|
if (mapping) {
|
|
20173
|
-
const wholeFileId = (0,
|
|
20300
|
+
const wholeFileId = (0, import_types73.fileId)(mapping.service, mapping.entryFile);
|
|
20174
20301
|
return {
|
|
20175
20302
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
20176
20303
|
serviceName: mapping.service,
|
|
20177
|
-
edgeType:
|
|
20304
|
+
edgeType: import_types73.EdgeType.CALLS
|
|
20178
20305
|
};
|
|
20179
20306
|
}
|
|
20180
20307
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -20183,13 +20310,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20183
20310
|
return {
|
|
20184
20311
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
20185
20312
|
serviceName: fileNode.service,
|
|
20186
|
-
edgeType:
|
|
20313
|
+
edgeType: import_types73.EdgeType.CALLS
|
|
20187
20314
|
};
|
|
20188
20315
|
}
|
|
20189
20316
|
return {
|
|
20190
|
-
targetNodeId: (0,
|
|
20317
|
+
targetNodeId: (0, import_types73.infraId)("cloudflare-worker", scriptName),
|
|
20191
20318
|
serviceName: scriptName,
|
|
20192
|
-
edgeType:
|
|
20319
|
+
edgeType: import_types73.EdgeType.CALLS,
|
|
20193
20320
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
20194
20321
|
};
|
|
20195
20322
|
};
|
|
@@ -20385,14 +20512,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
20385
20512
|
|
|
20386
20513
|
// src/connectors/neon/resolve.ts
|
|
20387
20514
|
init_cjs_shims();
|
|
20388
|
-
var
|
|
20515
|
+
var import_types77 = require("@neat.is/types");
|
|
20389
20516
|
function createNeonResolveTarget(config) {
|
|
20390
20517
|
return (signal) => {
|
|
20391
20518
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
20392
20519
|
return {
|
|
20393
|
-
targetNodeId: (0,
|
|
20520
|
+
targetNodeId: (0, import_types77.infraId)("sql-table", signal.targetName),
|
|
20394
20521
|
serviceName: config.serviceName,
|
|
20395
|
-
edgeType:
|
|
20522
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
20396
20523
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
20397
20524
|
};
|
|
20398
20525
|
};
|
|
@@ -20518,9 +20645,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
20518
20645
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
20519
20646
|
if (secondSep === -1) return null;
|
|
20520
20647
|
const method = rest.slice(0, secondSep);
|
|
20521
|
-
const
|
|
20522
|
-
if (!serviceName || !method || !
|
|
20523
|
-
return { serviceName, method, path:
|
|
20648
|
+
const path80 = rest.slice(secondSep + 1);
|
|
20649
|
+
if (!serviceName || !method || !path80) return null;
|
|
20650
|
+
return { serviceName, method, path: path80 };
|
|
20524
20651
|
}
|
|
20525
20652
|
|
|
20526
20653
|
// src/connectors/cloud-run/map.ts
|
|
@@ -20549,14 +20676,14 @@ function mapLogEntryToSignal2(entry2) {
|
|
|
20549
20676
|
if (!req2) return null;
|
|
20550
20677
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
20551
20678
|
const method = req2.requestMethod.toUpperCase();
|
|
20552
|
-
const
|
|
20553
|
-
if (
|
|
20679
|
+
const path80 = pathFromRequestUrl2(req2.requestUrl);
|
|
20680
|
+
if (path80 === null) return null;
|
|
20554
20681
|
const timestamp = entry2.timestamp;
|
|
20555
20682
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
20556
20683
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
|
|
20557
20684
|
return {
|
|
20558
20685
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
20559
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
20686
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path80 }),
|
|
20560
20687
|
callCount: 1,
|
|
20561
20688
|
errorCount: isError ? 1 : 0,
|
|
20562
20689
|
lastObservedIso: timestamp
|
|
@@ -20573,14 +20700,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
20573
20700
|
|
|
20574
20701
|
// src/connectors/cloud-run/resolve.ts
|
|
20575
20702
|
init_cjs_shims();
|
|
20576
|
-
var
|
|
20703
|
+
var import_types81 = require("@neat.is/types");
|
|
20577
20704
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
20578
20705
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
20579
20706
|
let found = null;
|
|
20580
20707
|
graph.forEachNode((_id, attrs) => {
|
|
20581
20708
|
if (found) return;
|
|
20582
20709
|
const node = attrs;
|
|
20583
|
-
if (node.type !==
|
|
20710
|
+
if (node.type !== import_types81.NodeType.RouteNode) return;
|
|
20584
20711
|
const route = attrs;
|
|
20585
20712
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20586
20713
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20595,23 +20722,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
20595
20722
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
20596
20723
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
20597
20724
|
if (!identity) return null;
|
|
20598
|
-
const { serviceName: gcpServiceName, method, path:
|
|
20725
|
+
const { serviceName: gcpServiceName, method, path: path80 } = identity;
|
|
20599
20726
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
20600
20727
|
if (mappedService) {
|
|
20601
20728
|
const routeNodeId = findMatchingRouteNode2(
|
|
20602
20729
|
graph,
|
|
20603
20730
|
mappedService,
|
|
20604
20731
|
method,
|
|
20605
|
-
normalizePathTemplate(
|
|
20732
|
+
normalizePathTemplate(path80)
|
|
20606
20733
|
);
|
|
20607
20734
|
if (routeNodeId) {
|
|
20608
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
20735
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types81.EdgeType.CALLS };
|
|
20609
20736
|
}
|
|
20610
20737
|
}
|
|
20611
20738
|
return {
|
|
20612
|
-
targetNodeId: (0,
|
|
20739
|
+
targetNodeId: (0, import_types81.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
20613
20740
|
serviceName: mappedService ?? gcpServiceName,
|
|
20614
|
-
edgeType:
|
|
20741
|
+
edgeType: import_types81.EdgeType.CALLS,
|
|
20615
20742
|
ensureInfraNode: {
|
|
20616
20743
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
20617
20744
|
name: gcpServiceName,
|
|
@@ -20738,9 +20865,9 @@ function parseGcpLbTargetName(targetName) {
|
|
|
20738
20865
|
const secondSep = rest.indexOf(FIELD_SEP3);
|
|
20739
20866
|
if (secondSep === -1) return null;
|
|
20740
20867
|
const method = rest.slice(0, secondSep);
|
|
20741
|
-
const
|
|
20742
|
-
if (!backendServiceName || !method || !
|
|
20743
|
-
return { backendServiceName, method, path:
|
|
20868
|
+
const path80 = rest.slice(secondSep + 1);
|
|
20869
|
+
if (!backendServiceName || !method || !path80) return null;
|
|
20870
|
+
return { backendServiceName, method, path: path80 };
|
|
20744
20871
|
}
|
|
20745
20872
|
|
|
20746
20873
|
// src/connectors/gcp-lb/map.ts
|
|
@@ -20769,14 +20896,14 @@ function mapLogEntryToSignal3(entry2) {
|
|
|
20769
20896
|
if (!req2) return null;
|
|
20770
20897
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
20771
20898
|
const method = req2.requestMethod.toUpperCase();
|
|
20772
|
-
const
|
|
20773
|
-
if (
|
|
20899
|
+
const path80 = pathFromRequestUrl3(req2.requestUrl);
|
|
20900
|
+
if (path80 === null) return null;
|
|
20774
20901
|
const timestamp = entry2.timestamp;
|
|
20775
20902
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
20776
20903
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD5;
|
|
20777
20904
|
return {
|
|
20778
20905
|
targetKind: GCP_LB_TARGET_KIND,
|
|
20779
|
-
targetName: packGcpLbTargetName({ backendServiceName, method, path:
|
|
20906
|
+
targetName: packGcpLbTargetName({ backendServiceName, method, path: path80 }),
|
|
20780
20907
|
callCount: 1,
|
|
20781
20908
|
errorCount: isError ? 1 : 0,
|
|
20782
20909
|
lastObservedIso: timestamp
|
|
@@ -20793,14 +20920,14 @@ function mapLogEntriesToSignals3(entries) {
|
|
|
20793
20920
|
|
|
20794
20921
|
// src/connectors/gcp-lb/resolve.ts
|
|
20795
20922
|
init_cjs_shims();
|
|
20796
|
-
var
|
|
20923
|
+
var import_types85 = require("@neat.is/types");
|
|
20797
20924
|
var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
|
|
20798
20925
|
function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
|
|
20799
20926
|
let found = null;
|
|
20800
20927
|
graph.forEachNode((_id, attrs) => {
|
|
20801
20928
|
if (found) return;
|
|
20802
20929
|
const node = attrs;
|
|
20803
|
-
if (node.type !==
|
|
20930
|
+
if (node.type !== import_types85.NodeType.RouteNode) return;
|
|
20804
20931
|
const route = attrs;
|
|
20805
20932
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20806
20933
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20815,23 +20942,23 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
20815
20942
|
if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
|
|
20816
20943
|
const identity = parseGcpLbTargetName(signal.targetName);
|
|
20817
20944
|
if (!identity) return null;
|
|
20818
|
-
const { backendServiceName, method, path:
|
|
20945
|
+
const { backendServiceName, method, path: path80 } = identity;
|
|
20819
20946
|
const mappedService = config.backendServiceMap?.[backendServiceName];
|
|
20820
20947
|
if (mappedService) {
|
|
20821
20948
|
const routeNodeId = findMatchingRouteNode3(
|
|
20822
20949
|
graph,
|
|
20823
20950
|
mappedService,
|
|
20824
20951
|
method,
|
|
20825
|
-
normalizePathTemplate(
|
|
20952
|
+
normalizePathTemplate(path80)
|
|
20826
20953
|
);
|
|
20827
20954
|
if (routeNodeId) {
|
|
20828
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
20955
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types85.EdgeType.CALLS };
|
|
20829
20956
|
}
|
|
20830
20957
|
}
|
|
20831
20958
|
return {
|
|
20832
|
-
targetNodeId: (0,
|
|
20959
|
+
targetNodeId: (0, import_types85.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
|
|
20833
20960
|
serviceName: mappedService ?? backendServiceName,
|
|
20834
|
-
edgeType:
|
|
20961
|
+
edgeType: import_types85.EdgeType.CALLS,
|
|
20835
20962
|
ensureInfraNode: {
|
|
20836
20963
|
kind: GCP_LB_BACKEND_INFRA_KIND,
|
|
20837
20964
|
name: backendServiceName,
|
|
@@ -20872,7 +20999,7 @@ function createGcpLbConnector(graph, config = {}) {
|
|
|
20872
20999
|
|
|
20873
21000
|
// src/connectors/render/index.ts
|
|
20874
21001
|
init_cjs_shims();
|
|
20875
|
-
var
|
|
21002
|
+
var import_types88 = require("@neat.is/types");
|
|
20876
21003
|
|
|
20877
21004
|
// src/connectors/render/types.ts
|
|
20878
21005
|
init_cjs_shims();
|
|
@@ -20950,7 +21077,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
20950
21077
|
const out = [];
|
|
20951
21078
|
graph.forEachNode((_id, attrs) => {
|
|
20952
21079
|
const node = attrs;
|
|
20953
|
-
if (node.type !==
|
|
21080
|
+
if (node.type !== import_types88.NodeType.RouteNode) return;
|
|
20954
21081
|
const route = attrs;
|
|
20955
21082
|
if (route.service !== serviceName) return;
|
|
20956
21083
|
out.push({
|
|
@@ -21035,7 +21162,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
21035
21162
|
function createRenderResolveTarget(config) {
|
|
21036
21163
|
return (signal) => {
|
|
21037
21164
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
21038
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
21165
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types88.EdgeType.CALLS };
|
|
21039
21166
|
}
|
|
21040
21167
|
return null;
|
|
21041
21168
|
};
|
|
@@ -21173,21 +21300,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
21173
21300
|
|
|
21174
21301
|
// src/connectors/planetscale/resolve.ts
|
|
21175
21302
|
init_cjs_shims();
|
|
21176
|
-
var
|
|
21303
|
+
var import_types92 = require("@neat.is/types");
|
|
21177
21304
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
21178
21305
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
21179
21306
|
const databaseName = `${config.organization}/${config.database}`;
|
|
21180
21307
|
return (signal, _ctx) => {
|
|
21181
21308
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
21182
|
-
const tableId = (0,
|
|
21309
|
+
const tableId = (0, import_types92.infraId)("sql-table", signal.targetName);
|
|
21183
21310
|
if (graph.hasNode(tableId)) {
|
|
21184
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
21311
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types92.EdgeType.CALLS };
|
|
21185
21312
|
}
|
|
21186
|
-
const providerId = (0,
|
|
21313
|
+
const providerId = (0, import_types92.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
21187
21314
|
return {
|
|
21188
21315
|
targetNodeId: providerId,
|
|
21189
21316
|
serviceName: config.serviceName,
|
|
21190
|
-
edgeType:
|
|
21317
|
+
edgeType: import_types92.EdgeType.CALLS,
|
|
21191
21318
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
21192
21319
|
};
|
|
21193
21320
|
};
|
|
@@ -21452,7 +21579,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
21452
21579
|
|
|
21453
21580
|
// src/connectors/eas/resolve.ts
|
|
21454
21581
|
init_cjs_shims();
|
|
21455
|
-
var
|
|
21582
|
+
var import_types97 = require("@neat.is/types");
|
|
21456
21583
|
var NO_ENV2 = "unknown";
|
|
21457
21584
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
21458
21585
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -21468,8 +21595,8 @@ function configBasenamesForPhase(phase) {
|
|
|
21468
21595
|
function configNodeService(graph, configNodeId) {
|
|
21469
21596
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
21470
21597
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
21471
|
-
if (edge.type !==
|
|
21472
|
-
const parsed = (0,
|
|
21598
|
+
if (edge.type !== import_types97.EdgeType.CONFIGURED_BY) continue;
|
|
21599
|
+
const parsed = (0, import_types97.parseFileId)(edge.source);
|
|
21473
21600
|
if (parsed) return parsed.service;
|
|
21474
21601
|
}
|
|
21475
21602
|
return null;
|
|
@@ -21480,7 +21607,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
21480
21607
|
graph.forEachNode((id, attrs) => {
|
|
21481
21608
|
if (scoped) return;
|
|
21482
21609
|
const node = attrs;
|
|
21483
|
-
if (node.type !==
|
|
21610
|
+
if (node.type !== import_types97.NodeType.ConfigNode) return;
|
|
21484
21611
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
21485
21612
|
if (anyMatch === null) anyMatch = id;
|
|
21486
21613
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -21497,13 +21624,13 @@ function createEasResolveTarget(graph) {
|
|
|
21497
21624
|
if (basenames.length > 0) {
|
|
21498
21625
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
21499
21626
|
if (configNodeId) {
|
|
21500
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
21627
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types97.EdgeType.CALLS };
|
|
21501
21628
|
}
|
|
21502
21629
|
}
|
|
21503
21630
|
return {
|
|
21504
21631
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
21505
21632
|
serviceName,
|
|
21506
|
-
edgeType:
|
|
21633
|
+
edgeType: import_types97.EdgeType.CALLS
|
|
21507
21634
|
};
|
|
21508
21635
|
};
|
|
21509
21636
|
}
|
|
@@ -22120,11 +22247,11 @@ function serializeGraph(graph) {
|
|
|
22120
22247
|
}
|
|
22121
22248
|
function projectFromReq(req2, singleProject) {
|
|
22122
22249
|
const params = req2.params;
|
|
22123
|
-
const
|
|
22250
|
+
const named2 = params.project;
|
|
22124
22251
|
if (singleProject) {
|
|
22125
|
-
return
|
|
22252
|
+
return named2 === void 0 || named2 === DEFAULT_PROJECT ? singleProject : named2;
|
|
22126
22253
|
}
|
|
22127
|
-
return
|
|
22254
|
+
return named2 ?? DEFAULT_PROJECT;
|
|
22128
22255
|
}
|
|
22129
22256
|
function resolveProject(registry, req2, reply, bootstrap, singleProject) {
|
|
22130
22257
|
const name = projectFromReq(req2, singleProject);
|
|
@@ -22265,11 +22392,11 @@ function registerRoutes(scope, ctx) {
|
|
|
22265
22392
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
22266
22393
|
const parsed = [];
|
|
22267
22394
|
for (const c of candidates) {
|
|
22268
|
-
const r =
|
|
22395
|
+
const r = import_types100.DivergenceTypeSchema.safeParse(c);
|
|
22269
22396
|
if (!r.success) {
|
|
22270
22397
|
return reply.code(400).send({
|
|
22271
22398
|
error: `unknown divergence type "${c}"`,
|
|
22272
|
-
allowed:
|
|
22399
|
+
allowed: import_types100.DivergenceTypeSchema.options
|
|
22273
22400
|
});
|
|
22274
22401
|
}
|
|
22275
22402
|
parsed.push(r.data);
|
|
@@ -22665,7 +22792,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22665
22792
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
22666
22793
|
let violations = await log.readAll();
|
|
22667
22794
|
if (req2.query.severity) {
|
|
22668
|
-
const sev =
|
|
22795
|
+
const sev = import_types100.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
22669
22796
|
if (!sev.success) {
|
|
22670
22797
|
return reply.code(400).send({
|
|
22671
22798
|
error: "invalid severity",
|
|
@@ -22704,7 +22831,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22704
22831
|
scope.post("/policies/check", async (req2, reply) => {
|
|
22705
22832
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
22706
22833
|
if (!proj) return;
|
|
22707
|
-
const parsed =
|
|
22834
|
+
const parsed = import_types100.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
22708
22835
|
if (!parsed.success) {
|
|
22709
22836
|
return reply.code(400).send({
|
|
22710
22837
|
error: "invalid /policies/check body",
|
|
@@ -23021,12 +23148,454 @@ async function buildApi(opts) {
|
|
|
23021
23148
|
|
|
23022
23149
|
// src/daemon.ts
|
|
23023
23150
|
init_otel();
|
|
23151
|
+
|
|
23152
|
+
// src/connectors/kubernetes/index.ts
|
|
23153
|
+
init_cjs_shims();
|
|
23154
|
+
|
|
23155
|
+
// src/connectors/kubernetes/client.ts
|
|
23156
|
+
init_cjs_shims();
|
|
23157
|
+
var import_node_https = require("https");
|
|
23158
|
+
function deploymentsPath(namespace) {
|
|
23159
|
+
return `/apis/apps/v1/namespaces/${namespace}/deployments`;
|
|
23160
|
+
}
|
|
23161
|
+
function podsPath(namespace) {
|
|
23162
|
+
return `/api/v1/namespaces/${namespace}/pods`;
|
|
23163
|
+
}
|
|
23164
|
+
function makeK8sFetchImpl(transport) {
|
|
23165
|
+
const agent = new import_node_https.Agent({
|
|
23166
|
+
...transport.ca ? { ca: transport.ca } : {},
|
|
23167
|
+
...transport.clientCert ? { cert: transport.clientCert } : {},
|
|
23168
|
+
...transport.clientKey ? { key: transport.clientKey } : {},
|
|
23169
|
+
rejectUnauthorized: !transport.insecureSkipTlsVerify
|
|
23170
|
+
});
|
|
23171
|
+
return ((url, init) => new Promise((resolve, reject) => {
|
|
23172
|
+
const u = new URL(String(url));
|
|
23173
|
+
const req2 = (0, import_node_https.request)(
|
|
23174
|
+
u,
|
|
23175
|
+
{
|
|
23176
|
+
method: (init?.method ?? "GET").toUpperCase(),
|
|
23177
|
+
headers: init?.headers ?? {},
|
|
23178
|
+
agent
|
|
23179
|
+
},
|
|
23180
|
+
(res) => {
|
|
23181
|
+
const chunks = [];
|
|
23182
|
+
res.on("data", (c) => chunks.push(c));
|
|
23183
|
+
res.on("end", () => {
|
|
23184
|
+
const body = Buffer.concat(chunks).toString("utf8");
|
|
23185
|
+
const status2 = res.statusCode ?? 0;
|
|
23186
|
+
resolve({
|
|
23187
|
+
ok: status2 >= 200 && status2 < 300,
|
|
23188
|
+
status: status2,
|
|
23189
|
+
statusText: res.statusMessage ?? "",
|
|
23190
|
+
json: async () => JSON.parse(body),
|
|
23191
|
+
text: async () => body
|
|
23192
|
+
});
|
|
23193
|
+
});
|
|
23194
|
+
}
|
|
23195
|
+
);
|
|
23196
|
+
req2.on("error", reject);
|
|
23197
|
+
const signal = init?.signal;
|
|
23198
|
+
if (signal) {
|
|
23199
|
+
if (signal.aborted) req2.destroy(new Error("aborted"));
|
|
23200
|
+
else signal.addEventListener("abort", () => req2.destroy(new Error("aborted")), { once: true });
|
|
23201
|
+
}
|
|
23202
|
+
req2.end();
|
|
23203
|
+
}));
|
|
23204
|
+
}
|
|
23205
|
+
async function listResource(transport, namespace, path80, opts = {}) {
|
|
23206
|
+
const base = opts.apiUrl ?? transport.server;
|
|
23207
|
+
const url = `${base.replace(/\/$/, "")}${path80}`;
|
|
23208
|
+
const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
|
|
23209
|
+
const res = await junctionFetch(
|
|
23210
|
+
url,
|
|
23211
|
+
{ method: "GET", headers: { ...transport.token ? bearerAuthHeader(transport.token) : {}, Accept: "application/json" } },
|
|
23212
|
+
// accountKey: the (cluster, namespace) pair — an identifier, safe to log, the
|
|
23213
|
+
// rate-limit bucket for one namespace on one cluster (ADR-131).
|
|
23214
|
+
{ provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
|
|
23215
|
+
);
|
|
23216
|
+
if (!res.ok) {
|
|
23217
|
+
throw new Error(`kubernetes ${path80} failed: ${res.status} ${res.statusText}`);
|
|
23218
|
+
}
|
|
23219
|
+
const json = await res.json();
|
|
23220
|
+
return Array.isArray(json.items) ? json.items : [];
|
|
23221
|
+
}
|
|
23222
|
+
function safeHost(server) {
|
|
23223
|
+
try {
|
|
23224
|
+
return new URL(server).host;
|
|
23225
|
+
} catch {
|
|
23226
|
+
return "cluster";
|
|
23227
|
+
}
|
|
23228
|
+
}
|
|
23229
|
+
async function fetchDeployments(transport, namespace, opts = {}) {
|
|
23230
|
+
return listResource(transport, namespace, deploymentsPath(namespace), opts);
|
|
23231
|
+
}
|
|
23232
|
+
async function fetchPods(transport, namespace, opts = {}) {
|
|
23233
|
+
return listResource(transport, namespace, podsPath(namespace), opts);
|
|
23234
|
+
}
|
|
23235
|
+
|
|
23236
|
+
// src/connectors/kubernetes/kubeconfig.ts
|
|
23237
|
+
init_cjs_shims();
|
|
23238
|
+
var import_node_fs39 = require("fs");
|
|
23239
|
+
var import_yaml4 = require("yaml");
|
|
23240
|
+
function pemFrom(dataField, pathField) {
|
|
23241
|
+
if (typeof dataField === "string" && dataField.length > 0) {
|
|
23242
|
+
return Buffer.from(dataField, "base64").toString("utf8");
|
|
23243
|
+
}
|
|
23244
|
+
if (typeof pathField === "string" && pathField.length > 0) {
|
|
23245
|
+
return (0, import_node_fs39.readFileSync)(pathField, "utf8");
|
|
23246
|
+
}
|
|
23247
|
+
return void 0;
|
|
23248
|
+
}
|
|
23249
|
+
function named(list, name) {
|
|
23250
|
+
if (!Array.isArray(list)) return void 0;
|
|
23251
|
+
const hit = list.find((e) => e && e.name === name);
|
|
23252
|
+
return hit;
|
|
23253
|
+
}
|
|
23254
|
+
function parseKubeconfig(kubeconfig) {
|
|
23255
|
+
const looksInline = /\n/.test(kubeconfig) || /(^|\s)clusters\s*:/.test(kubeconfig);
|
|
23256
|
+
const text = looksInline ? kubeconfig : (0, import_node_fs39.readFileSync)(kubeconfig, "utf8");
|
|
23257
|
+
let doc;
|
|
23258
|
+
try {
|
|
23259
|
+
doc = (0, import_yaml4.parse)(text);
|
|
23260
|
+
} catch {
|
|
23261
|
+
throw new Error("kubernetes connector: kubeconfig is not valid YAML");
|
|
23262
|
+
}
|
|
23263
|
+
if (!doc || typeof doc !== "object") {
|
|
23264
|
+
throw new Error("kubernetes connector: kubeconfig is empty or malformed");
|
|
23265
|
+
}
|
|
23266
|
+
const currentContext = doc["current-context"];
|
|
23267
|
+
if (typeof currentContext !== "string" || currentContext.length === 0) {
|
|
23268
|
+
throw new Error("kubernetes connector: kubeconfig has no current-context");
|
|
23269
|
+
}
|
|
23270
|
+
const ctxEntry = named(doc["contexts"], currentContext);
|
|
23271
|
+
const ctx = ctxEntry?.context;
|
|
23272
|
+
if (!ctx) {
|
|
23273
|
+
throw new Error(`kubernetes connector: kubeconfig context "${currentContext}" not found`);
|
|
23274
|
+
}
|
|
23275
|
+
const clusterEntry = named(doc["clusters"], String(ctx["cluster"] ?? ""));
|
|
23276
|
+
const cluster = clusterEntry?.cluster;
|
|
23277
|
+
if (!cluster || typeof cluster["server"] !== "string") {
|
|
23278
|
+
throw new Error("kubernetes connector: kubeconfig current context has no cluster server");
|
|
23279
|
+
}
|
|
23280
|
+
const userEntry = named(doc["users"], String(ctx["user"] ?? ""));
|
|
23281
|
+
const user = userEntry?.user ?? {};
|
|
23282
|
+
const transport = {
|
|
23283
|
+
server: cluster["server"],
|
|
23284
|
+
insecureSkipTlsVerify: cluster["insecure-skip-tls-verify"] === true
|
|
23285
|
+
};
|
|
23286
|
+
const ca = pemFrom(cluster["certificate-authority-data"], cluster["certificate-authority"]);
|
|
23287
|
+
if (ca) transport.ca = ca;
|
|
23288
|
+
if (typeof user["token"] === "string" && user["token"].length > 0) transport.token = user["token"];
|
|
23289
|
+
const clientCert = pemFrom(user["client-certificate-data"], user["client-certificate"]);
|
|
23290
|
+
const clientKey = pemFrom(user["client-key-data"], user["client-key"]);
|
|
23291
|
+
if (clientCert) transport.clientCert = clientCert;
|
|
23292
|
+
if (clientKey) transport.clientKey = clientKey;
|
|
23293
|
+
return transport;
|
|
23294
|
+
}
|
|
23295
|
+
function resolveK8sTransport(creds, config) {
|
|
23296
|
+
if (creds.kubeconfig) return parseKubeconfig(creds.kubeconfig);
|
|
23297
|
+
if (!config.apiServerUrl) {
|
|
23298
|
+
throw new Error("kubernetes connector: options.apiServerUrl is required with a token credential");
|
|
23299
|
+
}
|
|
23300
|
+
const transport = {
|
|
23301
|
+
server: config.apiServerUrl,
|
|
23302
|
+
insecureSkipTlsVerify: config.insecureSkipTlsVerify === true
|
|
23303
|
+
};
|
|
23304
|
+
if (creds.token) transport.token = creds.token;
|
|
23305
|
+
if (config.caCert) transport.ca = config.caCert;
|
|
23306
|
+
return transport;
|
|
23307
|
+
}
|
|
23308
|
+
|
|
23309
|
+
// src/connectors/kubernetes/map.ts
|
|
23310
|
+
init_cjs_shims();
|
|
23311
|
+
|
|
23312
|
+
// src/connectors/kubernetes/types.ts
|
|
23313
|
+
init_cjs_shims();
|
|
23314
|
+
function readK8sCredentials(raw) {
|
|
23315
|
+
const token = typeof raw["token"] === "string" && raw["token"].length > 0 ? raw["token"] : void 0;
|
|
23316
|
+
const kubeconfig = typeof raw["kubeconfig"] === "string" && raw["kubeconfig"].length > 0 ? raw["kubeconfig"] : void 0;
|
|
23317
|
+
if (!token && !kubeconfig) {
|
|
23318
|
+
throw new Error("kubernetes connector: credentials must carry a token or a kubeconfig");
|
|
23319
|
+
}
|
|
23320
|
+
const out = {};
|
|
23321
|
+
if (token) out.token = token;
|
|
23322
|
+
if (kubeconfig) out.kubeconfig = kubeconfig;
|
|
23323
|
+
return out;
|
|
23324
|
+
}
|
|
23325
|
+
var IMAGE_PULL_REASONS = /* @__PURE__ */ new Set(["ImagePullBackOff", "ErrImagePull", "InvalidImageName"]);
|
|
23326
|
+
var CRASH_LOOP_REASON = "CrashLoopBackOff";
|
|
23327
|
+
var FIELD_SEP5 = "\0";
|
|
23328
|
+
var K8S_TARGET_KIND = "k8s-workload";
|
|
23329
|
+
function packK8sTargetName(identity) {
|
|
23330
|
+
return [identity.serviceName, identity.fault].join(FIELD_SEP5);
|
|
23331
|
+
}
|
|
23332
|
+
function parseK8sTargetName(targetName) {
|
|
23333
|
+
const sep = targetName.indexOf(FIELD_SEP5);
|
|
23334
|
+
if (sep === -1) return null;
|
|
23335
|
+
const serviceName = targetName.slice(0, sep);
|
|
23336
|
+
const fault = targetName.slice(sep + 1);
|
|
23337
|
+
if (!serviceName || !fault) return null;
|
|
23338
|
+
return { serviceName, fault };
|
|
23339
|
+
}
|
|
23340
|
+
|
|
23341
|
+
// src/connectors/kubernetes/map.ts
|
|
23342
|
+
function serviceNameFor(deployment, config) {
|
|
23343
|
+
const name = deployment.metadata?.name ?? "";
|
|
23344
|
+
return config.serviceMap?.[name] ?? name;
|
|
23345
|
+
}
|
|
23346
|
+
function podMatchesSelector(pod, selector) {
|
|
23347
|
+
if (!selector || Object.keys(selector).length === 0) return false;
|
|
23348
|
+
const labels = pod.metadata?.labels ?? {};
|
|
23349
|
+
return Object.entries(selector).every(([k, v]) => labels[k] === v);
|
|
23350
|
+
}
|
|
23351
|
+
function podsForDeployment(deployment, pods) {
|
|
23352
|
+
const selector = deployment.spec?.selector?.matchLabels;
|
|
23353
|
+
return pods.filter((p) => podMatchesSelector(p, selector));
|
|
23354
|
+
}
|
|
23355
|
+
function nowIso2() {
|
|
23356
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
23357
|
+
}
|
|
23358
|
+
function podLevelFault(deployment, pods) {
|
|
23359
|
+
const name = deployment.metadata?.name ?? "";
|
|
23360
|
+
const owned = podsForDeployment(deployment, pods);
|
|
23361
|
+
let crash = null;
|
|
23362
|
+
for (const pod of owned) {
|
|
23363
|
+
for (const cs of pod.status?.containerStatuses ?? []) {
|
|
23364
|
+
const reason = cs.state?.waiting?.reason;
|
|
23365
|
+
if (typeof reason !== "string") continue;
|
|
23366
|
+
if (IMAGE_PULL_REASONS.has(reason)) {
|
|
23367
|
+
const image = typeof cs.image === "string" ? cs.image : "unknown image";
|
|
23368
|
+
const attrs = { "k8s.image": image, "k8s.waitingReason": reason };
|
|
23369
|
+
const wm = cs.state?.waiting?.message;
|
|
23370
|
+
if (typeof wm === "string" && wm.length > 0) attrs["k8s.waitingMessage"] = wm;
|
|
23371
|
+
return {
|
|
23372
|
+
fault: "image-pull",
|
|
23373
|
+
message: `Deployment ${name} cannot pull image ${image} (${reason})`,
|
|
23374
|
+
timestamp: pod.status?.startTime ?? nowIso2(),
|
|
23375
|
+
attributes: attrs
|
|
23376
|
+
};
|
|
23377
|
+
}
|
|
23378
|
+
if (reason === CRASH_LOOP_REASON && !crash) crash = { cs, pod };
|
|
23379
|
+
}
|
|
23380
|
+
}
|
|
23381
|
+
if (crash) {
|
|
23382
|
+
const { cs, pod } = crash;
|
|
23383
|
+
const term = cs.lastState?.terminated;
|
|
23384
|
+
const termReason = typeof term?.reason === "string" ? term.reason : void 0;
|
|
23385
|
+
const termMsg = typeof term?.message === "string" ? term.message.trim() : void 0;
|
|
23386
|
+
const restarts = typeof cs.restartCount === "number" ? cs.restartCount : 0;
|
|
23387
|
+
const detail = termReason ? `last terminated: ${termReason}${termMsg ? ` \u2014 ${termMsg}` : ""}${typeof term?.exitCode === "number" ? ` (exit ${term.exitCode})` : ""}` : "no last-termination detail reported";
|
|
23388
|
+
const attrs = { "k8s.waitingReason": CRASH_LOOP_REASON, "k8s.restartCount": restarts };
|
|
23389
|
+
if (termReason) attrs["k8s.terminatedReason"] = termReason;
|
|
23390
|
+
if (termMsg) attrs["k8s.terminatedMessage"] = termMsg;
|
|
23391
|
+
if (typeof term?.exitCode === "number") attrs["k8s.exitCode"] = term.exitCode;
|
|
23392
|
+
if (typeof cs.image === "string") attrs["k8s.image"] = cs.image;
|
|
23393
|
+
return {
|
|
23394
|
+
fault: "crash-loop",
|
|
23395
|
+
message: `Deployment ${name} is crashlooping (restarts: ${restarts}); ${detail}`,
|
|
23396
|
+
timestamp: term?.finishedAt ?? pod.status?.startTime ?? nowIso2(),
|
|
23397
|
+
attributes: attrs
|
|
23398
|
+
};
|
|
23399
|
+
}
|
|
23400
|
+
return null;
|
|
23401
|
+
}
|
|
23402
|
+
function classifyDeployment(deployment, pods, expectedZero) {
|
|
23403
|
+
const name = deployment.metadata?.name ?? "";
|
|
23404
|
+
const desired = typeof deployment.spec?.replicas === "number" ? deployment.spec.replicas : 1;
|
|
23405
|
+
const ready = typeof deployment.status?.readyReplicas === "number" ? deployment.status.readyReplicas : 0;
|
|
23406
|
+
if (desired === 0) {
|
|
23407
|
+
if (expectedZero?.has(name)) return null;
|
|
23408
|
+
return {
|
|
23409
|
+
fault: "scaled-to-zero",
|
|
23410
|
+
message: `Deployment ${name} is scaled to 0 \u2014 no running pods (desired 0)`,
|
|
23411
|
+
timestamp: nowIso2(),
|
|
23412
|
+
attributes: { "k8s.desiredReplicas": 0, "k8s.readyReplicas": ready }
|
|
23413
|
+
};
|
|
23414
|
+
}
|
|
23415
|
+
if (ready >= desired) return null;
|
|
23416
|
+
const podFault = podLevelFault(deployment, pods);
|
|
23417
|
+
if (podFault) {
|
|
23418
|
+
podFault.attributes["k8s.desiredReplicas"] = desired;
|
|
23419
|
+
podFault.attributes["k8s.readyReplicas"] = ready;
|
|
23420
|
+
return podFault;
|
|
23421
|
+
}
|
|
23422
|
+
return {
|
|
23423
|
+
fault: "no-ready-replicas",
|
|
23424
|
+
message: `Deployment ${name} has no ready replicas (desired ${desired}, ready ${ready})`,
|
|
23425
|
+
timestamp: nowIso2(),
|
|
23426
|
+
attributes: { "k8s.desiredReplicas": desired, "k8s.readyReplicas": ready }
|
|
23427
|
+
};
|
|
23428
|
+
}
|
|
23429
|
+
function mapDeploymentToSignal(deployment, pods, config) {
|
|
23430
|
+
const name = deployment.metadata?.name;
|
|
23431
|
+
if (typeof name !== "string" || name.length === 0) return null;
|
|
23432
|
+
const expectedZero = config.expectedZero ? new Set(config.expectedZero) : void 0;
|
|
23433
|
+
const finding = classifyDeployment(deployment, pods, expectedZero);
|
|
23434
|
+
if (!finding) return null;
|
|
23435
|
+
const serviceName = serviceNameFor(deployment, config);
|
|
23436
|
+
const namespace = deployment.metadata?.namespace ?? config.namespace;
|
|
23437
|
+
const attributes = {
|
|
23438
|
+
"k8s.namespace": namespace,
|
|
23439
|
+
"k8s.deployment": name,
|
|
23440
|
+
"k8s.fault": finding.fault,
|
|
23441
|
+
...finding.attributes
|
|
23442
|
+
};
|
|
23443
|
+
return {
|
|
23444
|
+
targetKind: K8S_TARGET_KIND,
|
|
23445
|
+
targetName: packK8sTargetName({ serviceName, fault: finding.fault }),
|
|
23446
|
+
// Incident-only — no edge, so no call/error count to replay.
|
|
23447
|
+
callCount: 0,
|
|
23448
|
+
errorCount: 0,
|
|
23449
|
+
lastObservedIso: finding.timestamp,
|
|
23450
|
+
incident: {
|
|
23451
|
+
id: `k8s:deploy:${namespace}:${name}:${finding.fault}`,
|
|
23452
|
+
timestamp: finding.timestamp,
|
|
23453
|
+
service: serviceName,
|
|
23454
|
+
errorType: "k8s-deploy-failure",
|
|
23455
|
+
errorMessage: finding.message,
|
|
23456
|
+
attributes
|
|
23457
|
+
}
|
|
23458
|
+
};
|
|
23459
|
+
}
|
|
23460
|
+
function mapWorkloadsToSignals(deployments, pods, config) {
|
|
23461
|
+
const out = [];
|
|
23462
|
+
for (const deployment of deployments) {
|
|
23463
|
+
const signal = mapDeploymentToSignal(deployment, pods, config);
|
|
23464
|
+
if (signal) out.push(signal);
|
|
23465
|
+
}
|
|
23466
|
+
return out;
|
|
23467
|
+
}
|
|
23468
|
+
|
|
23469
|
+
// src/connectors/kubernetes/resolve.ts
|
|
23470
|
+
init_cjs_shims();
|
|
23471
|
+
var import_types102 = require("@neat.is/types");
|
|
23472
|
+
var NO_ENV3 = "unknown";
|
|
23473
|
+
function createK8sResolveTarget(graph) {
|
|
23474
|
+
return (signal) => {
|
|
23475
|
+
if (signal.targetKind !== K8S_TARGET_KIND) return null;
|
|
23476
|
+
const identity = parseK8sTargetName(signal.targetName);
|
|
23477
|
+
if (!identity) return null;
|
|
23478
|
+
const { serviceName } = identity;
|
|
23479
|
+
return {
|
|
23480
|
+
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV3),
|
|
23481
|
+
serviceName,
|
|
23482
|
+
edgeType: import_types102.EdgeType.CALLS
|
|
23483
|
+
};
|
|
23484
|
+
};
|
|
23485
|
+
}
|
|
23486
|
+
|
|
23487
|
+
// src/connectors/kubernetes/substrate.ts
|
|
23488
|
+
init_cjs_shims();
|
|
23489
|
+
var import_promises = require("fs/promises");
|
|
23490
|
+
var import_node_os5 = __toESM(require("os"), 1);
|
|
23491
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
23492
|
+
function defaultHome() {
|
|
23493
|
+
const override = process.env.NEAT_HOME;
|
|
23494
|
+
if (override && override.length > 0) return import_node_path75.default.resolve(override);
|
|
23495
|
+
return import_node_path75.default.join(import_node_os5.default.homedir(), ".neat");
|
|
23496
|
+
}
|
|
23497
|
+
function k8sSubstrateConfigPath(home = defaultHome()) {
|
|
23498
|
+
return import_node_path75.default.join(home, "k8s.json");
|
|
23499
|
+
}
|
|
23500
|
+
async function readK8sSubstrateConfig(home = defaultHome()) {
|
|
23501
|
+
let raw;
|
|
23502
|
+
try {
|
|
23503
|
+
raw = await (0, import_promises.readFile)(k8sSubstrateConfigPath(home), "utf8");
|
|
23504
|
+
} catch {
|
|
23505
|
+
return { version: 1, deployments: [] };
|
|
23506
|
+
}
|
|
23507
|
+
try {
|
|
23508
|
+
const parsed = JSON.parse(raw);
|
|
23509
|
+
if (!parsed || !Array.isArray(parsed.deployments)) return { version: 1, deployments: [] };
|
|
23510
|
+
return parsed;
|
|
23511
|
+
} catch {
|
|
23512
|
+
return { version: 1, deployments: [] };
|
|
23513
|
+
}
|
|
23514
|
+
}
|
|
23515
|
+
async function startK8sSubstratePolling(input) {
|
|
23516
|
+
const config = await readK8sSubstrateConfig(input.home);
|
|
23517
|
+
const env = input.env ?? process.env;
|
|
23518
|
+
const stops = [];
|
|
23519
|
+
for (const entry2 of config.deployments) {
|
|
23520
|
+
if (entry2.project !== void 0 && entry2.project !== input.project) continue;
|
|
23521
|
+
if (typeof entry2.namespace !== "string" || entry2.namespace.length === 0) {
|
|
23522
|
+
input.onSkip?.(entry2, "missing namespace");
|
|
23523
|
+
continue;
|
|
23524
|
+
}
|
|
23525
|
+
let credentials;
|
|
23526
|
+
try {
|
|
23527
|
+
const resolved = resolveCredential(entry2.credential, env);
|
|
23528
|
+
credentials = resolved.kind === "fields" ? { ...resolved.fields } : { token: resolved.value };
|
|
23529
|
+
} catch (err) {
|
|
23530
|
+
input.onSkip?.(entry2, err.message);
|
|
23531
|
+
continue;
|
|
23532
|
+
}
|
|
23533
|
+
const cfg = {
|
|
23534
|
+
namespace: entry2.namespace,
|
|
23535
|
+
...entry2.apiServerUrl ? { apiServerUrl: entry2.apiServerUrl } : {},
|
|
23536
|
+
...entry2.caCert ? { caCert: entry2.caCert } : {},
|
|
23537
|
+
...entry2.insecureSkipTlsVerify ? { insecureSkipTlsVerify: true } : {},
|
|
23538
|
+
...entry2.serviceMap ? { serviceMap: entry2.serviceMap } : {},
|
|
23539
|
+
...entry2.expectedZero ? { expectedZero: entry2.expectedZero } : {}
|
|
23540
|
+
};
|
|
23541
|
+
const { connector, resolveTarget } = createKubernetesConnector(input.graph, cfg, input.fetchImpl);
|
|
23542
|
+
const stop = startConnectorPollLoop(
|
|
23543
|
+
connector,
|
|
23544
|
+
{
|
|
23545
|
+
projectDir: input.projectDir,
|
|
23546
|
+
project: input.project,
|
|
23547
|
+
credentials,
|
|
23548
|
+
...input.errorsPath ? { errorsPath: input.errorsPath } : {}
|
|
23549
|
+
},
|
|
23550
|
+
input.graph,
|
|
23551
|
+
resolveTarget,
|
|
23552
|
+
{ connectorId: `k8s:${entry2.id}`, ...entry2.intervalMs ? { intervalMs: entry2.intervalMs } : {} }
|
|
23553
|
+
);
|
|
23554
|
+
stops.push(stop);
|
|
23555
|
+
}
|
|
23556
|
+
return () => {
|
|
23557
|
+
for (const stop of stops) stop();
|
|
23558
|
+
};
|
|
23559
|
+
}
|
|
23560
|
+
|
|
23561
|
+
// src/connectors/kubernetes/index.ts
|
|
23562
|
+
var KubernetesConnector = class {
|
|
23563
|
+
constructor(config, fetchImpl) {
|
|
23564
|
+
this.config = config;
|
|
23565
|
+
this.fetchImpl = fetchImpl;
|
|
23566
|
+
}
|
|
23567
|
+
config;
|
|
23568
|
+
fetchImpl;
|
|
23569
|
+
provider = "kubernetes";
|
|
23570
|
+
async poll(ctx) {
|
|
23571
|
+
const creds = readK8sCredentials(ctx.credentials);
|
|
23572
|
+
const transport = resolveK8sTransport(creds, this.config);
|
|
23573
|
+
const namespace = this.config.namespace;
|
|
23574
|
+
const opts = {
|
|
23575
|
+
...this.config.apiUrl ? { apiUrl: this.config.apiUrl } : {},
|
|
23576
|
+
...this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}
|
|
23577
|
+
};
|
|
23578
|
+
const [deployments, pods] = await Promise.all([
|
|
23579
|
+
fetchDeployments(transport, namespace, opts),
|
|
23580
|
+
fetchPods(transport, namespace, opts)
|
|
23581
|
+
]);
|
|
23582
|
+
return mapWorkloadsToSignals(deployments, pods, this.config);
|
|
23583
|
+
}
|
|
23584
|
+
};
|
|
23585
|
+
function createKubernetesConnector(graph, config, fetchImpl) {
|
|
23586
|
+
return {
|
|
23587
|
+
connector: new KubernetesConnector(config, fetchImpl),
|
|
23588
|
+
resolveTarget: createK8sResolveTarget(graph)
|
|
23589
|
+
};
|
|
23590
|
+
}
|
|
23591
|
+
|
|
23592
|
+
// src/daemon.ts
|
|
23024
23593
|
init_auth();
|
|
23025
23594
|
|
|
23026
23595
|
// src/unrouted.ts
|
|
23027
23596
|
init_cjs_shims();
|
|
23028
|
-
var
|
|
23029
|
-
var
|
|
23597
|
+
var import_node_fs40 = require("fs");
|
|
23598
|
+
var import_node_path76 = __toESM(require("path"), 1);
|
|
23030
23599
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
23031
23600
|
return {
|
|
23032
23601
|
timestamp: now.toISOString(),
|
|
@@ -23036,34 +23605,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
23036
23605
|
};
|
|
23037
23606
|
}
|
|
23038
23607
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
23039
|
-
const target =
|
|
23040
|
-
await
|
|
23041
|
-
await
|
|
23608
|
+
const target = import_node_path76.default.join(neatHome4, "errors.ndjson");
|
|
23609
|
+
await import_node_fs40.promises.mkdir(neatHome4, { recursive: true });
|
|
23610
|
+
await import_node_fs40.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
23042
23611
|
}
|
|
23043
23612
|
function unroutedErrorsPath(neatHome4) {
|
|
23044
|
-
return
|
|
23613
|
+
return import_node_path76.default.join(neatHome4, "errors.ndjson");
|
|
23045
23614
|
}
|
|
23046
23615
|
|
|
23047
23616
|
// src/daemon.ts
|
|
23048
|
-
var
|
|
23617
|
+
var import_types105 = require("@neat.is/types");
|
|
23049
23618
|
function daemonJsonPath(scanPath) {
|
|
23050
|
-
return
|
|
23619
|
+
return import_node_path77.default.join(scanPath, "neat-out", "daemon.json");
|
|
23051
23620
|
}
|
|
23052
23621
|
function daemonsDiscoveryDir(home) {
|
|
23053
23622
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
23054
|
-
return
|
|
23623
|
+
return import_node_path77.default.join(base, "daemons");
|
|
23055
23624
|
}
|
|
23056
23625
|
function daemonDiscoveryPath(project, home) {
|
|
23057
|
-
return
|
|
23626
|
+
return import_node_path77.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
23058
23627
|
}
|
|
23059
23628
|
function sanitizeDiscoveryName(project) {
|
|
23060
23629
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
23061
23630
|
}
|
|
23062
23631
|
function neatHomeFromEnv() {
|
|
23063
23632
|
const env = process.env.NEAT_HOME;
|
|
23064
|
-
if (env && env.length > 0) return
|
|
23633
|
+
if (env && env.length > 0) return import_node_path77.default.resolve(env);
|
|
23065
23634
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
23066
|
-
return
|
|
23635
|
+
return import_node_path77.default.join(home, ".neat");
|
|
23067
23636
|
}
|
|
23068
23637
|
function resolveNeatVersion() {
|
|
23069
23638
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -23095,7 +23664,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
23095
23664
|
} catch {
|
|
23096
23665
|
}
|
|
23097
23666
|
try {
|
|
23098
|
-
await
|
|
23667
|
+
await import_node_fs41.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
23099
23668
|
} catch {
|
|
23100
23669
|
}
|
|
23101
23670
|
}
|
|
@@ -23104,12 +23673,12 @@ function reconcileDaemonRecordSync(record, home) {
|
|
|
23104
23673
|
const stopped = { ...record, status: "stopped" };
|
|
23105
23674
|
const target = daemonJsonPath(record.projectPath);
|
|
23106
23675
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
23107
|
-
(0,
|
|
23108
|
-
(0,
|
|
23676
|
+
(0, import_node_fs41.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
|
|
23677
|
+
(0, import_node_fs41.renameSync)(tmp, target);
|
|
23109
23678
|
} catch {
|
|
23110
23679
|
}
|
|
23111
23680
|
try {
|
|
23112
|
-
(0,
|
|
23681
|
+
(0, import_node_fs41.unlinkSync)(daemonDiscoveryPath(record.project, home));
|
|
23113
23682
|
} catch {
|
|
23114
23683
|
}
|
|
23115
23684
|
}
|
|
@@ -23126,17 +23695,21 @@ function teardownSlot(slot) {
|
|
|
23126
23695
|
slot.stopConnectors();
|
|
23127
23696
|
} catch {
|
|
23128
23697
|
}
|
|
23698
|
+
try {
|
|
23699
|
+
slot.stopK8sSubstrate();
|
|
23700
|
+
} catch {
|
|
23701
|
+
}
|
|
23129
23702
|
try {
|
|
23130
23703
|
slot.detachEvents();
|
|
23131
23704
|
} catch {
|
|
23132
23705
|
}
|
|
23133
23706
|
}
|
|
23134
23707
|
function neatHomeFor(opts) {
|
|
23135
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
23708
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path77.default.resolve(opts.neatHome);
|
|
23136
23709
|
const env = process.env.NEAT_HOME;
|
|
23137
|
-
if (env && env.length > 0) return
|
|
23710
|
+
if (env && env.length > 0) return import_node_path77.default.resolve(env);
|
|
23138
23711
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
23139
|
-
return
|
|
23712
|
+
return import_node_path77.default.join(home, ".neat");
|
|
23140
23713
|
}
|
|
23141
23714
|
function routeSpanToProject(serviceName, projects) {
|
|
23142
23715
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -23184,13 +23757,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
23184
23757
|
if (!serviceName) return true;
|
|
23185
23758
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
23186
23759
|
return graph.someNode(
|
|
23187
|
-
(_id, attrs) => attrs.type ===
|
|
23760
|
+
(_id, attrs) => attrs.type === import_types105.NodeType.ServiceNode && attrs.name === serviceName
|
|
23188
23761
|
);
|
|
23189
23762
|
}
|
|
23190
23763
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
23191
|
-
const paths = pathsForProject(entry2.name,
|
|
23764
|
+
const paths = pathsForProject(entry2.name, import_node_path77.default.join(entry2.path, "neat-out"));
|
|
23192
23765
|
try {
|
|
23193
|
-
const stat = await
|
|
23766
|
+
const stat = await import_node_fs41.promises.stat(entry2.path);
|
|
23194
23767
|
if (!stat.isDirectory()) {
|
|
23195
23768
|
throw new Error(`registered path ${entry2.path} is not a directory`);
|
|
23196
23769
|
}
|
|
@@ -23210,6 +23783,8 @@ async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
|
23210
23783
|
},
|
|
23211
23784
|
stopConnectors: () => {
|
|
23212
23785
|
},
|
|
23786
|
+
stopK8sSubstrate: () => {
|
|
23787
|
+
},
|
|
23213
23788
|
detachEvents: () => {
|
|
23214
23789
|
},
|
|
23215
23790
|
status: "broken",
|
|
@@ -23242,6 +23817,14 @@ async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
|
23242
23817
|
`neatd: connector "${skipped.id}" (${skipped.provider}) skipped for project "${entry2.name}" \u2014 ${reason}`
|
|
23243
23818
|
)
|
|
23244
23819
|
});
|
|
23820
|
+
const stopK8sSubstrate = await startK8sSubstratePolling({
|
|
23821
|
+
project: entry2.name,
|
|
23822
|
+
graph,
|
|
23823
|
+
projectDir: entry2.path,
|
|
23824
|
+
errorsPath: paths.errorsPath,
|
|
23825
|
+
...neatHome4 ? { home: neatHome4 } : {},
|
|
23826
|
+
onSkip: (skipped, reason) => console.warn(`neatd: k8s substrate "${skipped.id}" skipped for project "${entry2.name}" \u2014 ${reason}`)
|
|
23827
|
+
});
|
|
23245
23828
|
await touchLastSeen(entry2.name).catch(() => {
|
|
23246
23829
|
});
|
|
23247
23830
|
return {
|
|
@@ -23252,6 +23835,7 @@ async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
|
23252
23835
|
stopPersist,
|
|
23253
23836
|
stopStaleness,
|
|
23254
23837
|
stopConnectors,
|
|
23838
|
+
stopK8sSubstrate,
|
|
23255
23839
|
detachEvents,
|
|
23256
23840
|
status: "active"
|
|
23257
23841
|
};
|
|
@@ -23308,7 +23892,7 @@ async function startDaemon(opts = {}) {
|
|
|
23308
23892
|
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;
|
|
23309
23893
|
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;
|
|
23310
23894
|
const singleProject = projectArg;
|
|
23311
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
23895
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path77.default.resolve(projectPathArg) : null;
|
|
23312
23896
|
if (singleProject && !singleProjectPath) {
|
|
23313
23897
|
throw new Error(
|
|
23314
23898
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -23316,14 +23900,14 @@ async function startDaemon(opts = {}) {
|
|
|
23316
23900
|
}
|
|
23317
23901
|
if (!singleProject) {
|
|
23318
23902
|
try {
|
|
23319
|
-
await
|
|
23903
|
+
await import_node_fs41.promises.access(regPath);
|
|
23320
23904
|
} catch {
|
|
23321
23905
|
throw new Error(
|
|
23322
23906
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
23323
23907
|
);
|
|
23324
23908
|
}
|
|
23325
23909
|
}
|
|
23326
|
-
const pidPath =
|
|
23910
|
+
const pidPath = import_node_path77.default.join(home, "neatd.pid");
|
|
23327
23911
|
await writeAtomically(pidPath, `${process.pid}
|
|
23328
23912
|
`);
|
|
23329
23913
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -23535,7 +24119,7 @@ async function startDaemon(opts = {}) {
|
|
|
23535
24119
|
}
|
|
23536
24120
|
if (restApp) await restApp.close().catch(() => {
|
|
23537
24121
|
});
|
|
23538
|
-
await
|
|
24122
|
+
await import_node_fs41.promises.unlink(pidPath).catch(() => {
|
|
23539
24123
|
});
|
|
23540
24124
|
throw new Error(
|
|
23541
24125
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -23713,7 +24297,7 @@ async function startDaemon(opts = {}) {
|
|
|
23713
24297
|
});
|
|
23714
24298
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
23715
24299
|
});
|
|
23716
|
-
await
|
|
24300
|
+
await import_node_fs41.promises.unlink(pidPath).catch(() => {
|
|
23717
24301
|
});
|
|
23718
24302
|
throw new Error(
|
|
23719
24303
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -23748,7 +24332,7 @@ async function startDaemon(opts = {}) {
|
|
|
23748
24332
|
});
|
|
23749
24333
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
23750
24334
|
});
|
|
23751
|
-
await
|
|
24335
|
+
await import_node_fs41.promises.unlink(pidPath).catch(() => {
|
|
23752
24336
|
});
|
|
23753
24337
|
throw new Error(
|
|
23754
24338
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -23795,9 +24379,9 @@ async function startDaemon(opts = {}) {
|
|
|
23795
24379
|
let registryWatcher = null;
|
|
23796
24380
|
let reloadTimer = null;
|
|
23797
24381
|
if (!singleProject) try {
|
|
23798
|
-
const regDir =
|
|
23799
|
-
const regBase =
|
|
23800
|
-
registryWatcher = (0,
|
|
24382
|
+
const regDir = import_node_path77.default.dirname(regPath);
|
|
24383
|
+
const regBase = import_node_path77.default.basename(regPath);
|
|
24384
|
+
registryWatcher = (0, import_node_fs41.watch)(regDir, (_eventType, filename) => {
|
|
23801
24385
|
if (filename !== null && filename !== regBase) return;
|
|
23802
24386
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
23803
24387
|
reloadTimer = setTimeout(() => {
|
|
@@ -23846,7 +24430,7 @@ async function startDaemon(opts = {}) {
|
|
|
23846
24430
|
if (daemonRecord) {
|
|
23847
24431
|
await clearDaemonRecord(daemonRecord, home);
|
|
23848
24432
|
}
|
|
23849
|
-
await
|
|
24433
|
+
await import_node_fs41.promises.unlink(pidPath).catch(() => {
|
|
23850
24434
|
});
|
|
23851
24435
|
};
|
|
23852
24436
|
return {
|
|
@@ -23869,9 +24453,9 @@ init_auth();
|
|
|
23869
24453
|
// src/web-spawn.ts
|
|
23870
24454
|
init_cjs_shims();
|
|
23871
24455
|
var import_node_child_process2 = require("child_process");
|
|
23872
|
-
var
|
|
24456
|
+
var import_node_fs42 = require("fs");
|
|
23873
24457
|
var import_node_net = __toESM(require("net"), 1);
|
|
23874
|
-
var
|
|
24458
|
+
var import_node_path78 = __toESM(require("path"), 1);
|
|
23875
24459
|
var DEFAULT_WEB_PORT = 6328;
|
|
23876
24460
|
var DEFAULT_REST_PORT = 8080;
|
|
23877
24461
|
function asValidPort(value) {
|
|
@@ -23880,11 +24464,11 @@ function asValidPort(value) {
|
|
|
23880
24464
|
}
|
|
23881
24465
|
function projectRoot() {
|
|
23882
24466
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
23883
|
-
return
|
|
24467
|
+
return import_node_path78.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
23884
24468
|
}
|
|
23885
24469
|
async function readDaemonPorts(root) {
|
|
23886
24470
|
try {
|
|
23887
|
-
const raw = await
|
|
24471
|
+
const raw = await import_node_fs42.promises.readFile(import_node_path78.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
23888
24472
|
const parsed = JSON.parse(raw);
|
|
23889
24473
|
const ports = parsed?.ports ?? {};
|
|
23890
24474
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -23929,10 +24513,10 @@ function resolveWebPackageDir() {
|
|
|
23929
24513
|
eval("require")
|
|
23930
24514
|
);
|
|
23931
24515
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
23932
|
-
return
|
|
24516
|
+
return import_node_path78.default.dirname(pkgJsonPath);
|
|
23933
24517
|
}
|
|
23934
24518
|
function resolveStandaloneServerEntry(webDir) {
|
|
23935
|
-
return
|
|
24519
|
+
return import_node_path78.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
23936
24520
|
}
|
|
23937
24521
|
async function pickInternalPort() {
|
|
23938
24522
|
return new Promise((resolve, reject) => {
|
|
@@ -23985,7 +24569,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
23985
24569
|
NEAT_API_URL: apiUrl
|
|
23986
24570
|
};
|
|
23987
24571
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
23988
|
-
cwd:
|
|
24572
|
+
cwd: import_node_path78.default.dirname(serverEntry),
|
|
23989
24573
|
env,
|
|
23990
24574
|
stdio: ["ignore", "inherit", "inherit"],
|
|
23991
24575
|
detached: false
|
|
@@ -24084,13 +24668,13 @@ async function waitForListening(port, timeoutMs = 15e3) {
|
|
|
24084
24668
|
|
|
24085
24669
|
// src/version-skew.ts
|
|
24086
24670
|
init_cjs_shims();
|
|
24087
|
-
var
|
|
24671
|
+
var import_promises2 = require("timers/promises");
|
|
24088
24672
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/neat.is/latest";
|
|
24089
24673
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
24090
24674
|
async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
|
|
24091
24675
|
const controller = new AbortController();
|
|
24092
24676
|
let timedOut = false;
|
|
24093
|
-
const timer = (0,
|
|
24677
|
+
const timer = (0, import_promises2.setTimeout)(timeoutMs).then(() => {
|
|
24094
24678
|
timedOut = true;
|
|
24095
24679
|
controller.abort();
|
|
24096
24680
|
});
|
|
@@ -24161,14 +24745,14 @@ function localVersion() {
|
|
|
24161
24745
|
}
|
|
24162
24746
|
function neatHome3() {
|
|
24163
24747
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
24164
|
-
return
|
|
24748
|
+
return import_node_path79.default.resolve(process.env.NEAT_HOME);
|
|
24165
24749
|
}
|
|
24166
24750
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
24167
|
-
return
|
|
24751
|
+
return import_node_path79.default.join(home, ".neat");
|
|
24168
24752
|
}
|
|
24169
24753
|
async function readPid() {
|
|
24170
24754
|
try {
|
|
24171
|
-
const raw = await
|
|
24755
|
+
const raw = await import_node_fs43.promises.readFile(import_node_path79.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
24172
24756
|
const n = Number.parseInt(raw.trim(), 10);
|
|
24173
24757
|
return Number.isFinite(n) ? n : null;
|
|
24174
24758
|
} catch {
|