@neat.is/core 0.9.1-dev.20260819 → 0.9.2-dev.20260821
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FCO5Z3RW.js → chunk-ERE47MCR.js} +2 -2
- package/dist/{chunk-UUYCTH2E.js → chunk-GDGUY4T6.js} +34 -2
- package/dist/chunk-GDGUY4T6.js.map +1 -0
- package/dist/{chunk-ICO37D3H.js → chunk-L4SZIIER.js} +3 -3
- package/dist/{chunk-JUVJBH23.js → chunk-RQQUI3NQ.js} +1073 -378
- package/dist/chunk-RQQUI3NQ.js.map +1 -0
- package/dist/cli.cjs +1640 -911
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +1303 -574
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +1319 -590
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-CO47JRIN.js → otel-grpc-Y5K4WGWO.js} +3 -3
- package/dist/server.cjs +1206 -477
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-JUVJBH23.js.map +0 -1
- package/dist/chunk-UUYCTH2E.js.map +0 -1
- /package/dist/{chunk-FCO5Z3RW.js.map → chunk-ERE47MCR.js.map} +0 -0
- /package/dist/{chunk-ICO37D3H.js.map → chunk-L4SZIIER.js.map} +0 -0
- /package/dist/{otel-grpc-CO47JRIN.js.map → otel-grpc-Y5K4WGWO.js.map} +0 -0
package/dist/neatd.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path78 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path78) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path78)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path52.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url,
|
|
266
|
+
var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path52 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
415
415
|
const v = attrs[key];
|
|
416
416
|
if (typeof v === "string" && v.length > 0) {
|
|
417
417
|
const q = v.indexOf("?");
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
418
|
+
const path78 = q === -1 ? v : v.slice(0, q);
|
|
419
|
+
if (path78.length > 0) return path78;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
return void 0;
|
|
@@ -478,10 +478,10 @@ function parseOtlpRequest(body) {
|
|
|
478
478
|
return out;
|
|
479
479
|
}
|
|
480
480
|
function loadProtoRoot() {
|
|
481
|
-
const here =
|
|
482
|
-
const protoRoot =
|
|
481
|
+
const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
482
|
+
const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
|
|
483
483
|
const root = new import_protobufjs.default.Root();
|
|
484
|
-
root.resolvePath = (_origin, target) =>
|
|
484
|
+
root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
|
|
485
485
|
root.loadSync(
|
|
486
486
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
487
487
|
{ keepCase: true }
|
|
@@ -526,11 +526,42 @@ async function decodeProtobufBody(buf) {
|
|
|
526
526
|
const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
|
|
527
527
|
return reshapeGrpcRequest2(decoded);
|
|
528
528
|
}
|
|
529
|
+
function decompressorForEncoding(encoding) {
|
|
530
|
+
switch (encoding) {
|
|
531
|
+
case "gzip":
|
|
532
|
+
case "x-gzip":
|
|
533
|
+
return import_node_zlib.default.createGunzip();
|
|
534
|
+
case "deflate":
|
|
535
|
+
return import_node_zlib.default.createInflate();
|
|
536
|
+
default:
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
529
540
|
async function buildOtelReceiver(opts) {
|
|
530
541
|
const app = (0, import_fastify.default)({
|
|
531
542
|
logger: false,
|
|
532
543
|
bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
|
|
533
544
|
});
|
|
545
|
+
app.addHook("preParsing", (req2, _reply, payload, done) => {
|
|
546
|
+
const encoding = (req2.headers["content-encoding"] ?? "").toString().trim().toLowerCase();
|
|
547
|
+
if (encoding === "" || encoding === "identity") {
|
|
548
|
+
done(null, payload);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const decompressor = decompressorForEncoding(encoding);
|
|
552
|
+
if (!decompressor) {
|
|
553
|
+
done(null, payload);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const tracked = decompressor;
|
|
557
|
+
tracked.receivedEncodedLength = 0;
|
|
558
|
+
payload.on("data", (chunk) => {
|
|
559
|
+
tracked.receivedEncodedLength = (tracked.receivedEncodedLength ?? 0) + chunk.length;
|
|
560
|
+
});
|
|
561
|
+
payload.on("error", (err) => decompressor.destroy(err));
|
|
562
|
+
payload.pipe(decompressor);
|
|
563
|
+
done(null, decompressor);
|
|
564
|
+
});
|
|
534
565
|
const REJECT_WARN_INTERVAL_MS = 6e4;
|
|
535
566
|
let lastRejectWarnAt = 0;
|
|
536
567
|
const warnRejectedOtlp = () => {
|
|
@@ -728,13 +759,14 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
728
759
|
}
|
|
729
760
|
}
|
|
730
761
|
}
|
|
731
|
-
var
|
|
762
|
+
var import_node_path53, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
732
763
|
var init_otel = __esm({
|
|
733
764
|
"src/otel.ts"() {
|
|
734
765
|
"use strict";
|
|
735
766
|
init_cjs_shims();
|
|
736
|
-
|
|
767
|
+
import_node_path53 = __toESM(require("path"), 1);
|
|
737
768
|
import_node_url2 = require("url");
|
|
769
|
+
import_node_zlib = __toESM(require("zlib"), 1);
|
|
738
770
|
import_fastify = __toESM(require("fastify"), 1);
|
|
739
771
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
740
772
|
init_auth();
|
|
@@ -756,14 +788,14 @@ __export(neatd_exports, {
|
|
|
756
788
|
});
|
|
757
789
|
module.exports = __toCommonJS(neatd_exports);
|
|
758
790
|
init_cjs_shims();
|
|
759
|
-
var
|
|
760
|
-
var
|
|
791
|
+
var import_node_fs42 = require("fs");
|
|
792
|
+
var import_node_path77 = __toESM(require("path"), 1);
|
|
761
793
|
var import_node_module2 = require("module");
|
|
762
794
|
|
|
763
795
|
// src/daemon.ts
|
|
764
796
|
init_cjs_shims();
|
|
765
|
-
var
|
|
766
|
-
var
|
|
797
|
+
var import_node_fs40 = require("fs");
|
|
798
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
767
799
|
var import_node_module = require("module");
|
|
768
800
|
|
|
769
801
|
// src/graph.ts
|
|
@@ -1296,19 +1328,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1296
1328
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1297
1329
|
let best = { path: [start], edges: [] };
|
|
1298
1330
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1299
|
-
function step(node,
|
|
1300
|
-
if (
|
|
1301
|
-
best = { path: [...
|
|
1331
|
+
function step(node, path78, edges) {
|
|
1332
|
+
if (path78.length > best.path.length) {
|
|
1333
|
+
best = { path: [...path78], edges: [...edges] };
|
|
1302
1334
|
}
|
|
1303
|
-
if (
|
|
1335
|
+
if (path78.length - 1 >= maxDepth) return;
|
|
1304
1336
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1305
1337
|
for (const [srcId, edge] of incoming) {
|
|
1306
1338
|
if (visited.has(srcId)) continue;
|
|
1307
1339
|
visited.add(srcId);
|
|
1308
|
-
|
|
1340
|
+
path78.push(srcId);
|
|
1309
1341
|
edges.push(edge);
|
|
1310
|
-
step(srcId,
|
|
1311
|
-
|
|
1342
|
+
step(srcId, path78, edges);
|
|
1343
|
+
path78.pop();
|
|
1312
1344
|
edges.pop();
|
|
1313
1345
|
visited.delete(srcId);
|
|
1314
1346
|
}
|
|
@@ -1316,11 +1348,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1316
1348
|
step(start, [start], []);
|
|
1317
1349
|
return best;
|
|
1318
1350
|
}
|
|
1319
|
-
function databaseRootCauseShape(graph, origin,
|
|
1351
|
+
function databaseRootCauseShape(graph, origin, walk10) {
|
|
1320
1352
|
const targetDb = origin;
|
|
1321
1353
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1322
1354
|
if (candidatePairs.length === 0) return null;
|
|
1323
|
-
for (const id of
|
|
1355
|
+
for (const id of walk10.path) {
|
|
1324
1356
|
const owner = resolveOwningService(graph, id);
|
|
1325
1357
|
if (!owner) continue;
|
|
1326
1358
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1347,8 +1379,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
|
|
|
1347
1379
|
}
|
|
1348
1380
|
return null;
|
|
1349
1381
|
}
|
|
1350
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1351
|
-
for (const id of
|
|
1382
|
+
function serviceRootCauseShape(graph, _origin, walk10) {
|
|
1383
|
+
for (const id of walk10.path) {
|
|
1352
1384
|
const owner = resolveOwningService(graph, id);
|
|
1353
1385
|
if (!owner) continue;
|
|
1354
1386
|
const { id: serviceId15, svc } = owner;
|
|
@@ -1384,15 +1416,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
1384
1416
|
}
|
|
1385
1417
|
return null;
|
|
1386
1418
|
}
|
|
1387
|
-
function fileRootCauseShape(graph, origin,
|
|
1419
|
+
function fileRootCauseShape(graph, origin, walk10) {
|
|
1388
1420
|
const owner = resolveOwningService(graph, origin.id);
|
|
1389
1421
|
if (!owner) return null;
|
|
1390
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1422
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1391
1423
|
}
|
|
1392
|
-
function symbolRootCauseShape(graph, origin,
|
|
1424
|
+
function symbolRootCauseShape(graph, origin, walk10) {
|
|
1393
1425
|
const owner = resolveOwningService(graph, origin.id);
|
|
1394
1426
|
if (!owner) return null;
|
|
1395
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1427
|
+
return serviceRootCauseShape(graph, owner.svc, walk10);
|
|
1396
1428
|
}
|
|
1397
1429
|
var rootCauseShapes = {
|
|
1398
1430
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1405,25 +1437,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1405
1437
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1406
1438
|
const shape = rootCauseShapes[origin.type];
|
|
1407
1439
|
if (shape) {
|
|
1408
|
-
const
|
|
1409
|
-
const match = shape(graph, origin,
|
|
1440
|
+
const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1441
|
+
const match = shape(graph, origin, walk10);
|
|
1410
1442
|
if (match) {
|
|
1411
1443
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1412
|
-
return
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1444
|
+
return {
|
|
1445
|
+
source: "compat",
|
|
1446
|
+
result: import_types.RootCauseResultSchema.parse({
|
|
1447
|
+
rootCauseNode: match.rootCauseNode,
|
|
1448
|
+
rootCauseReason: reason,
|
|
1449
|
+
traversalPath: walk10.path,
|
|
1450
|
+
edgeProvenances: walk10.edges.map((e) => e.provenance),
|
|
1451
|
+
confidence: confidenceFromMix(walk10.edges),
|
|
1452
|
+
fixRecommendation: match.fixRecommendation
|
|
1453
|
+
})
|
|
1454
|
+
};
|
|
1420
1455
|
}
|
|
1421
1456
|
}
|
|
1422
1457
|
if (origin.type === import_types.NodeType.ServiceNode) {
|
|
1423
1458
|
const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
|
|
1424
|
-
if (crossService) return crossService;
|
|
1459
|
+
if (crossService) return { result: crossService, source: "cross-service" };
|
|
1425
1460
|
}
|
|
1426
|
-
|
|
1461
|
+
const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
|
|
1462
|
+
return incident ? { result: incident, source: "incident" } : null;
|
|
1427
1463
|
}
|
|
1428
1464
|
var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
|
|
1429
1465
|
function incidentMatchesNode(ev, nodeId) {
|
|
@@ -1519,26 +1555,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
|
|
|
1519
1555
|
return best;
|
|
1520
1556
|
}
|
|
1521
1557
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1522
|
-
const
|
|
1558
|
+
const path78 = [originServiceId];
|
|
1523
1559
|
const edges = [];
|
|
1524
1560
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1525
1561
|
let current = originServiceId;
|
|
1526
1562
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1527
1563
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1528
1564
|
if (!hop) break;
|
|
1529
|
-
|
|
1565
|
+
path78.push(hop.nextService);
|
|
1530
1566
|
edges.push(hop.edge);
|
|
1531
1567
|
visited.add(hop.nextService);
|
|
1532
1568
|
current = hop.nextService;
|
|
1533
1569
|
}
|
|
1534
1570
|
if (edges.length === 0) return null;
|
|
1535
|
-
return { path:
|
|
1571
|
+
return { path: path78, edges, culprit: current };
|
|
1572
|
+
}
|
|
1573
|
+
function isStaleCallEdge(e) {
|
|
1574
|
+
return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
|
|
1575
|
+
}
|
|
1576
|
+
function staleCallDominates(e, id, curEdge, curId) {
|
|
1577
|
+
const ev = e.signal?.spanCount ?? e.callCount ?? 0;
|
|
1578
|
+
const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
|
|
1579
|
+
if (ev !== cv) return ev > cv;
|
|
1580
|
+
return id < curId;
|
|
1581
|
+
}
|
|
1582
|
+
function dominantStaleCall(graph, serviceId15, visited) {
|
|
1583
|
+
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1584
|
+
for (const src of callSourcesForService(graph, serviceId15)) {
|
|
1585
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
1586
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
1587
|
+
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
1588
|
+
if (isFrontierNode(graph, e.target)) continue;
|
|
1589
|
+
const owner = resolveOwningService(graph, e.target);
|
|
1590
|
+
if (!owner || visited.has(owner.id)) continue;
|
|
1591
|
+
const cur = bestByCallee.get(owner.id);
|
|
1592
|
+
if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
|
|
1593
|
+
bestByCallee.set(owner.id, e);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
let best = null;
|
|
1598
|
+
for (const [id, edge] of bestByCallee) {
|
|
1599
|
+
if (!isStaleCallEdge(edge)) continue;
|
|
1600
|
+
if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
|
|
1601
|
+
best = { nextService: id, edge };
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return best;
|
|
1605
|
+
}
|
|
1606
|
+
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
1607
|
+
const path78 = [originServiceId];
|
|
1608
|
+
const edges = [];
|
|
1609
|
+
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1610
|
+
let current = originServiceId;
|
|
1611
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1612
|
+
const hop = dominantStaleCall(graph, current, visited);
|
|
1613
|
+
if (!hop) break;
|
|
1614
|
+
path78.push(hop.nextService);
|
|
1615
|
+
edges.push(hop.edge);
|
|
1616
|
+
visited.add(hop.nextService);
|
|
1617
|
+
current = hop.nextService;
|
|
1618
|
+
}
|
|
1619
|
+
if (edges.length === 0) return null;
|
|
1620
|
+
return { path: path78, edges, culprit: current };
|
|
1536
1621
|
}
|
|
1537
1622
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1538
1623
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1539
1624
|
if (!chain) return null;
|
|
1540
1625
|
const culprit = chain.culprit;
|
|
1541
|
-
const
|
|
1626
|
+
const path78 = [...chain.path];
|
|
1542
1627
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1543
1628
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1544
1629
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1546,14 +1631,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1546
1631
|
if (loc) {
|
|
1547
1632
|
let rootCauseNode = culprit;
|
|
1548
1633
|
if (loc.fileNode) {
|
|
1549
|
-
|
|
1634
|
+
path78.push(loc.fileNode);
|
|
1550
1635
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1551
1636
|
rootCauseNode = loc.fileNode;
|
|
1552
1637
|
}
|
|
1553
1638
|
return import_types.RootCauseResultSchema.parse({
|
|
1554
1639
|
rootCauseNode,
|
|
1555
1640
|
rootCauseReason: loc.rootCauseReason,
|
|
1556
|
-
traversalPath:
|
|
1641
|
+
traversalPath: path78,
|
|
1557
1642
|
edgeProvenances,
|
|
1558
1643
|
confidence,
|
|
1559
1644
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1565,7 +1650,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1565
1650
|
return import_types.RootCauseResultSchema.parse({
|
|
1566
1651
|
rootCauseNode: culprit,
|
|
1567
1652
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1568
|
-
traversalPath:
|
|
1653
|
+
traversalPath: path78,
|
|
1569
1654
|
edgeProvenances,
|
|
1570
1655
|
confidence,
|
|
1571
1656
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -1952,17 +2037,20 @@ function displayNameOf(nodeId) {
|
|
|
1952
2037
|
return nodeId.replace(/^[a-z]+:/, "");
|
|
1953
2038
|
}
|
|
1954
2039
|
function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
|
|
1955
|
-
const
|
|
1956
|
-
if (!
|
|
2040
|
+
const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
|
|
2041
|
+
if (!tagged) return null;
|
|
1957
2042
|
const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
|
|
1958
|
-
if (!navigation) return
|
|
1959
|
-
return enrichWithNavigation(graph, errorNodeId,
|
|
2043
|
+
if (!navigation) return tagged.result;
|
|
2044
|
+
return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
|
|
1960
2045
|
}
|
|
1961
|
-
function enrichWithNavigation(graph, errorNodeId,
|
|
2046
|
+
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
2047
|
+
const legacy = tagged.result;
|
|
1962
2048
|
const seedNode = legacy.rootCauseNode;
|
|
1963
2049
|
const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
|
|
1964
2050
|
const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
|
|
1965
2051
|
const candidates = [];
|
|
2052
|
+
const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
|
|
2053
|
+
const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
|
|
1966
2054
|
if (seedCtx && isVictimSeed(seedCtx)) {
|
|
1967
2055
|
const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
|
|
1968
2056
|
const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
|
|
@@ -1987,6 +2075,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
1987
2075
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
1988
2076
|
...lastProv ? { provenance: lastProv } : {}
|
|
1989
2077
|
});
|
|
2078
|
+
} else if (staleChain) {
|
|
2079
|
+
const culprit = staleChain.culprit;
|
|
2080
|
+
const culpritName = displayNameOf(culprit);
|
|
2081
|
+
const seedName = displayNameOf(seedNode);
|
|
2082
|
+
const staleConfidence = confidenceFromMix(staleChain.edges, now);
|
|
2083
|
+
candidates.push({
|
|
2084
|
+
node: culprit,
|
|
2085
|
+
classification: "primary-failure",
|
|
2086
|
+
reason: `${culpritName} is the stale-derived root cause (low confidence): live telemetry for this subgraph has gone quiet, but the last-observed topology traces the failure surfacing at ${seedName} downstream through a STALE call chain to ${culpritName}. Provenance is STALE, so confidence is capped low \u2014 restore instrumentation and re-run to confirm before acting.`,
|
|
2087
|
+
context: nodeContext(graph, culprit, incidents, now),
|
|
2088
|
+
confidence: staleConfidence,
|
|
2089
|
+
provenance: import_types.Provenance.STALE
|
|
2090
|
+
});
|
|
2091
|
+
candidates.push({
|
|
2092
|
+
node: seedNode,
|
|
2093
|
+
classification: "symptom-only",
|
|
2094
|
+
reason: `The failure surfaced here, but the only causal chain the graph still holds is STALE and runs downstream \u2014 ${seedName} is the surface of a stale-traced failure, not a proven origin.`,
|
|
2095
|
+
context: seedCtx ?? EMPTY_CONTEXT,
|
|
2096
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
|
|
2097
|
+
...lastProv ? { provenance: lastProv } : {}
|
|
2098
|
+
});
|
|
1990
2099
|
} else {
|
|
1991
2100
|
candidates.push({
|
|
1992
2101
|
node: seedNode,
|
|
@@ -2000,11 +2109,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
2000
2109
|
const top = candidates[0];
|
|
2001
2110
|
let traversalPath = legacy.traversalPath;
|
|
2002
2111
|
let edgeProvenances = legacy.edgeProvenances;
|
|
2003
|
-
if (top.node
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2112
|
+
if (staleChain && top.node === staleChain.culprit) {
|
|
2113
|
+
traversalPath = staleChain.path;
|
|
2114
|
+
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
2115
|
+
} else if (top.node !== seedNode) {
|
|
2116
|
+
const path78 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
2117
|
+
if (path78) {
|
|
2118
|
+
traversalPath = path78.nodes;
|
|
2119
|
+
edgeProvenances = path78.edges.map((e) => e.provenance);
|
|
2008
2120
|
} else {
|
|
2009
2121
|
traversalPath = [errorNodeId, top.node];
|
|
2010
2122
|
edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
|
|
@@ -2026,6 +2138,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
|
|
|
2026
2138
|
return legacy.fixRecommendation;
|
|
2027
2139
|
}
|
|
2028
2140
|
const name = top.node.replace(/^service:/, "");
|
|
2141
|
+
if (top.provenance === import_types.Provenance.STALE) {
|
|
2142
|
+
return `Live telemetry for this path has gone quiet; the last-observed topology traces the failure downstream to ${name}. Restore instrumentation (or re-run with live traces) to confirm, then inspect ${name}.`;
|
|
2143
|
+
}
|
|
2029
2144
|
if (top.classification === "primary-failure") {
|
|
2030
2145
|
return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
|
|
2031
2146
|
}
|
|
@@ -2544,6 +2659,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
|
2544
2659
|
var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
|
|
2545
2660
|
var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
|
|
2546
2661
|
var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
|
|
2662
|
+
var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
|
|
2547
2663
|
var import_types6 = require("@neat.is/types");
|
|
2548
2664
|
|
|
2549
2665
|
// src/extract/shared.ts
|
|
@@ -2824,7 +2940,7 @@ function buildServiceHostIndex(services) {
|
|
|
2824
2940
|
async function walkSourceFiles(dir, excludeDirs = []) {
|
|
2825
2941
|
const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
|
|
2826
2942
|
const out = [];
|
|
2827
|
-
async function
|
|
2943
|
+
async function walk10(current) {
|
|
2828
2944
|
const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2829
2945
|
for (const entry2 of entries) {
|
|
2830
2946
|
const full = import_node_path5.default.join(current, entry2.name);
|
|
@@ -2832,7 +2948,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2832
2948
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
2833
2949
|
if (excluded.has(import_node_path5.default.resolve(full))) continue;
|
|
2834
2950
|
if (await isPythonVenvDir(full)) continue;
|
|
2835
|
-
await
|
|
2951
|
+
await walk10(full);
|
|
2836
2952
|
} else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
2837
2953
|
// would attribute our instrumentation imports to the user's service.
|
|
2838
2954
|
!isNeatAuthoredSourceFile(entry2.name)) {
|
|
@@ -2840,7 +2956,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
|
|
|
2840
2956
|
}
|
|
2841
2957
|
}
|
|
2842
2958
|
}
|
|
2843
|
-
await
|
|
2959
|
+
await walk10(dir);
|
|
2844
2960
|
return out;
|
|
2845
2961
|
}
|
|
2846
2962
|
async function loadSourceFiles(dir, excludeDirs = []) {
|
|
@@ -3360,6 +3476,11 @@ function makePhpParser() {
|
|
|
3360
3476
|
p.setLanguage(import_tree_sitter_php.default.php_only);
|
|
3361
3477
|
return p;
|
|
3362
3478
|
}
|
|
3479
|
+
function makeRustParser() {
|
|
3480
|
+
const p = new import_tree_sitter2.default();
|
|
3481
|
+
p.setLanguage(import_tree_sitter_rust.default);
|
|
3482
|
+
return p;
|
|
3483
|
+
}
|
|
3363
3484
|
var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
3364
3485
|
"get",
|
|
3365
3486
|
"post",
|
|
@@ -3431,8 +3552,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3431
3552
|
chiWalk(tree.rootNode, "", out);
|
|
3432
3553
|
return out;
|
|
3433
3554
|
}
|
|
3434
|
-
function stripChiRegex(
|
|
3435
|
-
return
|
|
3555
|
+
function stripChiRegex(path78) {
|
|
3556
|
+
return path78.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3436
3557
|
}
|
|
3437
3558
|
function chiWalk(node, prefix, out) {
|
|
3438
3559
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -4104,9 +4225,9 @@ function rubyRocketRoute(args) {
|
|
|
4104
4225
|
if (!pair || pair.type !== "pair") continue;
|
|
4105
4226
|
const k = pair.childForFieldName("key");
|
|
4106
4227
|
if (k?.type !== "string") continue;
|
|
4107
|
-
const
|
|
4108
|
-
if (
|
|
4109
|
-
return { path:
|
|
4228
|
+
const path78 = rubyLiteral(k);
|
|
4229
|
+
if (path78 === null) continue;
|
|
4230
|
+
return { path: path78, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
4110
4231
|
}
|
|
4111
4232
|
return null;
|
|
4112
4233
|
}
|
|
@@ -4276,6 +4397,48 @@ function railsRoutesFromSource(source, parser) {
|
|
|
4276
4397
|
});
|
|
4277
4398
|
return out;
|
|
4278
4399
|
}
|
|
4400
|
+
var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4401
|
+
function sinatraRoutesFromSource(source, parser) {
|
|
4402
|
+
const tree = parseSource2(parser, source);
|
|
4403
|
+
if (!fileReferencesSinatra(tree.rootNode)) return [];
|
|
4404
|
+
const out = [];
|
|
4405
|
+
walk(tree.rootNode, (node) => {
|
|
4406
|
+
if (node.type !== "call") return;
|
|
4407
|
+
if (node.childForFieldName("receiver")) return;
|
|
4408
|
+
const method = node.childForFieldName("method")?.text;
|
|
4409
|
+
if (!method || !SINATRA_VERBS.has(method)) return;
|
|
4410
|
+
if (!node.childForFieldName("block")) return;
|
|
4411
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
4412
|
+
if (first?.type !== "string") return;
|
|
4413
|
+
const p = rubyLiteral(first);
|
|
4414
|
+
if (p === null || !p.startsWith("/")) return;
|
|
4415
|
+
out.push({
|
|
4416
|
+
method: method.toUpperCase(),
|
|
4417
|
+
pathTemplate: canonicalizeTemplate(p),
|
|
4418
|
+
line: node.startPosition.row + 1,
|
|
4419
|
+
framework: "sinatra"
|
|
4420
|
+
});
|
|
4421
|
+
});
|
|
4422
|
+
return out;
|
|
4423
|
+
}
|
|
4424
|
+
function fileReferencesSinatra(root) {
|
|
4425
|
+
let found = false;
|
|
4426
|
+
walk(root, (node) => {
|
|
4427
|
+
if (found) return;
|
|
4428
|
+
if (node.type === "call") {
|
|
4429
|
+
const m = node.childForFieldName("method")?.text;
|
|
4430
|
+
if (m === "require" || m === "require_relative") {
|
|
4431
|
+
const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
4432
|
+
if (s !== null && /^sinatra\b/.test(s)) found = true;
|
|
4433
|
+
}
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
|
|
4437
|
+
found = true;
|
|
4438
|
+
}
|
|
4439
|
+
});
|
|
4440
|
+
return found;
|
|
4441
|
+
}
|
|
4279
4442
|
var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
|
|
4280
4443
|
var LARAVEL_RESOURCE_ROWS = [
|
|
4281
4444
|
{ action: "index", methods: ["GET"], suffix: "" },
|
|
@@ -4451,6 +4614,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
|
|
|
4451
4614
|
}
|
|
4452
4615
|
return out;
|
|
4453
4616
|
}
|
|
4617
|
+
var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
|
|
4618
|
+
function isSlimAppCtor(node) {
|
|
4619
|
+
if (!node) return false;
|
|
4620
|
+
if (node.type === "scoped_call_expression") {
|
|
4621
|
+
const scope = node.childForFieldName("scope")?.text ?? "";
|
|
4622
|
+
const name = node.childForFieldName("name")?.text;
|
|
4623
|
+
return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
|
|
4624
|
+
}
|
|
4625
|
+
if (node.type === "object_creation_expression") {
|
|
4626
|
+
const cls = node.namedChild(0)?.text ?? "";
|
|
4627
|
+
return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
|
|
4628
|
+
}
|
|
4629
|
+
return false;
|
|
4630
|
+
}
|
|
4631
|
+
function isSlimAppType(node) {
|
|
4632
|
+
if (!node || node.type !== "named_type") return false;
|
|
4633
|
+
const text = node.text;
|
|
4634
|
+
return text === "App" || text.endsWith("\\App");
|
|
4635
|
+
}
|
|
4636
|
+
function collectSlimAppVars(root) {
|
|
4637
|
+
const vars = /* @__PURE__ */ new Set();
|
|
4638
|
+
walk(root, (node) => {
|
|
4639
|
+
if (node.type === "assignment_expression") {
|
|
4640
|
+
const left = node.childForFieldName("left");
|
|
4641
|
+
if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
|
|
4642
|
+
vars.add(left.text);
|
|
4643
|
+
}
|
|
4644
|
+
return;
|
|
4645
|
+
}
|
|
4646
|
+
if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
|
|
4647
|
+
const name = node.childForFieldName("name");
|
|
4648
|
+
if (name?.type === "variable_name") vars.add(name.text);
|
|
4649
|
+
}
|
|
4650
|
+
});
|
|
4651
|
+
return vars;
|
|
4652
|
+
}
|
|
4653
|
+
function slimClosureParamVars(closure) {
|
|
4654
|
+
const out = ["$this"];
|
|
4655
|
+
for (let i = 0; i < closure.namedChildCount; i++) {
|
|
4656
|
+
const params = closure.namedChild(i);
|
|
4657
|
+
if (params?.type !== "formal_parameters") continue;
|
|
4658
|
+
for (let j = 0; j < params.namedChildCount; j++) {
|
|
4659
|
+
const param = params.namedChild(j);
|
|
4660
|
+
if (param?.type !== "simple_parameter") continue;
|
|
4661
|
+
for (let k = 0; k < param.namedChildCount; k++) {
|
|
4662
|
+
const v = param.namedChild(k);
|
|
4663
|
+
if (v?.type === "variable_name") {
|
|
4664
|
+
out.push(v.text);
|
|
4665
|
+
break;
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
return out;
|
|
4671
|
+
}
|
|
4672
|
+
function phpStringArray(node) {
|
|
4673
|
+
const out = [];
|
|
4674
|
+
if (node?.type !== "array_creation_expression") return out;
|
|
4675
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4676
|
+
const el = node.namedChild(i);
|
|
4677
|
+
if (el?.type !== "array_element_initializer") continue;
|
|
4678
|
+
const s = phpStaticString(el.namedChild(0));
|
|
4679
|
+
if (s !== null) out.push(s);
|
|
4680
|
+
}
|
|
4681
|
+
return out;
|
|
4682
|
+
}
|
|
4683
|
+
function slimRoutesFromSource(source, parser) {
|
|
4684
|
+
const tree = parseSource2(parser, source);
|
|
4685
|
+
const appVars = collectSlimAppVars(tree.rootNode);
|
|
4686
|
+
if (appVars.size === 0) return [];
|
|
4687
|
+
const out = [];
|
|
4688
|
+
slimWalk(tree.rootNode, "", appVars, out);
|
|
4689
|
+
return out;
|
|
4690
|
+
}
|
|
4691
|
+
function slimWalk(node, prefix, appVars, out) {
|
|
4692
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4693
|
+
const child = node.namedChild(i);
|
|
4694
|
+
if (child) slimHandle(child, prefix, appVars, out);
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
function slimHandle(node, prefix, appVars, out) {
|
|
4698
|
+
if (node.type === "member_call_expression") {
|
|
4699
|
+
const obj = node.childForFieldName("object");
|
|
4700
|
+
const method = node.childForFieldName("name")?.text;
|
|
4701
|
+
const args = node.childForFieldName("arguments");
|
|
4702
|
+
if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
|
|
4703
|
+
const line = node.startPosition.row + 1;
|
|
4704
|
+
if (method === "group") {
|
|
4705
|
+
const groupPrefix = phpFirstString(args);
|
|
4706
|
+
const closure = laravelGroupClosure(args);
|
|
4707
|
+
if (groupPrefix !== null && closure) {
|
|
4708
|
+
const inner = new Set(appVars);
|
|
4709
|
+
for (const v of slimClosureParamVars(closure)) inner.add(v);
|
|
4710
|
+
const body = closure.childForFieldName("body");
|
|
4711
|
+
if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
|
|
4712
|
+
}
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
if (SLIM_VERBS.has(method)) {
|
|
4716
|
+
const p = phpFirstString(args);
|
|
4717
|
+
if (p !== null) {
|
|
4718
|
+
out.push({
|
|
4719
|
+
method: method.toUpperCase(),
|
|
4720
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4721
|
+
line,
|
|
4722
|
+
framework: "slim"
|
|
4723
|
+
});
|
|
4724
|
+
}
|
|
4725
|
+
return;
|
|
4726
|
+
}
|
|
4727
|
+
if (method === "any") {
|
|
4728
|
+
const p = phpFirstString(args);
|
|
4729
|
+
if (p !== null) {
|
|
4730
|
+
out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
|
|
4731
|
+
}
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
if (method === "map") {
|
|
4735
|
+
const vals = phpArgumentValues(args);
|
|
4736
|
+
const methods = phpStringArray(vals[0]);
|
|
4737
|
+
const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
|
|
4738
|
+
if (p !== null) {
|
|
4739
|
+
for (const m of methods) {
|
|
4740
|
+
out.push({
|
|
4741
|
+
method: m.toUpperCase(),
|
|
4742
|
+
pathTemplate: laravelJoinPath(prefix, p),
|
|
4743
|
+
line,
|
|
4744
|
+
framework: "slim"
|
|
4745
|
+
});
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
return;
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
slimWalk(node, prefix, appVars, out);
|
|
4753
|
+
}
|
|
4754
|
+
var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
|
|
4755
|
+
function rustStringContent(node) {
|
|
4756
|
+
if (!node || node.type !== "string_literal") return null;
|
|
4757
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
4758
|
+
if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
|
|
4759
|
+
}
|
|
4760
|
+
return "";
|
|
4761
|
+
}
|
|
4762
|
+
function actixRoutesFromSource(source, parser) {
|
|
4763
|
+
const tree = parseSource2(parser, source);
|
|
4764
|
+
const out = [];
|
|
4765
|
+
walk(tree.rootNode, (node) => {
|
|
4766
|
+
if (node.type === "attribute_item") {
|
|
4767
|
+
actixAttributeRoute(node, out);
|
|
4768
|
+
return;
|
|
4769
|
+
}
|
|
4770
|
+
if (node.type === "call_expression") {
|
|
4771
|
+
actixBuilderRoute(node, out);
|
|
4772
|
+
}
|
|
4773
|
+
});
|
|
4774
|
+
return out;
|
|
4775
|
+
}
|
|
4776
|
+
function actixAttributeRoute(attrItem, out) {
|
|
4777
|
+
const attr = attrItem.namedChild(0);
|
|
4778
|
+
if (!attr || attr.type !== "attribute") return;
|
|
4779
|
+
const nameNode = attr.namedChild(0);
|
|
4780
|
+
if (!nameNode) return;
|
|
4781
|
+
const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
|
|
4782
|
+
if (!macro) return;
|
|
4783
|
+
const tokens = attr.childForFieldName("arguments");
|
|
4784
|
+
if (!tokens || tokens.type !== "token_tree") return;
|
|
4785
|
+
const strings = [];
|
|
4786
|
+
for (let i = 0; i < tokens.namedChildCount; i++) {
|
|
4787
|
+
const s = rustStringContent(tokens.namedChild(i));
|
|
4788
|
+
if (s !== null) strings.push(s);
|
|
4789
|
+
}
|
|
4790
|
+
const pathStr = strings[0];
|
|
4791
|
+
if (pathStr === void 0 || !pathStr.startsWith("/")) return;
|
|
4792
|
+
const line = attrItem.startPosition.row + 1;
|
|
4793
|
+
const template = canonicalizeTemplate(pathStr);
|
|
4794
|
+
if (ACTIX_METHODS.has(macro)) {
|
|
4795
|
+
out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
|
|
4796
|
+
return;
|
|
4797
|
+
}
|
|
4798
|
+
if (macro === "route") {
|
|
4799
|
+
const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
|
|
4800
|
+
const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
|
|
4801
|
+
for (const m of list) {
|
|
4802
|
+
out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
function actixBuilderRoute(call, out) {
|
|
4807
|
+
const fn = call.childForFieldName("function");
|
|
4808
|
+
if (fn?.type !== "field_expression") return;
|
|
4809
|
+
if (fn.childForFieldName("field")?.text !== "route") return;
|
|
4810
|
+
const args = call.childForFieldName("arguments");
|
|
4811
|
+
const pathStr = rustStringContent(args?.namedChild(0));
|
|
4812
|
+
if (pathStr === null || !pathStr.startsWith("/")) return;
|
|
4813
|
+
const second = args?.namedChild(1);
|
|
4814
|
+
if (!second) return;
|
|
4815
|
+
const method = actixBuilderMethod(second);
|
|
4816
|
+
if (!method) return;
|
|
4817
|
+
out.push({
|
|
4818
|
+
method,
|
|
4819
|
+
pathTemplate: canonicalizeTemplate(pathStr),
|
|
4820
|
+
line: call.startPosition.row + 1,
|
|
4821
|
+
framework: "actix-web"
|
|
4822
|
+
});
|
|
4823
|
+
}
|
|
4824
|
+
function actixBuilderMethod(node) {
|
|
4825
|
+
let method = null;
|
|
4826
|
+
walk(node, (n) => {
|
|
4827
|
+
if (method || n.type !== "scoped_identifier") return;
|
|
4828
|
+
const verb = n.childForFieldName("name")?.text;
|
|
4829
|
+
const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
|
|
4830
|
+
if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
|
|
4831
|
+
});
|
|
4832
|
+
return method;
|
|
4833
|
+
}
|
|
4454
4834
|
function namedArgs(argsNode) {
|
|
4455
4835
|
const out = [];
|
|
4456
4836
|
if (!argsNode) return out;
|
|
@@ -4767,6 +5147,7 @@ async function addRoutes(graph, services) {
|
|
|
4767
5147
|
const goParser = makeGoParser2();
|
|
4768
5148
|
const rubyParser = makeRubyParser();
|
|
4769
5149
|
const phpParser = makePhpParser();
|
|
5150
|
+
const rustParser = makeRustParser();
|
|
4770
5151
|
let nodesAdded = 0;
|
|
4771
5152
|
let edgesAdded = 0;
|
|
4772
5153
|
for (const service of services) {
|
|
@@ -4789,7 +5170,10 @@ async function addRoutes(graph, services) {
|
|
|
4789
5170
|
const isGoService = service.node.language === "go";
|
|
4790
5171
|
const hasRails = deps["rails"] !== void 0;
|
|
4791
5172
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4792
|
-
|
|
5173
|
+
const hasSlim = deps["slim/slim"] !== void 0;
|
|
5174
|
+
const hasSinatra = deps["sinatra"] !== void 0;
|
|
5175
|
+
const hasActix = deps["actix-web"] !== void 0;
|
|
5176
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
|
|
4793
5177
|
continue;
|
|
4794
5178
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
4795
5179
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4800,7 +5184,8 @@ async function addRoutes(graph, services) {
|
|
|
4800
5184
|
const isGo = ext === ".go";
|
|
4801
5185
|
const isRb = ext === ".rb";
|
|
4802
5186
|
const isPhp = ext === ".php";
|
|
4803
|
-
|
|
5187
|
+
const isRs = ext === ".rs";
|
|
5188
|
+
if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
|
|
4804
5189
|
const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
|
|
4805
5190
|
let routes;
|
|
4806
5191
|
try {
|
|
@@ -4810,8 +5195,12 @@ async function addRoutes(graph, services) {
|
|
|
4810
5195
|
phpParser,
|
|
4811
5196
|
relFile === "routes/api.php" ? "/api" : ""
|
|
4812
5197
|
) : [];
|
|
5198
|
+
if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
|
|
4813
5199
|
} else if (isRb) {
|
|
4814
5200
|
routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
|
|
5201
|
+
if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
|
|
5202
|
+
} else if (isRs) {
|
|
5203
|
+
routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
|
|
4815
5204
|
} else if (isGo) {
|
|
4816
5205
|
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4817
5206
|
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
@@ -5046,6 +5435,37 @@ function loadIncidentThresholdsFromEnv() {
|
|
|
5046
5435
|
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
5047
5436
|
}
|
|
5048
5437
|
}
|
|
5438
|
+
var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
|
|
5439
|
+
function latencyStreamCeilingMs() {
|
|
5440
|
+
const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
|
|
5441
|
+
if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5442
|
+
const n = Number(raw);
|
|
5443
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
5444
|
+
console.warn(
|
|
5445
|
+
`[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
|
|
5446
|
+
);
|
|
5447
|
+
return DEFAULT_LATENCY_STREAM_CEILING_MS;
|
|
5448
|
+
}
|
|
5449
|
+
function spanServesEventStream(attrs) {
|
|
5450
|
+
for (const key of [
|
|
5451
|
+
"http.response.header.content-type",
|
|
5452
|
+
"http.response.header.content_type"
|
|
5453
|
+
]) {
|
|
5454
|
+
const v = attrs[key];
|
|
5455
|
+
const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
|
|
5456
|
+
for (const item of values) {
|
|
5457
|
+
if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
|
|
5458
|
+
return true;
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
return false;
|
|
5463
|
+
}
|
|
5464
|
+
function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
|
|
5465
|
+
if (span.websocketChannel !== void 0) return true;
|
|
5466
|
+
if (spanServesEventStream(span.attributes)) return true;
|
|
5467
|
+
return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
|
|
5468
|
+
}
|
|
5049
5469
|
function httpResponseStatusFromAttrs(attrs) {
|
|
5050
5470
|
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
5051
5471
|
const v = attrs[key];
|
|
@@ -5097,6 +5517,14 @@ function grpcStatusCodeFromAttrs(attrs) {
|
|
|
5097
5517
|
}
|
|
5098
5518
|
return void 0;
|
|
5099
5519
|
}
|
|
5520
|
+
function spanRecordsError(span) {
|
|
5521
|
+
if (span.statusCode === 2) return true;
|
|
5522
|
+
const grpc2 = grpcStatusCodeFromAttrs(span.attributes);
|
|
5523
|
+
if (grpc2 !== void 0 && grpc2 !== 0) return true;
|
|
5524
|
+
const httpStatus = httpResponseStatusFromAttrs(span.attributes);
|
|
5525
|
+
if (httpStatus !== void 0 && httpStatus >= 500) return true;
|
|
5526
|
+
return false;
|
|
5527
|
+
}
|
|
5100
5528
|
function nonHttpFailureMessageFromAttrs(attrs) {
|
|
5101
5529
|
const grpc2 = grpcStatusCodeFromAttrs(attrs);
|
|
5102
5530
|
if (grpc2 !== void 0 && grpc2 !== 0) {
|
|
@@ -5895,6 +6323,21 @@ async function recordExceptionIncident(ctx, span, ts) {
|
|
|
5895
6323
|
};
|
|
5896
6324
|
await appendErrorEvent(ctx, ev);
|
|
5897
6325
|
}
|
|
6326
|
+
async function recordGrpcFailureIncident(ctx, span, ts) {
|
|
6327
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
6328
|
+
const ev = {
|
|
6329
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
6330
|
+
timestamp: ts,
|
|
6331
|
+
service: span.service,
|
|
6332
|
+
traceId: span.traceId,
|
|
6333
|
+
spanId: span.spanId,
|
|
6334
|
+
errorType: "grpc-failure",
|
|
6335
|
+
errorMessage: incidentMessage(span),
|
|
6336
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
6337
|
+
affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath)
|
|
6338
|
+
};
|
|
6339
|
+
await appendErrorEvent(ctx, ev);
|
|
6340
|
+
}
|
|
5898
6341
|
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
5899
6342
|
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
5900
6343
|
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
@@ -5938,6 +6381,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
|
5938
6381
|
);
|
|
5939
6382
|
ctx.burstState.delete(key);
|
|
5940
6383
|
}
|
|
6384
|
+
var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
|
|
6385
|
+
function nextApiRouteTemplate(span) {
|
|
6386
|
+
const raw = pickAttr(span, "next.span_name") ?? span.name;
|
|
6387
|
+
const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
|
|
6388
|
+
return match ? match[1] : void 0;
|
|
6389
|
+
}
|
|
5941
6390
|
function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
|
|
5942
6391
|
const target = normalizePathTemplate(httpRoute);
|
|
5943
6392
|
const m = method?.toUpperCase();
|
|
@@ -5959,8 +6408,8 @@ async function handleSpan(ctx, span) {
|
|
|
5959
6408
|
warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT);
|
|
5960
6409
|
}
|
|
5961
6410
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
5962
|
-
const isError = span
|
|
5963
|
-
const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6411
|
+
const isError = spanRecordsError(span);
|
|
6412
|
+
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
5964
6413
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
5965
6414
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
5966
6415
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -6157,12 +6606,13 @@ async function handleSpan(ctx, span) {
|
|
|
6157
6606
|
}
|
|
6158
6607
|
}
|
|
6159
6608
|
}
|
|
6160
|
-
|
|
6609
|
+
const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
|
|
6610
|
+
if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
|
|
6161
6611
|
const routeNodeId = findRouteNodeByHttpRoute(
|
|
6162
6612
|
ctx.graph,
|
|
6163
6613
|
span.service,
|
|
6164
6614
|
span.httpMethod,
|
|
6165
|
-
|
|
6615
|
+
fusionRoute
|
|
6166
6616
|
);
|
|
6167
6617
|
if (routeNodeId) {
|
|
6168
6618
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
@@ -6194,10 +6644,13 @@ async function handleSpan(ctx, span) {
|
|
|
6194
6644
|
}
|
|
6195
6645
|
if (span.statusCode !== 2) {
|
|
6196
6646
|
const status2 = httpResponseStatus(span);
|
|
6647
|
+
const grpcStatus = grpcStatusCodeFromAttrs(span.attributes);
|
|
6197
6648
|
if (span.exception) {
|
|
6198
6649
|
await recordExceptionIncident(ctx, span, ts);
|
|
6199
6650
|
} else if (status2 !== void 0 && status2 >= 500) {
|
|
6200
6651
|
await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
|
|
6652
|
+
} else if (grpcStatus !== void 0 && grpcStatus !== 0) {
|
|
6653
|
+
await recordGrpcFailureIncident(ctx, span, ts);
|
|
6201
6654
|
} else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
6202
6655
|
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
|
|
6203
6656
|
}
|
|
@@ -7550,7 +8003,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
|
|
|
7550
8003
|
var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
7551
8004
|
var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
|
|
7552
8005
|
var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
|
|
7553
|
-
var
|
|
8006
|
+
var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
|
|
7554
8007
|
var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
|
|
7555
8008
|
var import_types20 = require("@neat.is/types");
|
|
7556
8009
|
var PARSE_CHUNK3 = 16384;
|
|
@@ -7571,7 +8024,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
|
|
|
7571
8024
|
".cs": import_tree_sitter_c_sharp.default,
|
|
7572
8025
|
".java": import_tree_sitter_java.default,
|
|
7573
8026
|
".kt": import_tree_sitter_kotlin.default,
|
|
7574
|
-
".rs":
|
|
8027
|
+
".rs": import_tree_sitter_rust2.default,
|
|
7575
8028
|
// C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
|
|
7576
8029
|
// `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
|
|
7577
8030
|
// headers. `.h` and `.c` are deliberately absent: they are shared with C (a
|
|
@@ -8011,15 +8464,15 @@ function collectKotlinSymbolDefs(root) {
|
|
|
8011
8464
|
});
|
|
8012
8465
|
};
|
|
8013
8466
|
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
8014
|
-
const
|
|
8467
|
+
const firstChildOfType2 = (node, types) => {
|
|
8015
8468
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8016
8469
|
const child = node.namedChild(i);
|
|
8017
8470
|
if (child && types.includes(child.type)) return child;
|
|
8018
8471
|
}
|
|
8019
8472
|
return void 0;
|
|
8020
8473
|
};
|
|
8021
|
-
const nameOf = (node, ...types) =>
|
|
8022
|
-
const bodyOf = (node) =>
|
|
8474
|
+
const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
|
|
8475
|
+
const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
|
|
8023
8476
|
let pkg;
|
|
8024
8477
|
for (let i = 0; i < root.namedChildCount; i++) {
|
|
8025
8478
|
const child = root.namedChild(i);
|
|
@@ -8520,7 +8973,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
8520
8973
|
return best;
|
|
8521
8974
|
};
|
|
8522
8975
|
const requests = [];
|
|
8523
|
-
const
|
|
8976
|
+
const walk10 = (node) => {
|
|
8524
8977
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
8525
8978
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
8526
8979
|
if (self && self.kind === "class") {
|
|
@@ -8566,10 +9019,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
8566
9019
|
}
|
|
8567
9020
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8568
9021
|
const child = node.namedChild(i);
|
|
8569
|
-
if (child)
|
|
9022
|
+
if (child) walk10(child);
|
|
8570
9023
|
}
|
|
8571
9024
|
};
|
|
8572
|
-
|
|
9025
|
+
walk10(root);
|
|
8573
9026
|
for (const req2 of requests) {
|
|
8574
9027
|
const targetSid = resolveTarget(req2.targetName, req2.wantKind);
|
|
8575
9028
|
if (!targetSid) continue;
|
|
@@ -8865,7 +9318,7 @@ async function addServerActions(graph, services) {
|
|
|
8865
9318
|
|
|
8866
9319
|
// src/extract/databases/index.ts
|
|
8867
9320
|
init_cjs_shims();
|
|
8868
|
-
var
|
|
9321
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
8869
9322
|
var import_types23 = require("@neat.is/types");
|
|
8870
9323
|
|
|
8871
9324
|
// src/extract/databases/db-config-yaml.ts
|
|
@@ -9300,9 +9753,163 @@ async function parse8(serviceDir) {
|
|
|
9300
9753
|
}
|
|
9301
9754
|
var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
9302
9755
|
|
|
9303
|
-
// src/extract/databases/
|
|
9756
|
+
// src/extract/databases/csharp.ts
|
|
9304
9757
|
init_cjs_shims();
|
|
9758
|
+
var import_node_fs22 = require("fs");
|
|
9305
9759
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
9760
|
+
var CS_EXT = ".cs";
|
|
9761
|
+
var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
|
|
9762
|
+
var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
|
|
9763
|
+
var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
|
|
9764
|
+
var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
9765
|
+
function hostIsUnresolved(host) {
|
|
9766
|
+
return host === "" || /[${}]/.test(host);
|
|
9767
|
+
}
|
|
9768
|
+
function looksLikePostgres(s) {
|
|
9769
|
+
return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
|
|
9770
|
+
}
|
|
9771
|
+
function looksLikeRedis(s) {
|
|
9772
|
+
return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
|
|
9773
|
+
}
|
|
9774
|
+
function parsePostgresConnection(raw) {
|
|
9775
|
+
const s = raw.trim();
|
|
9776
|
+
if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9777
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9778
|
+
for (const part of s.split(";")) {
|
|
9779
|
+
const eq = part.indexOf("=");
|
|
9780
|
+
if (eq < 0) continue;
|
|
9781
|
+
const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
|
|
9782
|
+
const value = part.slice(eq + 1).trim();
|
|
9783
|
+
if (value && !fields.has(key)) fields.set(key, value);
|
|
9784
|
+
}
|
|
9785
|
+
const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
|
|
9786
|
+
if (!hostRaw) return null;
|
|
9787
|
+
const host = hostRaw.split(",")[0].trim();
|
|
9788
|
+
if (hostIsUnresolved(host)) return null;
|
|
9789
|
+
const portRaw = fields.get("port");
|
|
9790
|
+
const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
|
|
9791
|
+
const database = fields.get("database") ?? fields.get("db") ?? "";
|
|
9792
|
+
return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
|
|
9793
|
+
}
|
|
9794
|
+
function parseRedisEndpoint(raw) {
|
|
9795
|
+
const s = raw.trim();
|
|
9796
|
+
if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
|
|
9797
|
+
const first = s.split(",")[0].trim();
|
|
9798
|
+
const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
|
|
9799
|
+
if (!m) return null;
|
|
9800
|
+
const host = m[1];
|
|
9801
|
+
if (hostIsUnresolved(host) || host.includes("=")) return null;
|
|
9802
|
+
const port = m[2] ? Number(m[2]) : void 0;
|
|
9803
|
+
return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
|
|
9804
|
+
}
|
|
9805
|
+
async function resolveEnvUpTree(startDir, name) {
|
|
9806
|
+
let dir = import_node_path32.default.resolve(startDir);
|
|
9807
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
9808
|
+
const value = await resolveEnvVar(dir, name);
|
|
9809
|
+
if (value !== null) return value;
|
|
9810
|
+
const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
|
|
9811
|
+
const parent = import_node_path32.default.dirname(dir);
|
|
9812
|
+
if (atRepoRoot || parent === dir) break;
|
|
9813
|
+
dir = parent;
|
|
9814
|
+
}
|
|
9815
|
+
return null;
|
|
9816
|
+
}
|
|
9817
|
+
async function interpolateEnvRefs(value, dir) {
|
|
9818
|
+
const refs = /* @__PURE__ */ new Set();
|
|
9819
|
+
for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
9820
|
+
refs.add(m[1] ?? m[2]);
|
|
9821
|
+
}
|
|
9822
|
+
let out = value;
|
|
9823
|
+
for (const name of refs) {
|
|
9824
|
+
const resolved = await resolveEnvUpTree(dir, name);
|
|
9825
|
+
if (resolved === null) continue;
|
|
9826
|
+
out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
|
|
9827
|
+
}
|
|
9828
|
+
return out;
|
|
9829
|
+
}
|
|
9830
|
+
function stringLiterals(masked) {
|
|
9831
|
+
const out = [];
|
|
9832
|
+
STRING_LITERAL_RE.lastIndex = 0;
|
|
9833
|
+
let m;
|
|
9834
|
+
while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
|
|
9835
|
+
return out;
|
|
9836
|
+
}
|
|
9837
|
+
function envKeys(masked) {
|
|
9838
|
+
const out = [];
|
|
9839
|
+
ENV_READ_RE.lastIndex = 0;
|
|
9840
|
+
let m;
|
|
9841
|
+
while ((m = ENV_READ_RE.exec(masked)) !== null) {
|
|
9842
|
+
const key = m[1] ?? m[2];
|
|
9843
|
+
if (key) out.push(key);
|
|
9844
|
+
}
|
|
9845
|
+
return out;
|
|
9846
|
+
}
|
|
9847
|
+
async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
|
|
9848
|
+
const out = [];
|
|
9849
|
+
for (const key of keys) {
|
|
9850
|
+
const raw = await resolveEnvUpTree(serviceDir, key);
|
|
9851
|
+
if (raw === null) continue;
|
|
9852
|
+
const value = await interpolateEnvRefs(raw, serviceDir);
|
|
9853
|
+
if (!looksLike(value)) continue;
|
|
9854
|
+
const parsed = parse11(value);
|
|
9855
|
+
if (parsed) out.push(parsed);
|
|
9856
|
+
}
|
|
9857
|
+
for (const lit of literals) {
|
|
9858
|
+
if (!looksLike(lit)) continue;
|
|
9859
|
+
const parsed = parse11(lit);
|
|
9860
|
+
if (parsed) out.push(parsed);
|
|
9861
|
+
}
|
|
9862
|
+
return out;
|
|
9863
|
+
}
|
|
9864
|
+
async function parse9(serviceDir) {
|
|
9865
|
+
const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
|
|
9866
|
+
(f) => import_node_path32.default.extname(f) === CS_EXT
|
|
9867
|
+
);
|
|
9868
|
+
if (files.length === 0) return [];
|
|
9869
|
+
const sources = [];
|
|
9870
|
+
for (const file of files) {
|
|
9871
|
+
const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
|
|
9872
|
+
if (content !== null) sources.push({ file, content });
|
|
9873
|
+
}
|
|
9874
|
+
let pgGateFile = null;
|
|
9875
|
+
let redisGateFile = null;
|
|
9876
|
+
for (const { file, content } of sources) {
|
|
9877
|
+
if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
|
|
9878
|
+
if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
|
|
9879
|
+
}
|
|
9880
|
+
if (!pgGateFile && !redisGateFile) return [];
|
|
9881
|
+
const literals = [];
|
|
9882
|
+
const keys = [];
|
|
9883
|
+
for (const { content } of sources) {
|
|
9884
|
+
const masked = maskCommentsInSource(content);
|
|
9885
|
+
literals.push(...stringLiterals(masked));
|
|
9886
|
+
keys.push(...envKeys(masked));
|
|
9887
|
+
}
|
|
9888
|
+
const out = [];
|
|
9889
|
+
const seenHosts = /* @__PURE__ */ new Set();
|
|
9890
|
+
const push = (config, sourceFile) => {
|
|
9891
|
+
const dedupe = `${config.engine}:${config.host}`;
|
|
9892
|
+
if (seenHosts.has(dedupe)) return;
|
|
9893
|
+
seenHosts.add(dedupe);
|
|
9894
|
+
out.push({ ...config, sourceFile });
|
|
9895
|
+
};
|
|
9896
|
+
if (pgGateFile) {
|
|
9897
|
+
for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
|
|
9898
|
+
push(pg3, pgGateFile);
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9901
|
+
if (redisGateFile) {
|
|
9902
|
+
for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
|
|
9903
|
+
push(redis, redisGateFile);
|
|
9904
|
+
}
|
|
9905
|
+
}
|
|
9906
|
+
return out;
|
|
9907
|
+
}
|
|
9908
|
+
var csharpParser = { name: "csharp", parse: parse9 };
|
|
9909
|
+
|
|
9910
|
+
// src/extract/databases/docker-compose.ts
|
|
9911
|
+
init_cjs_shims();
|
|
9912
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
9306
9913
|
function portFromService(svc) {
|
|
9307
9914
|
for (const raw of svc.ports ?? []) {
|
|
9308
9915
|
const str = String(raw);
|
|
@@ -9327,9 +9934,9 @@ function databaseFromEnv(svc) {
|
|
|
9327
9934
|
};
|
|
9328
9935
|
return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
|
|
9329
9936
|
}
|
|
9330
|
-
async function
|
|
9937
|
+
async function parse10(serviceDir) {
|
|
9331
9938
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
9332
|
-
const abs =
|
|
9939
|
+
const abs = import_node_path33.default.join(serviceDir, name);
|
|
9333
9940
|
if (!await exists(abs)) continue;
|
|
9334
9941
|
const raw = await readYaml(abs);
|
|
9335
9942
|
if (!raw?.services) return [];
|
|
@@ -9351,7 +9958,7 @@ async function parse9(serviceDir) {
|
|
|
9351
9958
|
}
|
|
9352
9959
|
return [];
|
|
9353
9960
|
}
|
|
9354
|
-
var dockerComposeParser = { name: "docker-compose", parse:
|
|
9961
|
+
var dockerComposeParser = { name: "docker-compose", parse: parse10 };
|
|
9355
9962
|
|
|
9356
9963
|
// src/extract/databases/index.ts
|
|
9357
9964
|
var DB_PARSERS = [
|
|
@@ -9363,6 +9970,7 @@ var DB_PARSERS = [
|
|
|
9363
9970
|
ormconfigParser,
|
|
9364
9971
|
typeormParser,
|
|
9365
9972
|
sequelizeParser,
|
|
9973
|
+
csharpParser,
|
|
9366
9974
|
dockerComposeParser
|
|
9367
9975
|
];
|
|
9368
9976
|
function compatibleDriversFor(engine) {
|
|
@@ -9501,7 +10109,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9501
10109
|
discoveredVia: mergedDiscoveredVia
|
|
9502
10110
|
});
|
|
9503
10111
|
}
|
|
9504
|
-
const relConfigFile = toPosix(
|
|
10112
|
+
const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
|
|
9505
10113
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9506
10114
|
graph,
|
|
9507
10115
|
service.pkg.name,
|
|
@@ -9510,7 +10118,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9510
10118
|
);
|
|
9511
10119
|
nodesAdded += fn;
|
|
9512
10120
|
edgesAdded += fe;
|
|
9513
|
-
const evidenceFile = toPosix(
|
|
10121
|
+
const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
|
|
9514
10122
|
const edge = {
|
|
9515
10123
|
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
|
|
9516
10124
|
source: fileNodeId,
|
|
@@ -9528,15 +10136,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9528
10136
|
if (allConfigs.length === 1) {
|
|
9529
10137
|
const primary = allConfigs[0];
|
|
9530
10138
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
9531
|
-
const relPath =
|
|
10139
|
+
const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
|
|
9532
10140
|
const cfgId = (0, import_types23.configId)(relPath);
|
|
9533
10141
|
if (!graph.hasNode(cfgId)) {
|
|
9534
10142
|
const cfgNode = {
|
|
9535
10143
|
id: cfgId,
|
|
9536
10144
|
type: import_types23.NodeType.ConfigNode,
|
|
9537
|
-
name:
|
|
10145
|
+
name: import_node_path34.default.basename(primary.sourceFile),
|
|
9538
10146
|
path: relPath,
|
|
9539
|
-
fileType: isConfigFile(
|
|
10147
|
+
fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
|
|
9540
10148
|
};
|
|
9541
10149
|
graph.addNode(cfgId, cfgNode);
|
|
9542
10150
|
nodesAdded++;
|
|
@@ -9577,27 +10185,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
9577
10185
|
|
|
9578
10186
|
// src/extract/configs.ts
|
|
9579
10187
|
init_cjs_shims();
|
|
9580
|
-
var
|
|
9581
|
-
var
|
|
10188
|
+
var import_node_fs23 = require("fs");
|
|
10189
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
9582
10190
|
var import_types24 = require("@neat.is/types");
|
|
9583
10191
|
async function walkConfigFiles(dir, excludeDirs = []) {
|
|
9584
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10192
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
|
|
9585
10193
|
const out = [];
|
|
9586
|
-
async function
|
|
9587
|
-
const entries = await
|
|
10194
|
+
async function walk10(current) {
|
|
10195
|
+
const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
|
|
9588
10196
|
for (const entry2 of entries) {
|
|
9589
|
-
const full =
|
|
10197
|
+
const full = import_node_path35.default.join(current, entry2.name);
|
|
9590
10198
|
if (entry2.isDirectory()) {
|
|
9591
10199
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
9592
|
-
if (excluded.has(
|
|
10200
|
+
if (excluded.has(import_node_path35.default.resolve(full))) continue;
|
|
9593
10201
|
if (await isPythonVenvDir(full)) continue;
|
|
9594
|
-
await
|
|
10202
|
+
await walk10(full);
|
|
9595
10203
|
} else if (entry2.isFile() && isConfigFile(entry2.name).match) {
|
|
9596
10204
|
out.push(full);
|
|
9597
10205
|
}
|
|
9598
10206
|
}
|
|
9599
10207
|
}
|
|
9600
|
-
await
|
|
10208
|
+
await walk10(dir);
|
|
9601
10209
|
return out;
|
|
9602
10210
|
}
|
|
9603
10211
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -9606,19 +10214,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9606
10214
|
for (const service of services) {
|
|
9607
10215
|
const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
|
|
9608
10216
|
for (const file of configFiles) {
|
|
9609
|
-
const relPath =
|
|
10217
|
+
const relPath = import_node_path35.default.relative(scanPath, file);
|
|
9610
10218
|
const node = {
|
|
9611
10219
|
id: (0, import_types24.configId)(relPath),
|
|
9612
10220
|
type: import_types24.NodeType.ConfigNode,
|
|
9613
|
-
name:
|
|
10221
|
+
name: import_node_path35.default.basename(file),
|
|
9614
10222
|
path: relPath,
|
|
9615
|
-
fileType: isConfigFile(
|
|
10223
|
+
fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
|
|
9616
10224
|
};
|
|
9617
10225
|
if (!graph.hasNode(node.id)) {
|
|
9618
10226
|
graph.addNode(node.id, node);
|
|
9619
10227
|
nodesAdded++;
|
|
9620
10228
|
}
|
|
9621
|
-
const relToService = toPosix(
|
|
10229
|
+
const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
|
|
9622
10230
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9623
10231
|
graph,
|
|
9624
10232
|
service.pkg.name,
|
|
@@ -9634,7 +10242,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9634
10242
|
type: import_types24.EdgeType.CONFIGURED_BY,
|
|
9635
10243
|
provenance: import_types24.Provenance.EXTRACTED,
|
|
9636
10244
|
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
9637
|
-
evidence: { file: relPath.split(
|
|
10245
|
+
evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
|
|
9638
10246
|
};
|
|
9639
10247
|
if (!graph.hasEdge(edge.id)) {
|
|
9640
10248
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -9647,8 +10255,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
9647
10255
|
|
|
9648
10256
|
// src/extract/proto.ts
|
|
9649
10257
|
init_cjs_shims();
|
|
9650
|
-
var
|
|
9651
|
-
var
|
|
10258
|
+
var import_node_fs24 = require("fs");
|
|
10259
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
9652
10260
|
var import_types25 = require("@neat.is/types");
|
|
9653
10261
|
var PROTO_EXTENSION = ".proto";
|
|
9654
10262
|
function packageOf(content) {
|
|
@@ -9686,23 +10294,23 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
9686
10294
|
return out;
|
|
9687
10295
|
}
|
|
9688
10296
|
async function walkProtoFiles(dir, excludeDirs = []) {
|
|
9689
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
10297
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
|
|
9690
10298
|
const out = [];
|
|
9691
|
-
async function
|
|
9692
|
-
const entries = await
|
|
10299
|
+
async function walk10(current) {
|
|
10300
|
+
const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
9693
10301
|
for (const entry2 of entries) {
|
|
9694
|
-
const full =
|
|
10302
|
+
const full = import_node_path36.default.join(current, entry2.name);
|
|
9695
10303
|
if (entry2.isDirectory()) {
|
|
9696
10304
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
9697
|
-
if (excluded.has(
|
|
10305
|
+
if (excluded.has(import_node_path36.default.resolve(full))) continue;
|
|
9698
10306
|
if (await isPythonVenvDir(full)) continue;
|
|
9699
|
-
await
|
|
9700
|
-
} else if (entry2.isFile() &&
|
|
10307
|
+
await walk10(full);
|
|
10308
|
+
} else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
|
|
9701
10309
|
out.push(full);
|
|
9702
10310
|
}
|
|
9703
10311
|
}
|
|
9704
10312
|
}
|
|
9705
|
-
await
|
|
10313
|
+
await walk10(dir);
|
|
9706
10314
|
return out;
|
|
9707
10315
|
}
|
|
9708
10316
|
async function addGrpcMethods(graph, services) {
|
|
@@ -9712,10 +10320,10 @@ async function addGrpcMethods(graph, services) {
|
|
|
9712
10320
|
const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
|
|
9713
10321
|
for (const protoPath of protoPaths) {
|
|
9714
10322
|
if (isTestPath(protoPath)) continue;
|
|
9715
|
-
const relFile = toPosix(
|
|
10323
|
+
const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
|
|
9716
10324
|
let content;
|
|
9717
10325
|
try {
|
|
9718
|
-
content = await
|
|
10326
|
+
content = await import_node_fs24.promises.readFile(protoPath, "utf8");
|
|
9719
10327
|
} catch (err) {
|
|
9720
10328
|
recordExtractionError("proto extraction", protoPath, err);
|
|
9721
10329
|
continue;
|
|
@@ -9770,11 +10378,11 @@ async function addGrpcMethods(graph, services) {
|
|
|
9770
10378
|
|
|
9771
10379
|
// src/extract/calls/index.ts
|
|
9772
10380
|
init_cjs_shims();
|
|
9773
|
-
var
|
|
10381
|
+
var import_types44 = require("@neat.is/types");
|
|
9774
10382
|
|
|
9775
10383
|
// src/extract/calls/http.ts
|
|
9776
10384
|
init_cjs_shims();
|
|
9777
|
-
var
|
|
10385
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
9778
10386
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
9779
10387
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9780
10388
|
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
@@ -9857,7 +10465,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9857
10465
|
const seen = /* @__PURE__ */ new Set();
|
|
9858
10466
|
for (const file of files) {
|
|
9859
10467
|
if (isTestPath(file.path)) continue;
|
|
9860
|
-
const parser = parserForExt(
|
|
10468
|
+
const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
|
|
9861
10469
|
let sites;
|
|
9862
10470
|
try {
|
|
9863
10471
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -9866,7 +10474,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9866
10474
|
continue;
|
|
9867
10475
|
}
|
|
9868
10476
|
if (sites.length === 0) continue;
|
|
9869
|
-
const relFile = toPosix(
|
|
10477
|
+
const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
|
|
9870
10478
|
for (const site of sites) {
|
|
9871
10479
|
const targetId = hostToNodeId.get(site.host);
|
|
9872
10480
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -9920,7 +10528,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
9920
10528
|
|
|
9921
10529
|
// src/extract/calls/route-match.ts
|
|
9922
10530
|
init_cjs_shims();
|
|
9923
|
-
var
|
|
10531
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
9924
10532
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
9925
10533
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9926
10534
|
var import_types27 = require("@neat.is/types");
|
|
@@ -10119,7 +10727,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10119
10727
|
const seen = /* @__PURE__ */ new Set();
|
|
10120
10728
|
for (const file of files) {
|
|
10121
10729
|
if (isTestPath(file.path)) continue;
|
|
10122
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
10730
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
|
|
10123
10731
|
let sites;
|
|
10124
10732
|
try {
|
|
10125
10733
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -10128,7 +10736,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10128
10736
|
continue;
|
|
10129
10737
|
}
|
|
10130
10738
|
if (sites.length === 0) continue;
|
|
10131
|
-
const relFile = toPosix(
|
|
10739
|
+
const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
|
|
10132
10740
|
for (const site of sites) {
|
|
10133
10741
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
10134
10742
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -10189,7 +10797,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
10189
10797
|
|
|
10190
10798
|
// src/extract/calls/kafka.ts
|
|
10191
10799
|
init_cjs_shims();
|
|
10192
|
-
var
|
|
10800
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
10193
10801
|
var import_types28 = require("@neat.is/types");
|
|
10194
10802
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10195
10803
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -10277,13 +10885,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10277
10885
|
// call sites — verified-call-site tier (ADR-066).
|
|
10278
10886
|
confidenceKind: "verified-call-site",
|
|
10279
10887
|
evidence: {
|
|
10280
|
-
file:
|
|
10888
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
10281
10889
|
line,
|
|
10282
10890
|
snippet: snippet(file.content, line)
|
|
10283
10891
|
}
|
|
10284
10892
|
});
|
|
10285
10893
|
};
|
|
10286
|
-
if (
|
|
10894
|
+
if (import_node_path39.default.extname(file.path) === ".go") {
|
|
10287
10895
|
goSaramaEndpoints(file.content, make);
|
|
10288
10896
|
} else {
|
|
10289
10897
|
for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
|
|
@@ -10294,7 +10902,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
10294
10902
|
|
|
10295
10903
|
// src/extract/calls/redis.ts
|
|
10296
10904
|
init_cjs_shims();
|
|
10297
|
-
var
|
|
10905
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
10298
10906
|
var import_types29 = require("@neat.is/types");
|
|
10299
10907
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
10300
10908
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -10317,7 +10925,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10317
10925
|
// support tier (ADR-066).
|
|
10318
10926
|
confidenceKind: "url-with-structural-support",
|
|
10319
10927
|
evidence: {
|
|
10320
|
-
file:
|
|
10928
|
+
file: import_node_path40.default.relative(serviceDir, file.path),
|
|
10321
10929
|
line,
|
|
10322
10930
|
snippet: snippet(file.content, line)
|
|
10323
10931
|
}
|
|
@@ -10328,7 +10936,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
10328
10936
|
|
|
10329
10937
|
// src/extract/calls/aws.ts
|
|
10330
10938
|
init_cjs_shims();
|
|
10331
|
-
var
|
|
10939
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
10332
10940
|
var import_types30 = require("@neat.is/types");
|
|
10333
10941
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
10334
10942
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -10362,7 +10970,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10362
10970
|
// (ADR-066).
|
|
10363
10971
|
confidenceKind: "verified-call-site",
|
|
10364
10972
|
evidence: {
|
|
10365
|
-
file:
|
|
10973
|
+
file: import_node_path41.default.relative(serviceDir, file.path),
|
|
10366
10974
|
line,
|
|
10367
10975
|
snippet: snippet(file.content, line)
|
|
10368
10976
|
}
|
|
@@ -10387,7 +10995,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
10387
10995
|
|
|
10388
10996
|
// src/extract/calls/grpc.ts
|
|
10389
10997
|
init_cjs_shims();
|
|
10390
|
-
var
|
|
10998
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
10391
10999
|
var import_types31 = require("@neat.is/types");
|
|
10392
11000
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
10393
11001
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -10446,7 +11054,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10446
11054
|
// tier (ADR-066).
|
|
10447
11055
|
confidenceKind: "verified-call-site",
|
|
10448
11056
|
evidence: {
|
|
10449
|
-
file:
|
|
11057
|
+
file: import_node_path42.default.relative(serviceDir, file.path),
|
|
10450
11058
|
line,
|
|
10451
11059
|
snippet: snippet(file.content, line)
|
|
10452
11060
|
}
|
|
@@ -10457,7 +11065,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
10457
11065
|
|
|
10458
11066
|
// src/extract/calls/supabase.ts
|
|
10459
11067
|
init_cjs_shims();
|
|
10460
|
-
var
|
|
11068
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
10461
11069
|
var import_types32 = require("@neat.is/types");
|
|
10462
11070
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
10463
11071
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -10516,7 +11124,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10516
11124
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
10517
11125
|
confidenceKind: "verified-call-site",
|
|
10518
11126
|
evidence: {
|
|
10519
|
-
file:
|
|
11127
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10520
11128
|
line,
|
|
10521
11129
|
snippet: snippet(file.content, line)
|
|
10522
11130
|
}
|
|
@@ -10543,7 +11151,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10543
11151
|
edgeType: "CALLS",
|
|
10544
11152
|
confidenceKind: "verified-call-site",
|
|
10545
11153
|
evidence: {
|
|
10546
|
-
file:
|
|
11154
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
10547
11155
|
line,
|
|
10548
11156
|
snippet: snippet(file.content, line)
|
|
10549
11157
|
}
|
|
@@ -10555,7 +11163,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
10555
11163
|
|
|
10556
11164
|
// src/extract/calls/firestore.ts
|
|
10557
11165
|
init_cjs_shims();
|
|
10558
|
-
var
|
|
11166
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
10559
11167
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
10560
11168
|
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
10561
11169
|
var import_types33 = require("@neat.is/types");
|
|
@@ -10594,7 +11202,7 @@ function isFirestoreClientFactory(node) {
|
|
|
10594
11202
|
}
|
|
10595
11203
|
function firestoreClientVars(root) {
|
|
10596
11204
|
const vars = /* @__PURE__ */ new Set();
|
|
10597
|
-
const
|
|
11205
|
+
const walk10 = (node) => {
|
|
10598
11206
|
if (node.type === "variable_declarator") {
|
|
10599
11207
|
const name = node.childForFieldName("name");
|
|
10600
11208
|
let value = node.childForFieldName("value");
|
|
@@ -10603,9 +11211,9 @@ function firestoreClientVars(root) {
|
|
|
10603
11211
|
vars.add(name.text);
|
|
10604
11212
|
}
|
|
10605
11213
|
}
|
|
10606
|
-
for (const c of namedChildren(node))
|
|
11214
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10607
11215
|
};
|
|
10608
|
-
|
|
11216
|
+
walk10(root);
|
|
10609
11217
|
return vars;
|
|
10610
11218
|
}
|
|
10611
11219
|
function isClientExpr(node, clientVars) {
|
|
@@ -10726,7 +11334,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10726
11334
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
10727
11335
|
if (!hasClient && !hasAdmin) return [];
|
|
10728
11336
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
10729
|
-
const tree = parseSource3(parserForExt2(
|
|
11337
|
+
const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
|
|
10730
11338
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
10731
11339
|
const collLine = /* @__PURE__ */ new Map();
|
|
10732
11340
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -10760,7 +11368,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10760
11368
|
}
|
|
10761
11369
|
s.add(field);
|
|
10762
11370
|
};
|
|
10763
|
-
const
|
|
11371
|
+
const walk10 = (node) => {
|
|
10764
11372
|
if (node.type === "call_expression") {
|
|
10765
11373
|
const fn = node.childForFieldName("function");
|
|
10766
11374
|
const line = node.startPosition.row + 1;
|
|
@@ -10800,9 +11408,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10800
11408
|
}
|
|
10801
11409
|
}
|
|
10802
11410
|
}
|
|
10803
|
-
for (const c of namedChildren(node))
|
|
11411
|
+
for (const c of namedChildren(node)) walk10(c);
|
|
10804
11412
|
};
|
|
10805
|
-
|
|
11413
|
+
walk10(tree.rootNode);
|
|
10806
11414
|
const out = [];
|
|
10807
11415
|
for (const [collPath, line] of collLine) {
|
|
10808
11416
|
const byField = writes.get(collPath);
|
|
@@ -10830,7 +11438,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10830
11438
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
10831
11439
|
...sdkWrites ? { sdkWrites } : {},
|
|
10832
11440
|
evidence: {
|
|
10833
|
-
file:
|
|
11441
|
+
file: import_node_path44.default.relative(serviceDir, file.path),
|
|
10834
11442
|
line,
|
|
10835
11443
|
snippet: snippet(file.content, line)
|
|
10836
11444
|
}
|
|
@@ -10841,7 +11449,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
10841
11449
|
|
|
10842
11450
|
// src/extract/calls/mongoose.ts
|
|
10843
11451
|
init_cjs_shims();
|
|
10844
|
-
var
|
|
11452
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
10845
11453
|
var import_types34 = require("@neat.is/types");
|
|
10846
11454
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
10847
11455
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -10992,7 +11600,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
10992
11600
|
kind: r.kind,
|
|
10993
11601
|
edgeType: "CALLS",
|
|
10994
11602
|
confidenceKind: "verified-call-site",
|
|
10995
|
-
evidence: { file:
|
|
11603
|
+
evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
10996
11604
|
};
|
|
10997
11605
|
}
|
|
10998
11606
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -11082,7 +11690,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11082
11690
|
const registry = /* @__PURE__ */ new Map();
|
|
11083
11691
|
for (const f of mongooseFiles) {
|
|
11084
11692
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
11085
|
-
if (fx) registry.set(toPosix(
|
|
11693
|
+
if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
|
|
11086
11694
|
}
|
|
11087
11695
|
if (registry.size === 0) return [];
|
|
11088
11696
|
const out = [];
|
|
@@ -11093,7 +11701,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11093
11701
|
const directColl = /* @__PURE__ */ new Map();
|
|
11094
11702
|
const nsExports = /* @__PURE__ */ new Map();
|
|
11095
11703
|
for (const b of bindings) {
|
|
11096
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
11704
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
|
|
11097
11705
|
if (!resolvedRel) continue;
|
|
11098
11706
|
const fx = registry.get(resolvedRel);
|
|
11099
11707
|
if (!fx) continue;
|
|
@@ -11137,7 +11745,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
11137
11745
|
|
|
11138
11746
|
// src/extract/calls/sqlalchemy.ts
|
|
11139
11747
|
init_cjs_shims();
|
|
11140
|
-
var
|
|
11748
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
11141
11749
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
11142
11750
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
11143
11751
|
var import_types35 = require("@neat.is/types");
|
|
@@ -11276,7 +11884,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
11276
11884
|
childTable,
|
|
11277
11885
|
parentTable,
|
|
11278
11886
|
evidence: {
|
|
11279
|
-
file:
|
|
11887
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11280
11888
|
line,
|
|
11281
11889
|
snippet: snippet(file.content, line)
|
|
11282
11890
|
}
|
|
@@ -11301,7 +11909,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
11301
11909
|
confidenceKind: "verified-call-site",
|
|
11302
11910
|
...columns && columns.length > 0 ? { columns } : {},
|
|
11303
11911
|
evidence: {
|
|
11304
|
-
file:
|
|
11912
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11305
11913
|
line,
|
|
11306
11914
|
snippet: snippet(file.content, line)
|
|
11307
11915
|
}
|
|
@@ -11408,7 +12016,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11408
12016
|
edgeType: "CALLS",
|
|
11409
12017
|
confidenceKind: "verified-call-site",
|
|
11410
12018
|
evidence: {
|
|
11411
|
-
file:
|
|
12019
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
11412
12020
|
line,
|
|
11413
12021
|
snippet: snippet(file.content, line)
|
|
11414
12022
|
}
|
|
@@ -11420,7 +12028,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
11420
12028
|
|
|
11421
12029
|
// src/extract/calls/django-orm.ts
|
|
11422
12030
|
init_cjs_shims();
|
|
11423
|
-
var
|
|
12031
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
11424
12032
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
11425
12033
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
11426
12034
|
var import_types36 = require("@neat.is/types");
|
|
@@ -11490,7 +12098,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11490
12098
|
const tree = parseSource7(makePyParser4(), file.content);
|
|
11491
12099
|
const out = [];
|
|
11492
12100
|
const seen = /* @__PURE__ */ new Set();
|
|
11493
|
-
const defaultAppLabel =
|
|
12101
|
+
const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
|
|
11494
12102
|
walk4(tree.rootNode, (node) => {
|
|
11495
12103
|
if (node.type !== "class_definition") return;
|
|
11496
12104
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -11508,7 +12116,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11508
12116
|
kind: "sql-table",
|
|
11509
12117
|
edgeType: "CALLS",
|
|
11510
12118
|
confidenceKind: "verified-call-site",
|
|
11511
|
-
evidence: { file:
|
|
12119
|
+
evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
11512
12120
|
});
|
|
11513
12121
|
});
|
|
11514
12122
|
return out;
|
|
@@ -11516,7 +12124,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
11516
12124
|
|
|
11517
12125
|
// src/extract/calls/drizzle.ts
|
|
11518
12126
|
init_cjs_shims();
|
|
11519
|
-
var
|
|
12127
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
11520
12128
|
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
11521
12129
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
11522
12130
|
var import_types37 = require("@neat.is/types");
|
|
@@ -11594,10 +12202,10 @@ function columnsFromObject(obj) {
|
|
|
11594
12202
|
}
|
|
11595
12203
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
11596
12204
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11597
|
-
const tree = parseSource3(parserForExt3(
|
|
12205
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11598
12206
|
const out = [];
|
|
11599
12207
|
const seen = /* @__PURE__ */ new Set();
|
|
11600
|
-
const
|
|
12208
|
+
const walk10 = (node) => {
|
|
11601
12209
|
if (node.type === "call_expression") {
|
|
11602
12210
|
const fn = node.childForFieldName("function");
|
|
11603
12211
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11617,7 +12225,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11617
12225
|
confidenceKind: "structural",
|
|
11618
12226
|
columns,
|
|
11619
12227
|
evidence: {
|
|
11620
|
-
file:
|
|
12228
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11621
12229
|
line,
|
|
11622
12230
|
snippet: snippet(file.content, line)
|
|
11623
12231
|
}
|
|
@@ -11625,9 +12233,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
11625
12233
|
}
|
|
11626
12234
|
}
|
|
11627
12235
|
}
|
|
11628
|
-
for (const c of namedChildren4(node))
|
|
12236
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11629
12237
|
};
|
|
11630
|
-
|
|
12238
|
+
walk10(tree.rootNode);
|
|
11631
12239
|
return out;
|
|
11632
12240
|
}
|
|
11633
12241
|
function enclosingVarName(call) {
|
|
@@ -11649,7 +12257,7 @@ function enclosingVarName(call) {
|
|
|
11649
12257
|
function collectDrizzleTables(root) {
|
|
11650
12258
|
const tables = [];
|
|
11651
12259
|
const varToTable = /* @__PURE__ */ new Map();
|
|
11652
|
-
const
|
|
12260
|
+
const walk10 = (node) => {
|
|
11653
12261
|
if (node.type === "call_expression") {
|
|
11654
12262
|
const fn = node.childForFieldName("function");
|
|
11655
12263
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -11664,9 +12272,9 @@ function collectDrizzleTables(root) {
|
|
|
11664
12272
|
}
|
|
11665
12273
|
}
|
|
11666
12274
|
}
|
|
11667
|
-
for (const c of namedChildren4(node))
|
|
12275
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11668
12276
|
};
|
|
11669
|
-
|
|
12277
|
+
walk10(root);
|
|
11670
12278
|
return { tables, varToTable };
|
|
11671
12279
|
}
|
|
11672
12280
|
function referencesTargetVar(call) {
|
|
@@ -11683,13 +12291,13 @@ function referencesTargetVar(call) {
|
|
|
11683
12291
|
}
|
|
11684
12292
|
function drizzleForeignKeys(file, serviceDir) {
|
|
11685
12293
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
11686
|
-
const tree = parseSource3(parserForExt3(
|
|
12294
|
+
const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
|
|
11687
12295
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
11688
12296
|
const out = [];
|
|
11689
12297
|
const seen = /* @__PURE__ */ new Set();
|
|
11690
12298
|
for (const table of tables) {
|
|
11691
12299
|
if (!table.object) continue;
|
|
11692
|
-
const
|
|
12300
|
+
const walk10 = (node) => {
|
|
11693
12301
|
if (node.type === "call_expression") {
|
|
11694
12302
|
const targetVar = referencesTargetVar(node);
|
|
11695
12303
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -11702,7 +12310,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11702
12310
|
childTable: table.tableName,
|
|
11703
12311
|
parentTable,
|
|
11704
12312
|
evidence: {
|
|
11705
|
-
file:
|
|
12313
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
11706
12314
|
line,
|
|
11707
12315
|
snippet: snippet(file.content, line)
|
|
11708
12316
|
}
|
|
@@ -11710,16 +12318,16 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
11710
12318
|
}
|
|
11711
12319
|
}
|
|
11712
12320
|
}
|
|
11713
|
-
for (const c of namedChildren4(node))
|
|
12321
|
+
for (const c of namedChildren4(node)) walk10(c);
|
|
11714
12322
|
};
|
|
11715
|
-
|
|
12323
|
+
walk10(table.object);
|
|
11716
12324
|
}
|
|
11717
12325
|
return out;
|
|
11718
12326
|
}
|
|
11719
12327
|
|
|
11720
12328
|
// src/extract/calls/prisma.ts
|
|
11721
12329
|
init_cjs_shims();
|
|
11722
|
-
var
|
|
12330
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
11723
12331
|
var import_types38 = require("@neat.is/types");
|
|
11724
12332
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
11725
12333
|
"Int",
|
|
@@ -11775,7 +12383,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11775
12383
|
confidenceKind: "structural",
|
|
11776
12384
|
columns: b.columns,
|
|
11777
12385
|
evidence: {
|
|
11778
|
-
file:
|
|
12386
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11779
12387
|
line: b.startLine,
|
|
11780
12388
|
snippet: snippet(content, b.startLine)
|
|
11781
12389
|
}
|
|
@@ -11836,7 +12444,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
11836
12444
|
}
|
|
11837
12445
|
async function prismaColumnEndpoints(serviceDir) {
|
|
11838
12446
|
const schemaPath = await findFirst(serviceDir, [
|
|
11839
|
-
|
|
12447
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11840
12448
|
"schema.prisma"
|
|
11841
12449
|
]);
|
|
11842
12450
|
if (!schemaPath) return [];
|
|
@@ -11911,7 +12519,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11911
12519
|
childTable: current.table,
|
|
11912
12520
|
parentTable,
|
|
11913
12521
|
evidence: {
|
|
11914
|
-
file:
|
|
12522
|
+
file: import_node_path49.default.relative(serviceDir, file.path),
|
|
11915
12523
|
line: lineNo,
|
|
11916
12524
|
snippet: snippet(content, lineNo)
|
|
11917
12525
|
}
|
|
@@ -11926,7 +12534,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
11926
12534
|
}
|
|
11927
12535
|
async function prismaForeignKeys(serviceDir) {
|
|
11928
12536
|
const schemaPath = await findFirst(serviceDir, [
|
|
11929
|
-
|
|
12537
|
+
import_node_path49.default.join("prisma", "schema.prisma"),
|
|
11930
12538
|
"schema.prisma"
|
|
11931
12539
|
]);
|
|
11932
12540
|
if (!schemaPath) return [];
|
|
@@ -11937,7 +12545,7 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
11937
12545
|
|
|
11938
12546
|
// src/extract/calls/activerecord.ts
|
|
11939
12547
|
init_cjs_shims();
|
|
11940
|
-
var
|
|
12548
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11941
12549
|
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
11942
12550
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
11943
12551
|
var import_types39 = require("@neat.is/types");
|
|
@@ -12145,7 +12753,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
12145
12753
|
confidenceKind: "structural",
|
|
12146
12754
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
12147
12755
|
evidence: {
|
|
12148
|
-
file:
|
|
12756
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12149
12757
|
line: table.line,
|
|
12150
12758
|
snippet: snippet(file.content, table.line)
|
|
12151
12759
|
}
|
|
@@ -12167,7 +12775,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
12167
12775
|
childTable,
|
|
12168
12776
|
parentTable,
|
|
12169
12777
|
evidence: {
|
|
12170
|
-
file:
|
|
12778
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12171
12779
|
line,
|
|
12172
12780
|
snippet: snippet(file.content, line)
|
|
12173
12781
|
}
|
|
@@ -12250,7 +12858,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
12250
12858
|
edgeType: "CALLS",
|
|
12251
12859
|
confidenceKind: "verified-call-site",
|
|
12252
12860
|
evidence: {
|
|
12253
|
-
file:
|
|
12861
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12254
12862
|
line,
|
|
12255
12863
|
snippet: snippet(file.content, line)
|
|
12256
12864
|
}
|
|
@@ -12287,7 +12895,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12287
12895
|
childTable,
|
|
12288
12896
|
parentTable,
|
|
12289
12897
|
evidence: {
|
|
12290
|
-
file:
|
|
12898
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
12291
12899
|
line,
|
|
12292
12900
|
snippet: snippet(file.content, line)
|
|
12293
12901
|
}
|
|
@@ -12299,7 +12907,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
12299
12907
|
|
|
12300
12908
|
// src/extract/calls/eloquent.ts
|
|
12301
12909
|
init_cjs_shims();
|
|
12302
|
-
var
|
|
12910
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12303
12911
|
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
12304
12912
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
12305
12913
|
var import_types40 = require("@neat.is/types");
|
|
@@ -12556,7 +13164,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
12556
13164
|
confidenceKind: "structural",
|
|
12557
13165
|
...columns.length > 0 ? { columns } : {},
|
|
12558
13166
|
evidence: {
|
|
12559
|
-
file:
|
|
13167
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12560
13168
|
line: bp.line,
|
|
12561
13169
|
snippet: snippet(file.content, bp.line)
|
|
12562
13170
|
}
|
|
@@ -12578,7 +13186,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
12578
13186
|
childTable,
|
|
12579
13187
|
parentTable,
|
|
12580
13188
|
evidence: {
|
|
12581
|
-
file:
|
|
13189
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12582
13190
|
line,
|
|
12583
13191
|
snippet: snippet(file.content, line)
|
|
12584
13192
|
}
|
|
@@ -12698,7 +13306,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
12698
13306
|
edgeType: "CALLS",
|
|
12699
13307
|
confidenceKind: "verified-call-site",
|
|
12700
13308
|
evidence: {
|
|
12701
|
-
file:
|
|
13309
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12702
13310
|
line,
|
|
12703
13311
|
snippet: snippet(file.content, line)
|
|
12704
13312
|
}
|
|
@@ -12734,7 +13342,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12734
13342
|
childTable,
|
|
12735
13343
|
parentTable,
|
|
12736
13344
|
evidence: {
|
|
12737
|
-
file:
|
|
13345
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
12738
13346
|
line,
|
|
12739
13347
|
snippet: snippet(file.content, line)
|
|
12740
13348
|
}
|
|
@@ -12746,7 +13354,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
12746
13354
|
|
|
12747
13355
|
// src/extract/calls/go.ts
|
|
12748
13356
|
init_cjs_shims();
|
|
12749
|
-
var
|
|
13357
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12750
13358
|
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
12751
13359
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
12752
13360
|
var import_types41 = require("@neat.is/types");
|
|
@@ -12821,7 +13429,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
12821
13429
|
return null;
|
|
12822
13430
|
}
|
|
12823
13431
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
12824
|
-
if (
|
|
13432
|
+
if (import_node_path54.default.extname(file.path) !== ".go") return [];
|
|
12825
13433
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
12826
13434
|
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12827
13435
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -12850,7 +13458,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12850
13458
|
confidenceKind: "verified-call-site",
|
|
12851
13459
|
...columns.length > 0 ? { columns } : {},
|
|
12852
13460
|
evidence: {
|
|
12853
|
-
file: toPosix(
|
|
13461
|
+
file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
|
|
12854
13462
|
line,
|
|
12855
13463
|
snippet: snippet(file.content, line)
|
|
12856
13464
|
}
|
|
@@ -12861,7 +13469,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
12861
13469
|
|
|
12862
13470
|
// src/extract/calls/gorm.ts
|
|
12863
13471
|
init_cjs_shims();
|
|
12864
|
-
var
|
|
13472
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12865
13473
|
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
12866
13474
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
12867
13475
|
var import_types42 = require("@neat.is/types");
|
|
@@ -13294,7 +13902,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
13294
13902
|
seen.delete(struct.name);
|
|
13295
13903
|
}
|
|
13296
13904
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
13297
|
-
if (
|
|
13905
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13298
13906
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13299
13907
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13300
13908
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13316,7 +13924,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13316
13924
|
confidenceKind: "structural",
|
|
13317
13925
|
...columns.length > 0 ? { columns } : {},
|
|
13318
13926
|
evidence: {
|
|
13319
|
-
file: toPosix(
|
|
13927
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13320
13928
|
line: struct.line,
|
|
13321
13929
|
snippet: snippet(file.content, struct.line)
|
|
13322
13930
|
}
|
|
@@ -13325,7 +13933,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
13325
13933
|
return out;
|
|
13326
13934
|
}
|
|
13327
13935
|
function gormForeignKeys(file, serviceDir) {
|
|
13328
|
-
if (
|
|
13936
|
+
if (import_node_path55.default.extname(file.path) !== ".go") return [];
|
|
13329
13937
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
13330
13938
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
13331
13939
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -13340,7 +13948,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13340
13948
|
childTable,
|
|
13341
13949
|
parentTable,
|
|
13342
13950
|
evidence: {
|
|
13343
|
-
file: toPosix(
|
|
13951
|
+
file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
|
|
13344
13952
|
line,
|
|
13345
13953
|
snippet: snippet(file.content, line)
|
|
13346
13954
|
}
|
|
@@ -13375,15 +13983,131 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
13375
13983
|
return out;
|
|
13376
13984
|
}
|
|
13377
13985
|
|
|
13986
|
+
// src/extract/calls/efcore.ts
|
|
13987
|
+
init_cjs_shims();
|
|
13988
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
13989
|
+
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
13990
|
+
var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
13991
|
+
var import_types43 = require("@neat.is/types");
|
|
13992
|
+
var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
|
|
13993
|
+
var PARSE_CHUNK12 = 16384;
|
|
13994
|
+
function makeCsParser() {
|
|
13995
|
+
const p = new import_tree_sitter16.default();
|
|
13996
|
+
p.setLanguage(import_tree_sitter_c_sharp2.default);
|
|
13997
|
+
return p;
|
|
13998
|
+
}
|
|
13999
|
+
function parseSource12(parser, source) {
|
|
14000
|
+
return parser.parse(
|
|
14001
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
|
|
14002
|
+
);
|
|
14003
|
+
}
|
|
14004
|
+
function walk9(node, visit) {
|
|
14005
|
+
visit(node);
|
|
14006
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14007
|
+
const c = node.namedChild(i);
|
|
14008
|
+
if (c) walk9(c, visit);
|
|
14009
|
+
}
|
|
14010
|
+
}
|
|
14011
|
+
function firstChildOfType(node, type) {
|
|
14012
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14013
|
+
const c = node.namedChild(i);
|
|
14014
|
+
if (c?.type === type) return c;
|
|
14015
|
+
}
|
|
14016
|
+
return null;
|
|
14017
|
+
}
|
|
14018
|
+
function csStringLiteral(node) {
|
|
14019
|
+
if (node.type === "string_literal") {
|
|
14020
|
+
let out = "";
|
|
14021
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
14022
|
+
const c = node.namedChild(i);
|
|
14023
|
+
if (c?.type === "string_literal_content") out += c.text;
|
|
14024
|
+
}
|
|
14025
|
+
return out;
|
|
14026
|
+
}
|
|
14027
|
+
if (node.type === "verbatim_string_literal") {
|
|
14028
|
+
const t = node.text;
|
|
14029
|
+
return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
|
|
14030
|
+
}
|
|
14031
|
+
return null;
|
|
14032
|
+
}
|
|
14033
|
+
function attributeName(attr) {
|
|
14034
|
+
const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
|
|
14035
|
+
if (!nameNode) return null;
|
|
14036
|
+
const text = nameNode.text;
|
|
14037
|
+
const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
|
|
14038
|
+
return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
|
|
14039
|
+
}
|
|
14040
|
+
function tableFromAttribute(attr) {
|
|
14041
|
+
if (attributeName(attr) !== "Table") return null;
|
|
14042
|
+
const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
|
|
14043
|
+
if (!args) return null;
|
|
14044
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
14045
|
+
const arg = args.namedChild(i);
|
|
14046
|
+
if (arg?.type !== "attribute_argument") continue;
|
|
14047
|
+
const first = arg.namedChild(0);
|
|
14048
|
+
if (!first) continue;
|
|
14049
|
+
const value = csStringLiteral(first);
|
|
14050
|
+
if (value !== null) return value;
|
|
14051
|
+
return null;
|
|
14052
|
+
}
|
|
14053
|
+
return null;
|
|
14054
|
+
}
|
|
14055
|
+
function tableFromToTable(call) {
|
|
14056
|
+
const fn = call.childForFieldName("function");
|
|
14057
|
+
if (fn?.type !== "member_access_expression") return null;
|
|
14058
|
+
const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
|
|
14059
|
+
if (method?.text !== "ToTable") return null;
|
|
14060
|
+
const args = call.childForFieldName("arguments");
|
|
14061
|
+
const firstArg2 = args?.namedChild(0);
|
|
14062
|
+
if (firstArg2?.type !== "argument") return null;
|
|
14063
|
+
const value = firstArg2.namedChild(0);
|
|
14064
|
+
return value ? csStringLiteral(value) : null;
|
|
14065
|
+
}
|
|
14066
|
+
function efcoreEndpointsFromFile(file, serviceDir) {
|
|
14067
|
+
if (import_node_path56.default.extname(file.path) !== ".cs") return [];
|
|
14068
|
+
if (!EFCORE_GATE.test(file.content)) return [];
|
|
14069
|
+
const tree = parseSource12(makeCsParser(), file.content);
|
|
14070
|
+
const out = [];
|
|
14071
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14072
|
+
const push = (name, line) => {
|
|
14073
|
+
if (!name || seen.has(name)) return;
|
|
14074
|
+
seen.add(name);
|
|
14075
|
+
out.push({
|
|
14076
|
+
infraId: (0, import_types43.infraId)("sql-table", name),
|
|
14077
|
+
name,
|
|
14078
|
+
kind: "sql-table",
|
|
14079
|
+
edgeType: "CALLS",
|
|
14080
|
+
confidenceKind: "structural",
|
|
14081
|
+
evidence: {
|
|
14082
|
+
file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
|
|
14083
|
+
line,
|
|
14084
|
+
snippet: snippet(file.content, line)
|
|
14085
|
+
}
|
|
14086
|
+
});
|
|
14087
|
+
};
|
|
14088
|
+
walk9(tree.rootNode, (node) => {
|
|
14089
|
+
if (node.type === "attribute") {
|
|
14090
|
+
const table = tableFromAttribute(node);
|
|
14091
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14092
|
+
return;
|
|
14093
|
+
}
|
|
14094
|
+
if (node.type === "invocation_expression") {
|
|
14095
|
+
const table = tableFromToTable(node);
|
|
14096
|
+
if (table) push(table, node.startPosition.row + 1);
|
|
14097
|
+
}
|
|
14098
|
+
});
|
|
14099
|
+
return out;
|
|
14100
|
+
}
|
|
14101
|
+
|
|
13378
14102
|
// src/extract/calls/index.ts
|
|
13379
14103
|
function edgeTypeFromEndpoint(ep) {
|
|
13380
14104
|
switch (ep.edgeType) {
|
|
13381
14105
|
case "PUBLISHES_TO":
|
|
13382
|
-
return
|
|
14106
|
+
return import_types44.EdgeType.PUBLISHES_TO;
|
|
13383
14107
|
case "CONSUMES_FROM":
|
|
13384
|
-
return
|
|
14108
|
+
return import_types44.EdgeType.CONSUMES_FROM;
|
|
13385
14109
|
default:
|
|
13386
|
-
return
|
|
14110
|
+
return import_types44.EdgeType.CALLS;
|
|
13387
14111
|
}
|
|
13388
14112
|
}
|
|
13389
14113
|
function isAwsKind(kind) {
|
|
@@ -13433,6 +14157,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13433
14157
|
} catch (err) {
|
|
13434
14158
|
recordExtractionError("laravel eloquent extraction", file.path, err);
|
|
13435
14159
|
}
|
|
14160
|
+
try {
|
|
14161
|
+
endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
|
|
14162
|
+
} catch (err) {
|
|
14163
|
+
recordExtractionError("efcore data-axis extraction", file.path, err);
|
|
14164
|
+
}
|
|
13436
14165
|
}
|
|
13437
14166
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
13438
14167
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
@@ -13443,7 +14172,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13443
14172
|
if (!graph.hasNode(ep.infraId)) {
|
|
13444
14173
|
const node = {
|
|
13445
14174
|
id: ep.infraId,
|
|
13446
|
-
type:
|
|
14175
|
+
type: import_types44.NodeType.InfraNode,
|
|
13447
14176
|
name: ep.name,
|
|
13448
14177
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
13449
14178
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -13456,21 +14185,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13456
14185
|
}
|
|
13457
14186
|
if (ep.columns && ep.columns.length > 0) {
|
|
13458
14187
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13459
|
-
if (node.type ===
|
|
14188
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13460
14189
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13461
14190
|
...node,
|
|
13462
14191
|
columns: foldColumns(
|
|
13463
14192
|
node.columns,
|
|
13464
14193
|
ep.columns,
|
|
13465
|
-
|
|
13466
|
-
(0,
|
|
14194
|
+
import_types44.Provenance.EXTRACTED,
|
|
14195
|
+
(0, import_types44.confidenceForExtracted)(ep.confidenceKind)
|
|
13467
14196
|
)
|
|
13468
14197
|
});
|
|
13469
14198
|
}
|
|
13470
14199
|
}
|
|
13471
14200
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
13472
14201
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
13473
|
-
if (node.type ===
|
|
14202
|
+
if (node.type === import_types44.NodeType.InfraNode) {
|
|
13474
14203
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
13475
14204
|
...node,
|
|
13476
14205
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -13478,7 +14207,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13478
14207
|
}
|
|
13479
14208
|
}
|
|
13480
14209
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
13481
|
-
const confidence = (0,
|
|
14210
|
+
const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
|
|
13482
14211
|
const relFile = toPosix(ep.evidence.file);
|
|
13483
14212
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
13484
14213
|
graph,
|
|
@@ -13488,7 +14217,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13488
14217
|
);
|
|
13489
14218
|
nodesAdded += n;
|
|
13490
14219
|
edgesAdded += e;
|
|
13491
|
-
if (!(0,
|
|
14220
|
+
if (!(0, import_types44.passesExtractedFloor)(confidence)) {
|
|
13492
14221
|
noteExtractedDropped({
|
|
13493
14222
|
source: fileNodeId,
|
|
13494
14223
|
target: ep.infraId,
|
|
@@ -13508,7 +14237,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
13508
14237
|
source: fileNodeId,
|
|
13509
14238
|
target: ep.infraId,
|
|
13510
14239
|
type: edgeType,
|
|
13511
|
-
provenance:
|
|
14240
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
13512
14241
|
confidence,
|
|
13513
14242
|
evidence: ep.evidence
|
|
13514
14243
|
};
|
|
@@ -13531,7 +14260,7 @@ async function addCallEdges(graph, services) {
|
|
|
13531
14260
|
|
|
13532
14261
|
// src/extract/table-edges.ts
|
|
13533
14262
|
init_cjs_shims();
|
|
13534
|
-
var
|
|
14263
|
+
var import_types45 = require("@neat.is/types");
|
|
13535
14264
|
async function addTableEdges(graph, services) {
|
|
13536
14265
|
let nodesAdded = 0;
|
|
13537
14266
|
let edgesAdded = 0;
|
|
@@ -13559,20 +14288,20 @@ async function addTableEdges(graph, services) {
|
|
|
13559
14288
|
}
|
|
13560
14289
|
refs.push(...modelRefs);
|
|
13561
14290
|
for (const ref of refs) {
|
|
13562
|
-
const childId = (0,
|
|
13563
|
-
const parentId = (0,
|
|
14291
|
+
const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
|
|
14292
|
+
const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
|
|
13564
14293
|
if (childId === parentId) continue;
|
|
13565
14294
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
13566
14295
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
13567
|
-
const edgeId = (0,
|
|
14296
|
+
const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
|
|
13568
14297
|
if (graph.hasEdge(edgeId)) continue;
|
|
13569
14298
|
const edge = {
|
|
13570
14299
|
id: edgeId,
|
|
13571
14300
|
source: childId,
|
|
13572
14301
|
target: parentId,
|
|
13573
|
-
type:
|
|
13574
|
-
provenance:
|
|
13575
|
-
confidence: (0,
|
|
14302
|
+
type: import_types45.EdgeType.REFERENCES,
|
|
14303
|
+
provenance: import_types45.Provenance.EXTRACTED,
|
|
14304
|
+
confidence: (0, import_types45.confidenceForExtracted)("structural"),
|
|
13576
14305
|
evidence: ref.evidence
|
|
13577
14306
|
};
|
|
13578
14307
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -13585,7 +14314,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
13585
14314
|
if (graph.hasNode(id)) return 0;
|
|
13586
14315
|
const node = {
|
|
13587
14316
|
id,
|
|
13588
|
-
type:
|
|
14317
|
+
type: import_types45.NodeType.InfraNode,
|
|
13589
14318
|
name,
|
|
13590
14319
|
provider: "self",
|
|
13591
14320
|
kind: "sql-table"
|
|
@@ -13599,16 +14328,16 @@ init_cjs_shims();
|
|
|
13599
14328
|
|
|
13600
14329
|
// src/extract/infra/docker-compose.ts
|
|
13601
14330
|
init_cjs_shims();
|
|
13602
|
-
var
|
|
13603
|
-
var
|
|
14331
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
14332
|
+
var import_types47 = require("@neat.is/types");
|
|
13604
14333
|
|
|
13605
14334
|
// src/extract/infra/shared.ts
|
|
13606
14335
|
init_cjs_shims();
|
|
13607
|
-
var
|
|
14336
|
+
var import_types46 = require("@neat.is/types");
|
|
13608
14337
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
13609
14338
|
return {
|
|
13610
|
-
id: (0,
|
|
13611
|
-
type:
|
|
14339
|
+
id: (0, import_types46.infraId)(kind, name),
|
|
14340
|
+
type: import_types46.NodeType.InfraNode,
|
|
13612
14341
|
name,
|
|
13613
14342
|
provider,
|
|
13614
14343
|
kind,
|
|
@@ -13652,8 +14381,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
13652
14381
|
source: anchorId,
|
|
13653
14382
|
target: node.id,
|
|
13654
14383
|
type: edgeType,
|
|
13655
|
-
provenance:
|
|
13656
|
-
confidence: (0,
|
|
14384
|
+
provenance: import_types46.Provenance.EXTRACTED,
|
|
14385
|
+
confidence: (0, import_types46.confidenceForExtracted)("structural"),
|
|
13657
14386
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
13658
14387
|
};
|
|
13659
14388
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13670,7 +14399,7 @@ function dependsOnList(value) {
|
|
|
13670
14399
|
}
|
|
13671
14400
|
function serviceNameToServiceNode(name, services) {
|
|
13672
14401
|
for (const s of services) {
|
|
13673
|
-
if (s.node.name === name ||
|
|
14402
|
+
if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
|
|
13674
14403
|
}
|
|
13675
14404
|
return null;
|
|
13676
14405
|
}
|
|
@@ -13679,7 +14408,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13679
14408
|
let edgesAdded = 0;
|
|
13680
14409
|
let composePath = null;
|
|
13681
14410
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
13682
|
-
const abs =
|
|
14411
|
+
const abs = import_node_path57.default.join(scanPath, name);
|
|
13683
14412
|
if (await exists(abs)) {
|
|
13684
14413
|
composePath = abs;
|
|
13685
14414
|
break;
|
|
@@ -13692,13 +14421,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13692
14421
|
} catch (err) {
|
|
13693
14422
|
recordExtractionError(
|
|
13694
14423
|
"infra docker-compose",
|
|
13695
|
-
|
|
14424
|
+
import_node_path57.default.relative(scanPath, composePath),
|
|
13696
14425
|
err
|
|
13697
14426
|
);
|
|
13698
14427
|
return { nodesAdded, edgesAdded };
|
|
13699
14428
|
}
|
|
13700
14429
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
13701
|
-
const evidenceFile =
|
|
14430
|
+
const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
|
|
13702
14431
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
13703
14432
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
13704
14433
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -13720,15 +14449,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13720
14449
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
13721
14450
|
const targetId = composeNameToNodeId.get(dep);
|
|
13722
14451
|
if (!targetId) continue;
|
|
13723
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
14452
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
|
|
13724
14453
|
if (graph.hasEdge(edgeId)) continue;
|
|
13725
14454
|
const edge = {
|
|
13726
14455
|
id: edgeId,
|
|
13727
14456
|
source: sourceId,
|
|
13728
14457
|
target: targetId,
|
|
13729
|
-
type:
|
|
13730
|
-
provenance:
|
|
13731
|
-
confidence: (0,
|
|
14458
|
+
type: import_types47.EdgeType.DEPENDS_ON,
|
|
14459
|
+
provenance: import_types47.Provenance.EXTRACTED,
|
|
14460
|
+
confidence: (0, import_types47.confidenceForExtracted)("structural"),
|
|
13732
14461
|
evidence: { file: evidenceFile }
|
|
13733
14462
|
};
|
|
13734
14463
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13740,9 +14469,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
13740
14469
|
|
|
13741
14470
|
// src/extract/infra/dockerfile.ts
|
|
13742
14471
|
init_cjs_shims();
|
|
13743
|
-
var
|
|
13744
|
-
var
|
|
13745
|
-
var
|
|
14472
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14473
|
+
var import_node_fs25 = require("fs");
|
|
14474
|
+
var import_types48 = require("@neat.is/types");
|
|
13746
14475
|
function readDockerfile(content) {
|
|
13747
14476
|
let image = null;
|
|
13748
14477
|
const ports = [];
|
|
@@ -13771,15 +14500,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13771
14500
|
let nodesAdded = 0;
|
|
13772
14501
|
let edgesAdded = 0;
|
|
13773
14502
|
for (const service of services) {
|
|
13774
|
-
const dockerfilePath =
|
|
14503
|
+
const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
|
|
13775
14504
|
if (!await exists(dockerfilePath)) continue;
|
|
13776
14505
|
let content;
|
|
13777
14506
|
try {
|
|
13778
|
-
content = await
|
|
14507
|
+
content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
|
|
13779
14508
|
} catch (err) {
|
|
13780
14509
|
recordExtractionError(
|
|
13781
14510
|
"infra dockerfile",
|
|
13782
|
-
|
|
14511
|
+
import_node_path58.default.relative(scanPath, dockerfilePath),
|
|
13783
14512
|
err
|
|
13784
14513
|
);
|
|
13785
14514
|
continue;
|
|
@@ -13791,8 +14520,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13791
14520
|
graph.addNode(node.id, node);
|
|
13792
14521
|
nodesAdded++;
|
|
13793
14522
|
}
|
|
13794
|
-
const relDockerfile = toPosix(
|
|
13795
|
-
const evidenceFile = toPosix(
|
|
14523
|
+
const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
|
|
14524
|
+
const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
|
|
13796
14525
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
13797
14526
|
graph,
|
|
13798
14527
|
service.pkg.name,
|
|
@@ -13801,15 +14530,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13801
14530
|
);
|
|
13802
14531
|
nodesAdded += fn;
|
|
13803
14532
|
edgesAdded += fe;
|
|
13804
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
14533
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
|
|
13805
14534
|
if (!graph.hasEdge(edgeId)) {
|
|
13806
14535
|
const edge = {
|
|
13807
14536
|
id: edgeId,
|
|
13808
14537
|
source: fileNodeId,
|
|
13809
14538
|
target: node.id,
|
|
13810
|
-
type:
|
|
13811
|
-
provenance:
|
|
13812
|
-
confidence: (0,
|
|
14539
|
+
type: import_types48.EdgeType.RUNS_ON,
|
|
14540
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14541
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13813
14542
|
evidence: {
|
|
13814
14543
|
file: evidenceFile,
|
|
13815
14544
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -13824,15 +14553,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13824
14553
|
graph.addNode(portNode.id, portNode);
|
|
13825
14554
|
nodesAdded++;
|
|
13826
14555
|
}
|
|
13827
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
14556
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
|
|
13828
14557
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
13829
14558
|
const portEdge = {
|
|
13830
14559
|
id: portEdgeId,
|
|
13831
14560
|
source: fileNodeId,
|
|
13832
14561
|
target: portNode.id,
|
|
13833
|
-
type:
|
|
13834
|
-
provenance:
|
|
13835
|
-
confidence: (0,
|
|
14562
|
+
type: import_types48.EdgeType.CONNECTS_TO,
|
|
14563
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
14564
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
13836
14565
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
13837
14566
|
};
|
|
13838
14567
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -13844,23 +14573,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
13844
14573
|
|
|
13845
14574
|
// src/extract/infra/terraform.ts
|
|
13846
14575
|
init_cjs_shims();
|
|
13847
|
-
var
|
|
13848
|
-
var
|
|
13849
|
-
var
|
|
14576
|
+
var import_node_fs26 = require("fs");
|
|
14577
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14578
|
+
var import_types49 = require("@neat.is/types");
|
|
13850
14579
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
13851
14580
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
13852
14581
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
13853
14582
|
if (depth > max) return [];
|
|
13854
14583
|
const out = [];
|
|
13855
|
-
const entries = await
|
|
14584
|
+
const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13856
14585
|
for (const entry2 of entries) {
|
|
13857
14586
|
if (entry2.isDirectory()) {
|
|
13858
14587
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
13859
|
-
const child =
|
|
14588
|
+
const child = import_node_path59.default.join(start, entry2.name);
|
|
13860
14589
|
if (await isPythonVenvDir(child)) continue;
|
|
13861
14590
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
13862
14591
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
13863
|
-
out.push(
|
|
14592
|
+
out.push(import_node_path59.default.join(start, entry2.name));
|
|
13864
14593
|
}
|
|
13865
14594
|
}
|
|
13866
14595
|
return out;
|
|
@@ -13891,8 +14620,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13891
14620
|
let edgesAdded = 0;
|
|
13892
14621
|
const files = await walkTfFiles(scanPath);
|
|
13893
14622
|
for (const file of files) {
|
|
13894
|
-
const content = await
|
|
13895
|
-
const evidenceFile = toPosix(
|
|
14623
|
+
const content = await import_node_fs26.promises.readFile(file, "utf8");
|
|
14624
|
+
const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
|
|
13896
14625
|
const resources = [];
|
|
13897
14626
|
const byKey = /* @__PURE__ */ new Map();
|
|
13898
14627
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -13927,16 +14656,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13927
14656
|
if (!target) continue;
|
|
13928
14657
|
if (seen.has(target.nodeId)) continue;
|
|
13929
14658
|
seen.add(target.nodeId);
|
|
13930
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
14659
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
|
|
13931
14660
|
if (graph.hasEdge(edgeId)) continue;
|
|
13932
14661
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
13933
14662
|
const edge = {
|
|
13934
14663
|
id: edgeId,
|
|
13935
14664
|
source: resource.nodeId,
|
|
13936
14665
|
target: target.nodeId,
|
|
13937
|
-
type:
|
|
13938
|
-
provenance:
|
|
13939
|
-
confidence: (0,
|
|
14666
|
+
type: import_types49.EdgeType.DEPENDS_ON,
|
|
14667
|
+
provenance: import_types49.Provenance.EXTRACTED,
|
|
14668
|
+
confidence: (0, import_types49.confidenceForExtracted)("structural"),
|
|
13940
14669
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
13941
14670
|
};
|
|
13942
14671
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -13949,8 +14678,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
13949
14678
|
|
|
13950
14679
|
// src/extract/infra/k8s.ts
|
|
13951
14680
|
init_cjs_shims();
|
|
13952
|
-
var
|
|
13953
|
-
var
|
|
14681
|
+
var import_node_fs27 = require("fs");
|
|
14682
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
13954
14683
|
var import_yaml3 = require("yaml");
|
|
13955
14684
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
13956
14685
|
Service: "k8s-service",
|
|
@@ -13964,15 +14693,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
13964
14693
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
13965
14694
|
if (depth > max) return [];
|
|
13966
14695
|
const out = [];
|
|
13967
|
-
const entries = await
|
|
14696
|
+
const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
13968
14697
|
for (const entry2 of entries) {
|
|
13969
14698
|
if (entry2.isDirectory()) {
|
|
13970
14699
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
13971
|
-
const child =
|
|
14700
|
+
const child = import_node_path60.default.join(start, entry2.name);
|
|
13972
14701
|
if (await isPythonVenvDir(child)) continue;
|
|
13973
14702
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
13974
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
13975
|
-
out.push(
|
|
14703
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry2.name))) {
|
|
14704
|
+
out.push(import_node_path60.default.join(start, entry2.name));
|
|
13976
14705
|
}
|
|
13977
14706
|
}
|
|
13978
14707
|
return out;
|
|
@@ -13981,7 +14710,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
13981
14710
|
let nodesAdded = 0;
|
|
13982
14711
|
const files = await walkYamlFiles2(scanPath);
|
|
13983
14712
|
for (const file of files) {
|
|
13984
|
-
const content = await
|
|
14713
|
+
const content = await import_node_fs27.promises.readFile(file, "utf8");
|
|
13985
14714
|
let docs;
|
|
13986
14715
|
try {
|
|
13987
14716
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -14005,16 +14734,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
14005
14734
|
|
|
14006
14735
|
// src/extract/infra/cloudflare.ts
|
|
14007
14736
|
init_cjs_shims();
|
|
14008
|
-
var
|
|
14009
|
-
var
|
|
14737
|
+
var import_node_fs28 = require("fs");
|
|
14738
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
14010
14739
|
var import_smol_toml3 = require("smol-toml");
|
|
14011
|
-
var
|
|
14740
|
+
var import_types50 = require("@neat.is/types");
|
|
14012
14741
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
14013
14742
|
async function readWranglerConfig(dir) {
|
|
14014
14743
|
for (const filename of WRANGLER_FILENAMES) {
|
|
14015
|
-
const abs =
|
|
14744
|
+
const abs = import_node_path61.default.join(dir, filename);
|
|
14016
14745
|
if (!await exists(abs)) continue;
|
|
14017
|
-
const raw = await
|
|
14746
|
+
const raw = await import_node_fs28.promises.readFile(abs, "utf8");
|
|
14018
14747
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14019
14748
|
return { config, relFile: filename, raw };
|
|
14020
14749
|
}
|
|
@@ -14056,8 +14785,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
14056
14785
|
source: anchorId,
|
|
14057
14786
|
target: node.id,
|
|
14058
14787
|
type: edgeType,
|
|
14059
|
-
provenance:
|
|
14060
|
-
confidence: (0,
|
|
14788
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14789
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14061
14790
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
14062
14791
|
};
|
|
14063
14792
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14075,11 +14804,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14075
14804
|
try {
|
|
14076
14805
|
read = await readWranglerConfig(service.dir);
|
|
14077
14806
|
} catch (err) {
|
|
14078
|
-
recordExtractionError("infra cloudflare",
|
|
14807
|
+
recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
|
|
14079
14808
|
continue;
|
|
14080
14809
|
}
|
|
14081
14810
|
if (!read || !read.config.name) continue;
|
|
14082
|
-
const evidenceFile = toPosix(
|
|
14811
|
+
const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
|
|
14083
14812
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
14084
14813
|
}
|
|
14085
14814
|
for (const worker of discovered) {
|
|
@@ -14091,7 +14820,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14091
14820
|
}
|
|
14092
14821
|
let anchorId = service.node.id;
|
|
14093
14822
|
if (config.main) {
|
|
14094
|
-
const entryRelPath = toPosix(
|
|
14823
|
+
const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
|
|
14095
14824
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
14096
14825
|
graph,
|
|
14097
14826
|
service.pkg.name,
|
|
@@ -14118,15 +14847,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14118
14847
|
nodesAdded++;
|
|
14119
14848
|
}
|
|
14120
14849
|
if (runtimeNode.id !== anchorId) {
|
|
14121
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
14850
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
|
|
14122
14851
|
if (!graph.hasEdge(runsOnId)) {
|
|
14123
14852
|
const edge = {
|
|
14124
14853
|
id: runsOnId,
|
|
14125
14854
|
source: anchorId,
|
|
14126
14855
|
target: runtimeNode.id,
|
|
14127
|
-
type:
|
|
14128
|
-
provenance:
|
|
14129
|
-
confidence: (0,
|
|
14856
|
+
type: import_types50.EdgeType.RUNS_ON,
|
|
14857
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14858
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14130
14859
|
evidence: {
|
|
14131
14860
|
file: evidenceFile,
|
|
14132
14861
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -14140,7 +14869,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14140
14869
|
const result = addResourceEdge(
|
|
14141
14870
|
graph,
|
|
14142
14871
|
anchorId,
|
|
14143
|
-
|
|
14872
|
+
import_types50.EdgeType.CONNECTS_TO,
|
|
14144
14873
|
"cloudflare-route",
|
|
14145
14874
|
route,
|
|
14146
14875
|
evidenceFile,
|
|
@@ -14164,7 +14893,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14164
14893
|
const result = addResourceEdge(
|
|
14165
14894
|
graph,
|
|
14166
14895
|
anchorId,
|
|
14167
|
-
|
|
14896
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14168
14897
|
group.kind,
|
|
14169
14898
|
name,
|
|
14170
14899
|
evidenceFile,
|
|
@@ -14178,7 +14907,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14178
14907
|
const result = addResourceEdge(
|
|
14179
14908
|
graph,
|
|
14180
14909
|
anchorId,
|
|
14181
|
-
|
|
14910
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14182
14911
|
"cloudflare-cron",
|
|
14183
14912
|
cron,
|
|
14184
14913
|
evidenceFile,
|
|
@@ -14191,7 +14920,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14191
14920
|
const result = addResourceEdge(
|
|
14192
14921
|
graph,
|
|
14193
14922
|
anchorId,
|
|
14194
|
-
|
|
14923
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14195
14924
|
"cloudflare-env-var",
|
|
14196
14925
|
varName,
|
|
14197
14926
|
evidenceFile,
|
|
@@ -14204,15 +14933,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14204
14933
|
if (!svc.service) continue;
|
|
14205
14934
|
const target = workerIndex.get(svc.service);
|
|
14206
14935
|
if (target && target.anchorId !== anchorId) {
|
|
14207
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
14936
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
|
|
14208
14937
|
if (!graph.hasEdge(edgeId)) {
|
|
14209
14938
|
const edge = {
|
|
14210
14939
|
id: edgeId,
|
|
14211
14940
|
source: anchorId,
|
|
14212
14941
|
target: target.anchorId,
|
|
14213
|
-
type:
|
|
14214
|
-
provenance:
|
|
14215
|
-
confidence: (0,
|
|
14942
|
+
type: import_types50.EdgeType.CALLS,
|
|
14943
|
+
provenance: import_types50.Provenance.EXTRACTED,
|
|
14944
|
+
confidence: (0, import_types50.confidenceForExtracted)("structural"),
|
|
14216
14945
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
14217
14946
|
};
|
|
14218
14947
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -14223,7 +14952,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14223
14952
|
const result = addResourceEdge(
|
|
14224
14953
|
graph,
|
|
14225
14954
|
anchorId,
|
|
14226
|
-
|
|
14955
|
+
import_types50.EdgeType.DEPENDS_ON,
|
|
14227
14956
|
"cloudflare-service-binding",
|
|
14228
14957
|
svc.service,
|
|
14229
14958
|
evidenceFile,
|
|
@@ -14238,24 +14967,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
14238
14967
|
|
|
14239
14968
|
// src/extract/infra/vercel.ts
|
|
14240
14969
|
init_cjs_shims();
|
|
14241
|
-
var
|
|
14242
|
-
var
|
|
14243
|
-
var
|
|
14970
|
+
var import_node_fs29 = require("fs");
|
|
14971
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
14972
|
+
var import_types51 = require("@neat.is/types");
|
|
14244
14973
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
14245
14974
|
async function readVercelConfig(dir) {
|
|
14246
14975
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
14247
|
-
const abs =
|
|
14976
|
+
const abs = import_node_path62.default.join(dir, filename);
|
|
14248
14977
|
if (!await exists(abs)) continue;
|
|
14249
|
-
const raw = await
|
|
14978
|
+
const raw = await import_node_fs29.promises.readFile(abs, "utf8");
|
|
14250
14979
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
14251
14980
|
return { config, relFile: filename, raw };
|
|
14252
14981
|
}
|
|
14253
14982
|
return null;
|
|
14254
14983
|
}
|
|
14255
14984
|
async function readLinkedProjectName(dir) {
|
|
14256
|
-
const abs =
|
|
14985
|
+
const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
|
|
14257
14986
|
if (!await exists(abs)) return void 0;
|
|
14258
|
-
const parsed = JSON.parse(await
|
|
14987
|
+
const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
|
|
14259
14988
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
14260
14989
|
}
|
|
14261
14990
|
function routeSource(route) {
|
|
@@ -14271,7 +15000,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14271
15000
|
read = await readVercelConfig(service.dir);
|
|
14272
15001
|
projectName = await readLinkedProjectName(service.dir);
|
|
14273
15002
|
} catch (err) {
|
|
14274
|
-
recordExtractionError("infra vercel",
|
|
15003
|
+
recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
|
|
14275
15004
|
continue;
|
|
14276
15005
|
}
|
|
14277
15006
|
if (!read && !projectName) continue;
|
|
@@ -14287,7 +15016,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14287
15016
|
const anchorId = service.node.id;
|
|
14288
15017
|
if (!read) continue;
|
|
14289
15018
|
const { config, relFile, raw } = read;
|
|
14290
|
-
const evidenceFile = toPosix(
|
|
15019
|
+
const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
|
|
14291
15020
|
const add = (edgeType, kind, name) => {
|
|
14292
15021
|
if (!name) return;
|
|
14293
15022
|
const result = emitPlatformResourceEdge(
|
|
@@ -14303,12 +15032,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14303
15032
|
nodesAdded += result.nodesAdded;
|
|
14304
15033
|
edgesAdded += result.edgesAdded;
|
|
14305
15034
|
};
|
|
14306
|
-
add(
|
|
14307
|
-
for (const cron of config.crons ?? []) add(
|
|
14308
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
14309
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
15035
|
+
add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
15036
|
+
for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
15037
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
15038
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
14310
15039
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
14311
|
-
add(
|
|
15040
|
+
add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
14312
15041
|
}
|
|
14313
15042
|
}
|
|
14314
15043
|
return { nodesAdded, edgesAdded };
|
|
@@ -14316,16 +15045,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
14316
15045
|
|
|
14317
15046
|
// src/extract/infra/railway.ts
|
|
14318
15047
|
init_cjs_shims();
|
|
14319
|
-
var
|
|
14320
|
-
var
|
|
15048
|
+
var import_node_fs30 = require("fs");
|
|
15049
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
14321
15050
|
var import_smol_toml4 = require("smol-toml");
|
|
14322
|
-
var
|
|
15051
|
+
var import_types52 = require("@neat.is/types");
|
|
14323
15052
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
14324
15053
|
async function readRailwayConfig(dir) {
|
|
14325
15054
|
for (const filename of RAILWAY_FILENAMES) {
|
|
14326
|
-
const abs =
|
|
15055
|
+
const abs = import_node_path63.default.join(dir, filename);
|
|
14327
15056
|
if (!await exists(abs)) continue;
|
|
14328
|
-
const raw = await
|
|
15057
|
+
const raw = await import_node_fs30.promises.readFile(abs, "utf8");
|
|
14329
15058
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
14330
15059
|
return { config, relFile: filename, raw };
|
|
14331
15060
|
}
|
|
@@ -14339,7 +15068,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14339
15068
|
try {
|
|
14340
15069
|
read = await readRailwayConfig(service.dir);
|
|
14341
15070
|
} catch (err) {
|
|
14342
|
-
recordExtractionError("infra railway",
|
|
15071
|
+
recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
|
|
14343
15072
|
continue;
|
|
14344
15073
|
}
|
|
14345
15074
|
if (!read) continue;
|
|
@@ -14349,7 +15078,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14349
15078
|
}
|
|
14350
15079
|
const anchorId = service.node.id;
|
|
14351
15080
|
const { config, relFile, raw } = read;
|
|
14352
|
-
const evidenceFile = toPosix(
|
|
15081
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
|
|
14353
15082
|
const add = (edgeType, kind, name) => {
|
|
14354
15083
|
if (!name) return;
|
|
14355
15084
|
const result = emitPlatformResourceEdge(
|
|
@@ -14365,24 +15094,24 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
14365
15094
|
nodesAdded += result.nodesAdded;
|
|
14366
15095
|
edgesAdded += result.edgesAdded;
|
|
14367
15096
|
};
|
|
14368
|
-
add(
|
|
14369
|
-
add(
|
|
14370
|
-
add(
|
|
15097
|
+
add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
|
|
15098
|
+
add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
15099
|
+
add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
14371
15100
|
}
|
|
14372
15101
|
return { nodesAdded, edgesAdded };
|
|
14373
15102
|
}
|
|
14374
15103
|
|
|
14375
15104
|
// src/extract/infra/supabase.ts
|
|
14376
15105
|
init_cjs_shims();
|
|
14377
|
-
var
|
|
14378
|
-
var
|
|
15106
|
+
var import_node_fs31 = require("fs");
|
|
15107
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
14379
15108
|
var import_smol_toml5 = require("smol-toml");
|
|
14380
|
-
var
|
|
15109
|
+
var import_types53 = require("@neat.is/types");
|
|
14381
15110
|
async function readSupabaseConfig(dir) {
|
|
14382
|
-
const relFile =
|
|
14383
|
-
const abs =
|
|
15111
|
+
const relFile = import_node_path64.default.join("supabase", "config.toml");
|
|
15112
|
+
const abs = import_node_path64.default.join(dir, relFile);
|
|
14384
15113
|
if (!await exists(abs)) return null;
|
|
14385
|
-
const raw = await
|
|
15114
|
+
const raw = await import_node_fs31.promises.readFile(abs, "utf8");
|
|
14386
15115
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
14387
15116
|
return { config, relFile, raw };
|
|
14388
15117
|
}
|
|
@@ -14394,7 +15123,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14394
15123
|
try {
|
|
14395
15124
|
read = await readSupabaseConfig(service.dir);
|
|
14396
15125
|
} catch (err) {
|
|
14397
|
-
recordExtractionError("infra supabase",
|
|
15126
|
+
recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
|
|
14398
15127
|
continue;
|
|
14399
15128
|
}
|
|
14400
15129
|
if (!read) continue;
|
|
@@ -14409,7 +15138,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14409
15138
|
});
|
|
14410
15139
|
}
|
|
14411
15140
|
const anchorId = service.node.id;
|
|
14412
|
-
const evidenceFile = toPosix(
|
|
15141
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
|
|
14413
15142
|
const add = (edgeType, kind, name) => {
|
|
14414
15143
|
if (!name) return;
|
|
14415
15144
|
const result = emitPlatformResourceEdge(
|
|
@@ -14425,10 +15154,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
14425
15154
|
nodesAdded += result.nodesAdded;
|
|
14426
15155
|
edgesAdded += result.edgesAdded;
|
|
14427
15156
|
};
|
|
14428
|
-
add(
|
|
14429
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
14430
|
-
if (config.storage) add(
|
|
14431
|
-
if (config.auth) add(
|
|
15157
|
+
add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
15158
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
15159
|
+
if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
15160
|
+
if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
14432
15161
|
}
|
|
14433
15162
|
return { nodesAdded, edgesAdded };
|
|
14434
15163
|
}
|
|
@@ -14451,14 +15180,14 @@ async function addInfra(graph, scanPath, services) {
|
|
|
14451
15180
|
|
|
14452
15181
|
// src/extract/zod-shapes.ts
|
|
14453
15182
|
init_cjs_shims();
|
|
14454
|
-
var
|
|
14455
|
-
var
|
|
15183
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15184
|
+
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
14456
15185
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
14457
|
-
var
|
|
15186
|
+
var import_types54 = require("@neat.is/types");
|
|
14458
15187
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
14459
15188
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
14460
15189
|
function parserForExt4(ext) {
|
|
14461
|
-
const p = new
|
|
15190
|
+
const p = new import_tree_sitter17.default();
|
|
14462
15191
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
14463
15192
|
return p;
|
|
14464
15193
|
}
|
|
@@ -14546,7 +15275,7 @@ function topLevelSchemas(root) {
|
|
|
14546
15275
|
}
|
|
14547
15276
|
function zodShapesFromFile(file, serviceDir) {
|
|
14548
15277
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
14549
|
-
const tree = parseSource3(parserForExt4(
|
|
15278
|
+
const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
|
|
14550
15279
|
const out = [];
|
|
14551
15280
|
const seen = /* @__PURE__ */ new Set();
|
|
14552
15281
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14560,11 +15289,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
14560
15289
|
seen.add(name);
|
|
14561
15290
|
const line = call.startPosition.row + 1;
|
|
14562
15291
|
out.push({
|
|
14563
|
-
infraId: (0,
|
|
15292
|
+
infraId: (0, import_types54.infraId)("zod-schema", name),
|
|
14564
15293
|
name,
|
|
14565
15294
|
fields,
|
|
14566
15295
|
evidence: {
|
|
14567
|
-
file:
|
|
15296
|
+
file: import_node_path65.default.relative(serviceDir, file.path),
|
|
14568
15297
|
line,
|
|
14569
15298
|
snippet: snippet(file.content, line)
|
|
14570
15299
|
}
|
|
@@ -14595,7 +15324,7 @@ async function addZodShapes(graph, services) {
|
|
|
14595
15324
|
if (!graph.hasNode(shape.infraId)) {
|
|
14596
15325
|
const node = {
|
|
14597
15326
|
id: shape.infraId,
|
|
14598
|
-
type:
|
|
15327
|
+
type: import_types54.NodeType.InfraNode,
|
|
14599
15328
|
name: shape.name,
|
|
14600
15329
|
provider: "self",
|
|
14601
15330
|
kind: "zod-schema"
|
|
@@ -14605,14 +15334,14 @@ async function addZodShapes(graph, services) {
|
|
|
14605
15334
|
}
|
|
14606
15335
|
if (shape.fields.length > 0) {
|
|
14607
15336
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
14608
|
-
if (node.type ===
|
|
15337
|
+
if (node.type === import_types54.NodeType.InfraNode) {
|
|
14609
15338
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
14610
15339
|
...node,
|
|
14611
15340
|
columns: foldColumns(
|
|
14612
15341
|
node.columns,
|
|
14613
15342
|
shape.fields,
|
|
14614
|
-
|
|
14615
|
-
(0,
|
|
15343
|
+
import_types54.Provenance.EXTRACTED,
|
|
15344
|
+
(0, import_types54.confidenceForExtracted)("structural")
|
|
14616
15345
|
)
|
|
14617
15346
|
});
|
|
14618
15347
|
}
|
|
@@ -14626,15 +15355,15 @@ async function addZodShapes(graph, services) {
|
|
|
14626
15355
|
);
|
|
14627
15356
|
nodesAdded += n;
|
|
14628
15357
|
edgesAdded += e;
|
|
14629
|
-
const edgeId = (0,
|
|
15358
|
+
const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
|
|
14630
15359
|
if (!graph.hasEdge(edgeId)) {
|
|
14631
15360
|
const edge = {
|
|
14632
15361
|
id: edgeId,
|
|
14633
15362
|
source: fileNodeId,
|
|
14634
15363
|
target: shape.infraId,
|
|
14635
|
-
type:
|
|
14636
|
-
provenance:
|
|
14637
|
-
confidence: (0,
|
|
15364
|
+
type: import_types54.EdgeType.CONTAINS,
|
|
15365
|
+
provenance: import_types54.Provenance.EXTRACTED,
|
|
15366
|
+
confidence: (0, import_types54.confidenceForExtracted)("structural"),
|
|
14638
15367
|
evidence: shape.evidence
|
|
14639
15368
|
};
|
|
14640
15369
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -14648,7 +15377,7 @@ async function addZodShapes(graph, services) {
|
|
|
14648
15377
|
|
|
14649
15378
|
// src/extract/firestore-rules.ts
|
|
14650
15379
|
init_cjs_shims();
|
|
14651
|
-
var
|
|
15380
|
+
var import_types55 = require("@neat.is/types");
|
|
14652
15381
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
14653
15382
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
14654
15383
|
function stripComments(src) {
|
|
@@ -14788,7 +15517,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
14788
15517
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
14789
15518
|
graph.forEachNode((id, attrs) => {
|
|
14790
15519
|
const node = attrs;
|
|
14791
|
-
if (node.type !==
|
|
15520
|
+
if (node.type !== import_types55.NodeType.InfraNode) return;
|
|
14792
15521
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
14793
15522
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
14794
15523
|
if (!fields || fields.size === 0) return;
|
|
@@ -14801,17 +15530,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
14801
15530
|
}
|
|
14802
15531
|
|
|
14803
15532
|
// src/extract/index.ts
|
|
14804
|
-
var
|
|
15533
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
14805
15534
|
|
|
14806
15535
|
// src/extract/retire.ts
|
|
14807
15536
|
init_cjs_shims();
|
|
14808
|
-
var
|
|
14809
|
-
var
|
|
14810
|
-
var
|
|
15537
|
+
var import_node_fs32 = require("fs");
|
|
15538
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
15539
|
+
var import_types56 = require("@neat.is/types");
|
|
14811
15540
|
function dropOrphanedFileNodes(graph) {
|
|
14812
15541
|
const orphans = [];
|
|
14813
15542
|
graph.forEachNode((id, attrs) => {
|
|
14814
|
-
if (attrs.type !==
|
|
15543
|
+
if (attrs.type !== import_types56.NodeType.FileNode) return;
|
|
14815
15544
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
14816
15545
|
orphans.push(id);
|
|
14817
15546
|
}
|
|
@@ -14824,14 +15553,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
14824
15553
|
const bases = [scanPath, ...serviceDirs];
|
|
14825
15554
|
graph.forEachEdge((id, attrs) => {
|
|
14826
15555
|
const edge = attrs;
|
|
14827
|
-
if (edge.provenance !==
|
|
15556
|
+
if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
|
|
14828
15557
|
const evidenceFile = edge.evidence?.file;
|
|
14829
15558
|
if (!evidenceFile) return;
|
|
14830
|
-
if (
|
|
14831
|
-
if (!(0,
|
|
15559
|
+
if (import_node_path66.default.isAbsolute(evidenceFile)) {
|
|
15560
|
+
if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
|
|
14832
15561
|
return;
|
|
14833
15562
|
}
|
|
14834
|
-
const found = bases.some((base) => (0,
|
|
15563
|
+
const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
|
|
14835
15564
|
if (!found) toDrop.push(id);
|
|
14836
15565
|
});
|
|
14837
15566
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -14888,7 +15617,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14888
15617
|
}
|
|
14889
15618
|
const droppedEntries = drainDroppedExtracted();
|
|
14890
15619
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
14891
|
-
const rejectedPath =
|
|
15620
|
+
const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
14892
15621
|
try {
|
|
14893
15622
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
14894
15623
|
} catch (err) {
|
|
@@ -14922,9 +15651,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
14922
15651
|
|
|
14923
15652
|
// src/persist.ts
|
|
14924
15653
|
init_cjs_shims();
|
|
14925
|
-
var
|
|
14926
|
-
var
|
|
14927
|
-
var
|
|
15654
|
+
var import_node_fs33 = require("fs");
|
|
15655
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
15656
|
+
var import_types57 = require("@neat.is/types");
|
|
14928
15657
|
var SCHEMA_VERSION = 7;
|
|
14929
15658
|
function migrateV1ToV2(payload) {
|
|
14930
15659
|
const nodes = payload.graph.nodes;
|
|
@@ -14948,7 +15677,7 @@ function migrateV5ToV6(payload) {
|
|
|
14948
15677
|
if (Array.isArray(nodes)) {
|
|
14949
15678
|
for (const node of nodes) {
|
|
14950
15679
|
const attrs = node.attributes;
|
|
14951
|
-
if (!attrs || attrs.type !==
|
|
15680
|
+
if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
|
|
14952
15681
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
14953
15682
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
14954
15683
|
}
|
|
@@ -14964,12 +15693,12 @@ function migrateV2ToV3(payload) {
|
|
|
14964
15693
|
for (const edge of edges) {
|
|
14965
15694
|
const attrs = edge.attributes;
|
|
14966
15695
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
14967
|
-
attrs.provenance =
|
|
15696
|
+
attrs.provenance = import_types57.Provenance.OBSERVED;
|
|
14968
15697
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
14969
15698
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
14970
15699
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
14971
15700
|
if (type && source && target) {
|
|
14972
|
-
const newId = (0,
|
|
15701
|
+
const newId = (0, import_types57.observedEdgeId)(source, target, type);
|
|
14973
15702
|
attrs.id = newId;
|
|
14974
15703
|
if (edge.key) edge.key = newId;
|
|
14975
15704
|
}
|
|
@@ -14978,7 +15707,7 @@ function migrateV2ToV3(payload) {
|
|
|
14978
15707
|
return { ...payload, schemaVersion: 3 };
|
|
14979
15708
|
}
|
|
14980
15709
|
async function ensureDir(filePath) {
|
|
14981
|
-
await
|
|
15710
|
+
await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
|
|
14982
15711
|
}
|
|
14983
15712
|
async function saveGraphToDisk(graph, outPath) {
|
|
14984
15713
|
await ensureDir(outPath);
|
|
@@ -14988,13 +15717,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
14988
15717
|
graph: graph.export()
|
|
14989
15718
|
};
|
|
14990
15719
|
const tmp = `${outPath}.tmp`;
|
|
14991
|
-
await
|
|
14992
|
-
await
|
|
15720
|
+
await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
15721
|
+
await import_node_fs33.promises.rename(tmp, outPath);
|
|
14993
15722
|
}
|
|
14994
15723
|
async function loadGraphFromDisk(graph, outPath) {
|
|
14995
15724
|
let raw;
|
|
14996
15725
|
try {
|
|
14997
|
-
raw = await
|
|
15726
|
+
raw = await import_node_fs33.promises.readFile(outPath, "utf8");
|
|
14998
15727
|
} catch (err) {
|
|
14999
15728
|
if (err.code === "ENOENT") return;
|
|
15000
15729
|
throw err;
|
|
@@ -15068,23 +15797,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
15068
15797
|
|
|
15069
15798
|
// src/projects.ts
|
|
15070
15799
|
init_cjs_shims();
|
|
15071
|
-
var
|
|
15800
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
15072
15801
|
function pathsForProject(project, baseDir) {
|
|
15073
15802
|
if (project === DEFAULT_PROJECT) {
|
|
15074
15803
|
return {
|
|
15075
|
-
snapshotPath:
|
|
15076
|
-
errorsPath:
|
|
15077
|
-
staleEventsPath:
|
|
15078
|
-
embeddingsCachePath:
|
|
15079
|
-
policyViolationsPath:
|
|
15804
|
+
snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
|
|
15805
|
+
errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
|
|
15806
|
+
staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
|
|
15807
|
+
embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
|
|
15808
|
+
policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
|
|
15080
15809
|
};
|
|
15081
15810
|
}
|
|
15082
15811
|
return {
|
|
15083
|
-
snapshotPath:
|
|
15084
|
-
errorsPath:
|
|
15085
|
-
staleEventsPath:
|
|
15086
|
-
embeddingsCachePath:
|
|
15087
|
-
policyViolationsPath:
|
|
15812
|
+
snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
|
|
15813
|
+
errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
|
|
15814
|
+
staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
15815
|
+
embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
|
|
15816
|
+
policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
15088
15817
|
};
|
|
15089
15818
|
}
|
|
15090
15819
|
var Projects = class {
|
|
@@ -15122,19 +15851,19 @@ var Projects = class {
|
|
|
15122
15851
|
init_cjs_shims();
|
|
15123
15852
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15124
15853
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15125
|
-
var
|
|
15854
|
+
var import_types92 = require("@neat.is/types");
|
|
15126
15855
|
|
|
15127
15856
|
// src/extend/index.ts
|
|
15128
15857
|
init_cjs_shims();
|
|
15129
|
-
var
|
|
15130
|
-
var
|
|
15858
|
+
var import_node_fs35 = require("fs");
|
|
15859
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
15131
15860
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
15132
15861
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
15133
15862
|
|
|
15134
15863
|
// src/installers/package-manager.ts
|
|
15135
15864
|
init_cjs_shims();
|
|
15136
|
-
var
|
|
15137
|
-
var
|
|
15865
|
+
var import_node_fs34 = require("fs");
|
|
15866
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
15138
15867
|
var import_node_child_process = require("child_process");
|
|
15139
15868
|
var LOCKFILE_PRIORITY = [
|
|
15140
15869
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -15149,29 +15878,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
15149
15878
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
15150
15879
|
async function exists2(p) {
|
|
15151
15880
|
try {
|
|
15152
|
-
await
|
|
15881
|
+
await import_node_fs34.promises.access(p);
|
|
15153
15882
|
return true;
|
|
15154
15883
|
} catch {
|
|
15155
15884
|
return false;
|
|
15156
15885
|
}
|
|
15157
15886
|
}
|
|
15158
15887
|
async function detectPackageManager(serviceDir) {
|
|
15159
|
-
let dir =
|
|
15888
|
+
let dir = import_node_path70.default.resolve(serviceDir);
|
|
15160
15889
|
const stops = /* @__PURE__ */ new Set();
|
|
15161
15890
|
for (let i = 0; i < 64; i++) {
|
|
15162
15891
|
if (stops.has(dir)) break;
|
|
15163
15892
|
stops.add(dir);
|
|
15164
15893
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
15165
|
-
const lockPath =
|
|
15894
|
+
const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
|
|
15166
15895
|
if (await exists2(lockPath)) {
|
|
15167
15896
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
15168
15897
|
}
|
|
15169
15898
|
}
|
|
15170
|
-
const parent =
|
|
15899
|
+
const parent = import_node_path70.default.dirname(dir);
|
|
15171
15900
|
if (parent === dir) break;
|
|
15172
15901
|
dir = parent;
|
|
15173
15902
|
}
|
|
15174
|
-
return { pm: "npm", cwd:
|
|
15903
|
+
return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
15175
15904
|
}
|
|
15176
15905
|
async function runPackageManagerInstall(cmd) {
|
|
15177
15906
|
return new Promise((resolve) => {
|
|
@@ -15213,15 +15942,15 @@ ${err.message}`
|
|
|
15213
15942
|
// src/extend/index.ts
|
|
15214
15943
|
async function fileExists2(p) {
|
|
15215
15944
|
try {
|
|
15216
|
-
await
|
|
15945
|
+
await import_node_fs35.promises.access(p);
|
|
15217
15946
|
return true;
|
|
15218
15947
|
} catch {
|
|
15219
15948
|
return false;
|
|
15220
15949
|
}
|
|
15221
15950
|
}
|
|
15222
15951
|
async function readPackageJson(scanPath) {
|
|
15223
|
-
const pkgPath =
|
|
15224
|
-
const raw = await
|
|
15952
|
+
const pkgPath = import_node_path71.default.join(scanPath, "package.json");
|
|
15953
|
+
const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
|
|
15225
15954
|
return JSON.parse(raw);
|
|
15226
15955
|
}
|
|
15227
15956
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -15234,27 +15963,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
15234
15963
|
]);
|
|
15235
15964
|
async function findHookFiles(scanPath) {
|
|
15236
15965
|
const found = [];
|
|
15237
|
-
const
|
|
15238
|
-
const entries = await
|
|
15966
|
+
const walk10 = async (dir) => {
|
|
15967
|
+
const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
15239
15968
|
for (const entry2 of entries) {
|
|
15240
15969
|
if (entry2.isDirectory()) {
|
|
15241
15970
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
15242
|
-
await
|
|
15971
|
+
await walk10(import_node_path71.default.join(dir, entry2.name));
|
|
15243
15972
|
} else if (entry2.isFile()) {
|
|
15244
15973
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
15245
|
-
const rel =
|
|
15246
|
-
found.push(rel.split(
|
|
15974
|
+
const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry2.name));
|
|
15975
|
+
found.push(rel.split(import_node_path71.default.sep).join("/"));
|
|
15247
15976
|
}
|
|
15248
15977
|
}
|
|
15249
15978
|
}
|
|
15250
15979
|
};
|
|
15251
|
-
await
|
|
15980
|
+
await walk10(scanPath);
|
|
15252
15981
|
return found.sort();
|
|
15253
15982
|
}
|
|
15254
15983
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
15255
15984
|
let fallback = null;
|
|
15256
15985
|
for (const file of hookFiles) {
|
|
15257
|
-
const content = await
|
|
15986
|
+
const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
|
|
15258
15987
|
const patched = splicedContent(content, snippet2);
|
|
15259
15988
|
if (patched !== null) return { file, content, patched };
|
|
15260
15989
|
if (fallback === null) fallback = { file, content };
|
|
@@ -15262,12 +15991,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
15262
15991
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
15263
15992
|
}
|
|
15264
15993
|
function extendLogPath() {
|
|
15265
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
15994
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
15266
15995
|
}
|
|
15267
15996
|
async function appendExtendLog(entry2) {
|
|
15268
15997
|
const logPath = extendLogPath();
|
|
15269
|
-
await
|
|
15270
|
-
await
|
|
15998
|
+
await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
|
|
15999
|
+
await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
15271
16000
|
}
|
|
15272
16001
|
function splicedContent(fileContent, snippet2) {
|
|
15273
16002
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -15325,7 +16054,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
15325
16054
|
}
|
|
15326
16055
|
async function describeProjectInstrumentation(ctx) {
|
|
15327
16056
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15328
|
-
const envNeat = await fileExists2(
|
|
16057
|
+
const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
|
|
15329
16058
|
const registryInstrPackages = new Set(
|
|
15330
16059
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
15331
16060
|
);
|
|
@@ -15347,7 +16076,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
15347
16076
|
);
|
|
15348
16077
|
}
|
|
15349
16078
|
for (const file of hookFiles) {
|
|
15350
|
-
const content = await
|
|
16079
|
+
const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
|
|
15351
16080
|
if (content.includes(args.registration_snippet)) {
|
|
15352
16081
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
15353
16082
|
}
|
|
@@ -15359,18 +16088,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
15359
16088
|
);
|
|
15360
16089
|
}
|
|
15361
16090
|
const primaryFile = primary.file;
|
|
15362
|
-
const primaryPath =
|
|
16091
|
+
const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
|
|
15363
16092
|
const filesTouched = [];
|
|
15364
16093
|
const depsAdded = [];
|
|
15365
|
-
const pkgPath =
|
|
16094
|
+
const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
|
|
15366
16095
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15367
16096
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
15368
16097
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
15369
|
-
await
|
|
16098
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15370
16099
|
filesTouched.push("package.json");
|
|
15371
16100
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
15372
16101
|
}
|
|
15373
|
-
await
|
|
16102
|
+
await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
15374
16103
|
filesTouched.push(primaryFile);
|
|
15375
16104
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
15376
16105
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -15401,7 +16130,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
15401
16130
|
};
|
|
15402
16131
|
}
|
|
15403
16132
|
for (const file of hookFiles) {
|
|
15404
|
-
const content = await
|
|
16133
|
+
const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
|
|
15405
16134
|
if (content.includes(args.registration_snippet)) {
|
|
15406
16135
|
return {
|
|
15407
16136
|
library: args.library,
|
|
@@ -15436,28 +16165,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
15436
16165
|
if (!await fileExists2(logPath)) {
|
|
15437
16166
|
return { undone: false, message: "no apply found for library" };
|
|
15438
16167
|
}
|
|
15439
|
-
const raw = await
|
|
16168
|
+
const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
|
|
15440
16169
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
15441
16170
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
15442
16171
|
if (!match) {
|
|
15443
16172
|
return { undone: false, message: "no apply found for library" };
|
|
15444
16173
|
}
|
|
15445
|
-
const pkgPath =
|
|
16174
|
+
const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
|
|
15446
16175
|
if (await fileExists2(pkgPath)) {
|
|
15447
16176
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
15448
16177
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
15449
16178
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
15450
16179
|
pkg.dependencies = rest;
|
|
15451
|
-
await
|
|
16180
|
+
await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
15452
16181
|
}
|
|
15453
16182
|
}
|
|
15454
16183
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
15455
16184
|
for (const file of hookFiles) {
|
|
15456
|
-
const filePath =
|
|
15457
|
-
const content = await
|
|
16185
|
+
const filePath = import_node_path71.default.join(ctx.scanPath, file);
|
|
16186
|
+
const content = await import_node_fs35.promises.readFile(filePath, "utf8");
|
|
15458
16187
|
if (content.includes(match.registration_snippet)) {
|
|
15459
16188
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
15460
|
-
await
|
|
16189
|
+
await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
|
|
15461
16190
|
break;
|
|
15462
16191
|
}
|
|
15463
16192
|
}
|
|
@@ -15469,39 +16198,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
15469
16198
|
|
|
15470
16199
|
// src/divergences.ts
|
|
15471
16200
|
init_cjs_shims();
|
|
15472
|
-
var
|
|
16201
|
+
var import_types58 = require("@neat.is/types");
|
|
15473
16202
|
function bucketKey(source, target, type) {
|
|
15474
16203
|
return `${type}|${source}|${target}`;
|
|
15475
16204
|
}
|
|
15476
16205
|
function bucketSourceFor(graph, edge) {
|
|
15477
|
-
if (edge.type !==
|
|
15478
|
-
const parsed = (0,
|
|
16206
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
|
|
16207
|
+
const parsed = (0, import_types58.parseFileId)(edge.source);
|
|
15479
16208
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
15480
16209
|
const target = graph.getNodeAttributes(edge.target);
|
|
15481
|
-
if (target.type !==
|
|
15482
|
-
return (0,
|
|
16210
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
|
|
16211
|
+
return (0, import_types58.serviceId)(parsed.service);
|
|
15483
16212
|
}
|
|
15484
16213
|
function bucketEdges(graph) {
|
|
15485
16214
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
15486
16215
|
graph.forEachEdge((id, attrs) => {
|
|
15487
16216
|
const e = attrs;
|
|
15488
|
-
const parsed = (0,
|
|
16217
|
+
const parsed = (0, import_types58.parseEdgeId)(id);
|
|
15489
16218
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
15490
16219
|
const source = bucketSourceFor(graph, e);
|
|
15491
16220
|
const key = bucketKey(source, e.target, e.type);
|
|
15492
16221
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
15493
16222
|
switch (provenance) {
|
|
15494
|
-
case
|
|
16223
|
+
case import_types58.Provenance.EXTRACTED:
|
|
15495
16224
|
cur.extracted = e;
|
|
15496
16225
|
break;
|
|
15497
|
-
case
|
|
16226
|
+
case import_types58.Provenance.OBSERVED:
|
|
15498
16227
|
cur.observed = e;
|
|
15499
16228
|
break;
|
|
15500
|
-
case
|
|
16229
|
+
case import_types58.Provenance.INFERRED:
|
|
15501
16230
|
cur.inferred = e;
|
|
15502
16231
|
break;
|
|
15503
16232
|
default:
|
|
15504
|
-
if (e.provenance ===
|
|
16233
|
+
if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
|
|
15505
16234
|
}
|
|
15506
16235
|
buckets2.set(key, cur);
|
|
15507
16236
|
});
|
|
@@ -15510,22 +16239,22 @@ function bucketEdges(graph) {
|
|
|
15510
16239
|
function nodeIsFrontier(graph, nodeId) {
|
|
15511
16240
|
if (!graph.hasNode(nodeId)) return false;
|
|
15512
16241
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15513
|
-
return attrs.type ===
|
|
16242
|
+
return attrs.type === import_types58.NodeType.FrontierNode;
|
|
15514
16243
|
}
|
|
15515
16244
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
15516
16245
|
if (!graph.hasNode(nodeId)) return false;
|
|
15517
16246
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15518
|
-
return attrs.type ===
|
|
16247
|
+
return attrs.type === import_types58.NodeType.WebSocketChannelNode;
|
|
15519
16248
|
}
|
|
15520
16249
|
function nodeIsServerAction(graph, nodeId) {
|
|
15521
16250
|
if (!graph.hasNode(nodeId)) return false;
|
|
15522
16251
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15523
|
-
return attrs.type ===
|
|
16252
|
+
return attrs.type === import_types58.NodeType.ServerActionNode;
|
|
15524
16253
|
}
|
|
15525
16254
|
function nodeIsSymbol(graph, nodeId) {
|
|
15526
16255
|
if (!graph.hasNode(nodeId)) return false;
|
|
15527
16256
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
15528
|
-
return attrs.type ===
|
|
16257
|
+
return attrs.type === import_types58.NodeType.SymbolNode;
|
|
15529
16258
|
}
|
|
15530
16259
|
function clampConfidence(n) {
|
|
15531
16260
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -15545,14 +16274,14 @@ function gradedConfidence(edge) {
|
|
|
15545
16274
|
return clampConfidence(confidenceForEdge(edge));
|
|
15546
16275
|
}
|
|
15547
16276
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
16277
|
+
import_types58.EdgeType.CALLS,
|
|
16278
|
+
import_types58.EdgeType.CONNECTS_TO,
|
|
16279
|
+
import_types58.EdgeType.PUBLISHES_TO,
|
|
16280
|
+
import_types58.EdgeType.CONSUMES_FROM
|
|
15552
16281
|
]);
|
|
15553
16282
|
function detectMissingDivergences(graph, bucket) {
|
|
15554
16283
|
const out = [];
|
|
15555
|
-
if (bucket.type ===
|
|
16284
|
+
if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
|
|
15556
16285
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
15557
16286
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
15558
16287
|
if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
|
|
@@ -15594,7 +16323,7 @@ function declaredHostFor(svc) {
|
|
|
15594
16323
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
15595
16324
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15596
16325
|
const e = graph.getEdgeAttributes(edgeId);
|
|
15597
|
-
if (e.type ===
|
|
16326
|
+
if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
|
|
15598
16327
|
return true;
|
|
15599
16328
|
}
|
|
15600
16329
|
}
|
|
@@ -15607,10 +16336,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
15607
16336
|
const out = [];
|
|
15608
16337
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15609
16338
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15610
|
-
if (edge.type !==
|
|
15611
|
-
if (edge.provenance !==
|
|
16339
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16340
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15612
16341
|
const target = graph.getNodeAttributes(edge.target);
|
|
15613
|
-
if (target.type !==
|
|
16342
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15614
16343
|
const observedHost = target.host?.trim();
|
|
15615
16344
|
if (!observedHost) continue;
|
|
15616
16345
|
if (observedHost === declaredHost) continue;
|
|
@@ -15632,10 +16361,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
15632
16361
|
const deps = svc.dependencies ?? {};
|
|
15633
16362
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
15634
16363
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
15635
|
-
if (edge.type !==
|
|
15636
|
-
if (edge.provenance !==
|
|
16364
|
+
if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
|
|
16365
|
+
if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
|
|
15637
16366
|
const target = graph.getNodeAttributes(edge.target);
|
|
15638
|
-
if (target.type !==
|
|
16367
|
+
if (target.type !== import_types58.NodeType.DatabaseNode) continue;
|
|
15639
16368
|
for (const pair of compatPairs()) {
|
|
15640
16369
|
if (pair.engine !== target.engine) continue;
|
|
15641
16370
|
const declared = deps[pair.driver];
|
|
@@ -15732,7 +16461,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
15732
16461
|
for (const d of all) {
|
|
15733
16462
|
if (d.type !== "host-mismatch") continue;
|
|
15734
16463
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
15735
|
-
declaredHalf.add((0,
|
|
16464
|
+
declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
|
|
15736
16465
|
}
|
|
15737
16466
|
if (observedHalf.size === 0) return all;
|
|
15738
16467
|
return all.filter((d) => {
|
|
@@ -15751,13 +16480,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15751
16480
|
}
|
|
15752
16481
|
graph.forEachNode((nodeId, attrs) => {
|
|
15753
16482
|
const n = attrs;
|
|
15754
|
-
if (n.type ===
|
|
16483
|
+
if (n.type === import_types58.NodeType.ServiceNode) {
|
|
15755
16484
|
const svc = n;
|
|
15756
16485
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
15757
16486
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
15758
16487
|
return;
|
|
15759
16488
|
}
|
|
15760
|
-
if (n.type ===
|
|
16489
|
+
if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
|
|
15761
16490
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
15762
16491
|
}
|
|
15763
16492
|
});
|
|
@@ -15793,7 +16522,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
15793
16522
|
const bc = "column" in b && b.column ? b.column : "";
|
|
15794
16523
|
return ac.localeCompare(bc);
|
|
15795
16524
|
});
|
|
15796
|
-
return
|
|
16525
|
+
return import_types58.DivergenceResultSchema.parse({
|
|
15797
16526
|
divergences: filtered,
|
|
15798
16527
|
totalAffected: filtered.length,
|
|
15799
16528
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15847,7 +16576,7 @@ function queryLogEntries(opts) {
|
|
|
15847
16576
|
|
|
15848
16577
|
// src/diff.ts
|
|
15849
16578
|
init_cjs_shims();
|
|
15850
|
-
var
|
|
16579
|
+
var import_node_fs36 = require("fs");
|
|
15851
16580
|
async function loadSnapshotForDiff(target) {
|
|
15852
16581
|
if (/^https?:\/\//i.test(target)) {
|
|
15853
16582
|
const res = await fetch(target);
|
|
@@ -15856,7 +16585,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
15856
16585
|
}
|
|
15857
16586
|
return await res.json();
|
|
15858
16587
|
}
|
|
15859
|
-
const raw = await
|
|
16588
|
+
const raw = await import_node_fs36.promises.readFile(target, "utf8");
|
|
15860
16589
|
return JSON.parse(raw);
|
|
15861
16590
|
}
|
|
15862
16591
|
function indexEntries(entries) {
|
|
@@ -15924,28 +16653,28 @@ function canonicalJson(value) {
|
|
|
15924
16653
|
|
|
15925
16654
|
// src/registry.ts
|
|
15926
16655
|
init_cjs_shims();
|
|
15927
|
-
var
|
|
16656
|
+
var import_node_fs37 = require("fs");
|
|
15928
16657
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
15929
|
-
var
|
|
15930
|
-
var
|
|
16658
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
16659
|
+
var import_types59 = require("@neat.is/types");
|
|
15931
16660
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
15932
16661
|
var LOCK_RETRY_MS = 50;
|
|
15933
16662
|
function neatHome() {
|
|
15934
16663
|
const override = process.env.NEAT_HOME;
|
|
15935
|
-
if (override && override.length > 0) return
|
|
15936
|
-
return
|
|
16664
|
+
if (override && override.length > 0) return import_node_path72.default.resolve(override);
|
|
16665
|
+
return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
|
|
15937
16666
|
}
|
|
15938
16667
|
function registryPath() {
|
|
15939
|
-
return
|
|
16668
|
+
return import_node_path72.default.join(neatHome(), "projects.json");
|
|
15940
16669
|
}
|
|
15941
16670
|
function registryLockPath() {
|
|
15942
|
-
return
|
|
16671
|
+
return import_node_path72.default.join(neatHome(), "projects.json.lock");
|
|
15943
16672
|
}
|
|
15944
16673
|
function daemonPidPath() {
|
|
15945
|
-
return
|
|
16674
|
+
return import_node_path72.default.join(neatHome(), "neatd.pid");
|
|
15946
16675
|
}
|
|
15947
16676
|
function daemonsDir() {
|
|
15948
|
-
return
|
|
16677
|
+
return import_node_path72.default.join(neatHome(), "daemons");
|
|
15949
16678
|
}
|
|
15950
16679
|
function isFiniteInt(v) {
|
|
15951
16680
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -15978,7 +16707,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
15978
16707
|
const dir = daemonsDir();
|
|
15979
16708
|
let names;
|
|
15980
16709
|
try {
|
|
15981
|
-
names = await
|
|
16710
|
+
names = await import_node_fs37.promises.readdir(dir);
|
|
15982
16711
|
} catch (err) {
|
|
15983
16712
|
if (err.code === "ENOENT") return [];
|
|
15984
16713
|
throw err;
|
|
@@ -15986,10 +16715,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
15986
16715
|
const out = [];
|
|
15987
16716
|
for (const name of names) {
|
|
15988
16717
|
if (!name.endsWith(".json")) continue;
|
|
15989
|
-
const file =
|
|
16718
|
+
const file = import_node_path72.default.join(dir, name);
|
|
15990
16719
|
let raw;
|
|
15991
16720
|
try {
|
|
15992
|
-
raw = await
|
|
16721
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
15993
16722
|
} catch {
|
|
15994
16723
|
continue;
|
|
15995
16724
|
}
|
|
@@ -16015,7 +16744,7 @@ function isPidAliveDefault(pid) {
|
|
|
16015
16744
|
}
|
|
16016
16745
|
async function readPidFile(file) {
|
|
16017
16746
|
try {
|
|
16018
|
-
const raw = await
|
|
16747
|
+
const raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16019
16748
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
16020
16749
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
16021
16750
|
} catch {
|
|
@@ -16063,24 +16792,24 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
16063
16792
|
}
|
|
16064
16793
|
}
|
|
16065
16794
|
async function writeAtomically(target, contents) {
|
|
16066
|
-
await
|
|
16795
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
|
|
16067
16796
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
16068
|
-
const fd = await
|
|
16797
|
+
const fd = await import_node_fs37.promises.open(tmp, "w");
|
|
16069
16798
|
try {
|
|
16070
16799
|
await fd.writeFile(contents, "utf8");
|
|
16071
16800
|
await fd.sync();
|
|
16072
16801
|
} finally {
|
|
16073
16802
|
await fd.close();
|
|
16074
16803
|
}
|
|
16075
|
-
await
|
|
16804
|
+
await import_node_fs37.promises.rename(tmp, target);
|
|
16076
16805
|
}
|
|
16077
16806
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
16078
16807
|
const deadline = Date.now() + timeoutMs;
|
|
16079
|
-
await
|
|
16808
|
+
await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
|
|
16080
16809
|
let probedHolder = false;
|
|
16081
16810
|
while (true) {
|
|
16082
16811
|
try {
|
|
16083
|
-
const fd = await
|
|
16812
|
+
const fd = await import_node_fs37.promises.open(lockPath, "wx");
|
|
16084
16813
|
try {
|
|
16085
16814
|
await fd.writeFile(`${process.pid}
|
|
16086
16815
|
`, "utf8");
|
|
@@ -16105,7 +16834,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
16105
16834
|
}
|
|
16106
16835
|
}
|
|
16107
16836
|
async function releaseLock(lockPath) {
|
|
16108
|
-
await
|
|
16837
|
+
await import_node_fs37.promises.unlink(lockPath).catch(() => {
|
|
16109
16838
|
});
|
|
16110
16839
|
}
|
|
16111
16840
|
async function withLock(fn) {
|
|
@@ -16121,7 +16850,7 @@ async function readRegistry() {
|
|
|
16121
16850
|
const file = registryPath();
|
|
16122
16851
|
let raw;
|
|
16123
16852
|
try {
|
|
16124
|
-
raw = await
|
|
16853
|
+
raw = await import_node_fs37.promises.readFile(file, "utf8");
|
|
16125
16854
|
} catch (err) {
|
|
16126
16855
|
if (err.code === "ENOENT") {
|
|
16127
16856
|
return { version: 1, projects: [] };
|
|
@@ -16129,10 +16858,10 @@ async function readRegistry() {
|
|
|
16129
16858
|
throw err;
|
|
16130
16859
|
}
|
|
16131
16860
|
const parsed = JSON.parse(raw);
|
|
16132
|
-
return
|
|
16861
|
+
return import_types59.RegistryFileSchema.parse(parsed);
|
|
16133
16862
|
}
|
|
16134
16863
|
async function writeRegistry(reg) {
|
|
16135
|
-
const validated =
|
|
16864
|
+
const validated = import_types59.RegistryFileSchema.parse(reg);
|
|
16136
16865
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16137
16866
|
}
|
|
16138
16867
|
async function getProject(name) {
|
|
@@ -16176,7 +16905,7 @@ function pruneTtlMs() {
|
|
|
16176
16905
|
}
|
|
16177
16906
|
async function statPathStatus(p) {
|
|
16178
16907
|
try {
|
|
16179
|
-
const stat = await
|
|
16908
|
+
const stat = await import_node_fs37.promises.stat(p);
|
|
16180
16909
|
return stat.isDirectory() ? "present" : "unknown";
|
|
16181
16910
|
} catch (err) {
|
|
16182
16911
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -16277,8 +17006,8 @@ init_auth();
|
|
|
16277
17006
|
// src/connectors-config.ts
|
|
16278
17007
|
init_cjs_shims();
|
|
16279
17008
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
16280
|
-
var
|
|
16281
|
-
var
|
|
17009
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
17010
|
+
var import_node_fs38 = require("fs");
|
|
16282
17011
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
16283
17012
|
var EnvRefUnsetError = class extends Error {
|
|
16284
17013
|
ref;
|
|
@@ -16292,17 +17021,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
16292
17021
|
};
|
|
16293
17022
|
function neatHome2() {
|
|
16294
17023
|
const override = process.env.NEAT_HOME;
|
|
16295
|
-
if (override && override.length > 0) return
|
|
16296
|
-
return
|
|
17024
|
+
if (override && override.length > 0) return import_node_path73.default.resolve(override);
|
|
17025
|
+
return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
|
|
16297
17026
|
}
|
|
16298
17027
|
function connectorsConfigPath(home = neatHome2()) {
|
|
16299
|
-
return
|
|
17028
|
+
return import_node_path73.default.join(home, "connectors.json");
|
|
16300
17029
|
}
|
|
16301
17030
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
16302
17031
|
async function warnIfModeLooserThan0600(file) {
|
|
16303
17032
|
if (process.platform === "win32") return;
|
|
16304
17033
|
try {
|
|
16305
|
-
const stat = await
|
|
17034
|
+
const stat = await import_node_fs38.promises.stat(file);
|
|
16306
17035
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
16307
17036
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
16308
17037
|
console.warn(
|
|
@@ -16316,7 +17045,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
16316
17045
|
const file = connectorsConfigPath(home);
|
|
16317
17046
|
let raw;
|
|
16318
17047
|
try {
|
|
16319
|
-
raw = await
|
|
17048
|
+
raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
16320
17049
|
} catch (err) {
|
|
16321
17050
|
if (err.code === "ENOENT") {
|
|
16322
17051
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -16483,15 +17212,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
16483
17212
|
|
|
16484
17213
|
// src/connectors/index.ts
|
|
16485
17214
|
init_cjs_shims();
|
|
16486
|
-
var
|
|
17215
|
+
var import_types60 = require("@neat.is/types");
|
|
16487
17216
|
var NO_ENV = "unknown";
|
|
16488
17217
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
16489
17218
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16490
17219
|
const sites = [];
|
|
16491
17220
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
16492
17221
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
16493
|
-
if (edge.provenance !==
|
|
16494
|
-
const parsed = (0,
|
|
17222
|
+
if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
|
|
17223
|
+
const parsed = (0, import_types60.parseFileId)(edge.source);
|
|
16495
17224
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
16496
17225
|
const site = { relPath: edge.evidence.file };
|
|
16497
17226
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -16502,7 +17231,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
16502
17231
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
16503
17232
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
16504
17233
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
16505
|
-
if (attrs.type !==
|
|
17234
|
+
if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
|
|
16506
17235
|
const site = { relPath: attrs.path };
|
|
16507
17236
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
16508
17237
|
return site;
|
|
@@ -17000,10 +17729,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
17000
17729
|
// src/connectors/supabase/map.ts
|
|
17001
17730
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
17002
17731
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
17003
|
-
function targetFromRestPath(
|
|
17004
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
17732
|
+
function targetFromRestPath(path78) {
|
|
17733
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path78);
|
|
17005
17734
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
17006
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
17735
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path78);
|
|
17007
17736
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
17008
17737
|
return null;
|
|
17009
17738
|
}
|
|
@@ -17114,23 +17843,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17114
17843
|
|
|
17115
17844
|
// src/connectors/supabase/resolve.ts
|
|
17116
17845
|
init_cjs_shims();
|
|
17117
|
-
var
|
|
17846
|
+
var import_types62 = require("@neat.is/types");
|
|
17118
17847
|
function createSupabaseResolveTarget(graph, config) {
|
|
17119
17848
|
return (signal, _ctx) => {
|
|
17120
17849
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17121
17850
|
return null;
|
|
17122
17851
|
}
|
|
17123
|
-
const subResourceId = (0,
|
|
17852
|
+
const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17124
17853
|
if (graph.hasNode(subResourceId)) {
|
|
17125
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
17854
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17126
17855
|
}
|
|
17127
|
-
const bareResourceId = (0,
|
|
17856
|
+
const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
|
|
17128
17857
|
if (graph.hasNode(bareResourceId)) {
|
|
17129
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
17858
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17130
17859
|
}
|
|
17131
|
-
const projectLevelId = (0,
|
|
17860
|
+
const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
|
|
17132
17861
|
if (graph.hasNode(projectLevelId)) {
|
|
17133
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
17862
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
|
|
17134
17863
|
}
|
|
17135
17864
|
return null;
|
|
17136
17865
|
};
|
|
@@ -17223,7 +17952,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
17223
17952
|
|
|
17224
17953
|
// src/connectors/railway/index.ts
|
|
17225
17954
|
init_cjs_shims();
|
|
17226
|
-
var
|
|
17955
|
+
var import_types66 = require("@neat.is/types");
|
|
17227
17956
|
|
|
17228
17957
|
// src/connectors/railway/client.ts
|
|
17229
17958
|
init_cjs_shims();
|
|
@@ -17374,7 +18103,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
17374
18103
|
const out = [];
|
|
17375
18104
|
graph.forEachNode((_id, attrs) => {
|
|
17376
18105
|
const node = attrs;
|
|
17377
|
-
if (node.type !==
|
|
18106
|
+
if (node.type !== import_types66.NodeType.RouteNode) return;
|
|
17378
18107
|
const route = attrs;
|
|
17379
18108
|
if (route.service !== serviceName) return;
|
|
17380
18109
|
out.push({
|
|
@@ -17478,12 +18207,12 @@ function createRailwayResolveTarget(config) {
|
|
|
17478
18207
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
17479
18208
|
if (!serviceName) return null;
|
|
17480
18209
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
17481
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18210
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
|
|
17482
18211
|
}
|
|
17483
18212
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
17484
18213
|
const peerName = config.serviceNameById[signal.targetName];
|
|
17485
18214
|
if (!peerName) return null;
|
|
17486
|
-
return { targetNodeId: (0,
|
|
18215
|
+
return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
|
|
17487
18216
|
}
|
|
17488
18217
|
return null;
|
|
17489
18218
|
};
|
|
@@ -17607,9 +18336,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
17607
18336
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
17608
18337
|
if (secondSep === -1) return null;
|
|
17609
18338
|
const method = rest.slice(0, secondSep);
|
|
17610
|
-
const
|
|
17611
|
-
if (!resourceName || !method || !
|
|
17612
|
-
return { resourceName, method, path:
|
|
18339
|
+
const path78 = rest.slice(secondSep + 1);
|
|
18340
|
+
if (!resourceName || !method || !path78) return null;
|
|
18341
|
+
return { resourceName, method, path: path78 };
|
|
17613
18342
|
}
|
|
17614
18343
|
function resourceNameFor(type, labels) {
|
|
17615
18344
|
if (!labels) return null;
|
|
@@ -17647,14 +18376,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
17647
18376
|
if (!req2) return null;
|
|
17648
18377
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
17649
18378
|
const method = req2.requestMethod.toUpperCase();
|
|
17650
|
-
const
|
|
17651
|
-
if (
|
|
18379
|
+
const path78 = pathFromRequestUrl(req2.requestUrl);
|
|
18380
|
+
if (path78 === null) return null;
|
|
17652
18381
|
const timestamp = entry2.timestamp;
|
|
17653
18382
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
17654
18383
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
17655
18384
|
return {
|
|
17656
18385
|
targetKind: resourceType,
|
|
17657
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
18386
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
|
|
17658
18387
|
callCount: 1,
|
|
17659
18388
|
errorCount: isError ? 1 : 0,
|
|
17660
18389
|
lastObservedIso: timestamp
|
|
@@ -17671,7 +18400,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
17671
18400
|
|
|
17672
18401
|
// src/connectors/firebase/resolve.ts
|
|
17673
18402
|
init_cjs_shims();
|
|
17674
|
-
var
|
|
18403
|
+
var import_types67 = require("@neat.is/types");
|
|
17675
18404
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
17676
18405
|
switch (resourceType) {
|
|
17677
18406
|
case "cloud_function":
|
|
@@ -17686,7 +18415,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
17686
18415
|
const entries = [];
|
|
17687
18416
|
graph.forEachNode((_id, attrs) => {
|
|
17688
18417
|
const node = attrs;
|
|
17689
|
-
if (node.type !==
|
|
18418
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
17690
18419
|
const route = attrs;
|
|
17691
18420
|
if (route.service !== serviceName) return;
|
|
17692
18421
|
entries.push({
|
|
@@ -17718,7 +18447,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
17718
18447
|
return {
|
|
17719
18448
|
targetNodeId: match.routeNodeId,
|
|
17720
18449
|
serviceName,
|
|
17721
|
-
edgeType:
|
|
18450
|
+
edgeType: import_types67.EdgeType.CALLS
|
|
17722
18451
|
};
|
|
17723
18452
|
};
|
|
17724
18453
|
}
|
|
@@ -17745,7 +18474,7 @@ init_cjs_shims();
|
|
|
17745
18474
|
|
|
17746
18475
|
// src/connectors/cloudflare/connector.ts
|
|
17747
18476
|
init_cjs_shims();
|
|
17748
|
-
var
|
|
18477
|
+
var import_types69 = require("@neat.is/types");
|
|
17749
18478
|
|
|
17750
18479
|
// src/connectors/cloudflare/client.ts
|
|
17751
18480
|
init_cjs_shims();
|
|
@@ -17861,7 +18590,7 @@ function mapEventToSignal(event) {
|
|
|
17861
18590
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
17862
18591
|
const statusCode = metadata?.statusCode;
|
|
17863
18592
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
17864
|
-
const
|
|
18593
|
+
const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
17865
18594
|
return {
|
|
17866
18595
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
17867
18596
|
targetName: scriptName,
|
|
@@ -17869,7 +18598,7 @@ function mapEventToSignal(event) {
|
|
|
17869
18598
|
errorCount: isError ? 1 : 0,
|
|
17870
18599
|
lastObservedIso: observedAt.toISOString(),
|
|
17871
18600
|
method,
|
|
17872
|
-
...
|
|
18601
|
+
...path78 ? { path: path78 } : {},
|
|
17873
18602
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
17874
18603
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
17875
18604
|
};
|
|
@@ -17909,19 +18638,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
17909
18638
|
graph.forEachNode((id, attrs) => {
|
|
17910
18639
|
if (found) return;
|
|
17911
18640
|
const a = attrs;
|
|
17912
|
-
if (a.type ===
|
|
18641
|
+
if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
17913
18642
|
found = id;
|
|
17914
18643
|
}
|
|
17915
18644
|
});
|
|
17916
18645
|
return found;
|
|
17917
18646
|
}
|
|
17918
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
17919
|
-
const normalizedPath = normalizePathTemplate(
|
|
18647
|
+
function findMatchingRouteNode(graph, serviceName, method, path78) {
|
|
18648
|
+
const normalizedPath = normalizePathTemplate(path78);
|
|
17920
18649
|
let found = null;
|
|
17921
18650
|
graph.forEachNode((id, attrs) => {
|
|
17922
18651
|
if (found) return;
|
|
17923
18652
|
const a = attrs;
|
|
17924
|
-
if (a.type !==
|
|
18653
|
+
if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
|
|
17925
18654
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
17926
18655
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
17927
18656
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -17933,18 +18662,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
17933
18662
|
return (signal) => {
|
|
17934
18663
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
17935
18664
|
const scriptName = signal.targetName;
|
|
17936
|
-
const { method, path:
|
|
18665
|
+
const { method, path: path78 } = signal;
|
|
17937
18666
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
17938
|
-
if (!method || !
|
|
17939
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
18667
|
+
if (!method || !path78) return wholeFileId;
|
|
18668
|
+
return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
|
|
17940
18669
|
};
|
|
17941
18670
|
const mapping = config.workers?.[scriptName];
|
|
17942
18671
|
if (mapping) {
|
|
17943
|
-
const wholeFileId = (0,
|
|
18672
|
+
const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
|
|
17944
18673
|
return {
|
|
17945
18674
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
17946
18675
|
serviceName: mapping.service,
|
|
17947
|
-
edgeType:
|
|
18676
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
17948
18677
|
};
|
|
17949
18678
|
}
|
|
17950
18679
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -17953,13 +18682,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
17953
18682
|
return {
|
|
17954
18683
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
17955
18684
|
serviceName: fileNode.service,
|
|
17956
|
-
edgeType:
|
|
18685
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
17957
18686
|
};
|
|
17958
18687
|
}
|
|
17959
18688
|
return {
|
|
17960
|
-
targetNodeId: (0,
|
|
18689
|
+
targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
|
|
17961
18690
|
serviceName: scriptName,
|
|
17962
|
-
edgeType:
|
|
18691
|
+
edgeType: import_types69.EdgeType.CALLS,
|
|
17963
18692
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
17964
18693
|
};
|
|
17965
18694
|
};
|
|
@@ -18155,14 +18884,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18155
18884
|
|
|
18156
18885
|
// src/connectors/neon/resolve.ts
|
|
18157
18886
|
init_cjs_shims();
|
|
18158
|
-
var
|
|
18887
|
+
var import_types73 = require("@neat.is/types");
|
|
18159
18888
|
function createNeonResolveTarget(config) {
|
|
18160
18889
|
return (signal) => {
|
|
18161
18890
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18162
18891
|
return {
|
|
18163
|
-
targetNodeId: (0,
|
|
18892
|
+
targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
|
|
18164
18893
|
serviceName: config.serviceName,
|
|
18165
|
-
edgeType:
|
|
18894
|
+
edgeType: import_types73.EdgeType.CALLS,
|
|
18166
18895
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18167
18896
|
};
|
|
18168
18897
|
};
|
|
@@ -18288,9 +19017,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
18288
19017
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
18289
19018
|
if (secondSep === -1) return null;
|
|
18290
19019
|
const method = rest.slice(0, secondSep);
|
|
18291
|
-
const
|
|
18292
|
-
if (!serviceName || !method || !
|
|
18293
|
-
return { serviceName, method, path:
|
|
19020
|
+
const path78 = rest.slice(secondSep + 1);
|
|
19021
|
+
if (!serviceName || !method || !path78) return null;
|
|
19022
|
+
return { serviceName, method, path: path78 };
|
|
18294
19023
|
}
|
|
18295
19024
|
|
|
18296
19025
|
// src/connectors/cloud-run/map.ts
|
|
@@ -18319,14 +19048,14 @@ function mapLogEntryToSignal2(entry2) {
|
|
|
18319
19048
|
if (!req2) return null;
|
|
18320
19049
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
18321
19050
|
const method = req2.requestMethod.toUpperCase();
|
|
18322
|
-
const
|
|
18323
|
-
if (
|
|
19051
|
+
const path78 = pathFromRequestUrl2(req2.requestUrl);
|
|
19052
|
+
if (path78 === null) return null;
|
|
18324
19053
|
const timestamp = entry2.timestamp;
|
|
18325
19054
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
18326
19055
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
|
|
18327
19056
|
return {
|
|
18328
19057
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
18329
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
19058
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
|
|
18330
19059
|
callCount: 1,
|
|
18331
19060
|
errorCount: isError ? 1 : 0,
|
|
18332
19061
|
lastObservedIso: timestamp
|
|
@@ -18343,14 +19072,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
18343
19072
|
|
|
18344
19073
|
// src/connectors/cloud-run/resolve.ts
|
|
18345
19074
|
init_cjs_shims();
|
|
18346
|
-
var
|
|
19075
|
+
var import_types77 = require("@neat.is/types");
|
|
18347
19076
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
18348
19077
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
18349
19078
|
let found = null;
|
|
18350
19079
|
graph.forEachNode((_id, attrs) => {
|
|
18351
19080
|
if (found) return;
|
|
18352
19081
|
const node = attrs;
|
|
18353
|
-
if (node.type !==
|
|
19082
|
+
if (node.type !== import_types77.NodeType.RouteNode) return;
|
|
18354
19083
|
const route = attrs;
|
|
18355
19084
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
18356
19085
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -18365,23 +19094,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
18365
19094
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
18366
19095
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
18367
19096
|
if (!identity) return null;
|
|
18368
|
-
const { serviceName: gcpServiceName, method, path:
|
|
19097
|
+
const { serviceName: gcpServiceName, method, path: path78 } = identity;
|
|
18369
19098
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
18370
19099
|
if (mappedService) {
|
|
18371
19100
|
const routeNodeId = findMatchingRouteNode2(
|
|
18372
19101
|
graph,
|
|
18373
19102
|
mappedService,
|
|
18374
19103
|
method,
|
|
18375
|
-
normalizePathTemplate(
|
|
19104
|
+
normalizePathTemplate(path78)
|
|
18376
19105
|
);
|
|
18377
19106
|
if (routeNodeId) {
|
|
18378
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19107
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
|
|
18379
19108
|
}
|
|
18380
19109
|
}
|
|
18381
19110
|
return {
|
|
18382
|
-
targetNodeId: (0,
|
|
19111
|
+
targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
18383
19112
|
serviceName: mappedService ?? gcpServiceName,
|
|
18384
|
-
edgeType:
|
|
19113
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
18385
19114
|
ensureInfraNode: {
|
|
18386
19115
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
18387
19116
|
name: gcpServiceName,
|
|
@@ -18422,7 +19151,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
18422
19151
|
|
|
18423
19152
|
// src/connectors/render/index.ts
|
|
18424
19153
|
init_cjs_shims();
|
|
18425
|
-
var
|
|
19154
|
+
var import_types80 = require("@neat.is/types");
|
|
18426
19155
|
|
|
18427
19156
|
// src/connectors/render/types.ts
|
|
18428
19157
|
init_cjs_shims();
|
|
@@ -18500,7 +19229,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
18500
19229
|
const out = [];
|
|
18501
19230
|
graph.forEachNode((_id, attrs) => {
|
|
18502
19231
|
const node = attrs;
|
|
18503
|
-
if (node.type !==
|
|
19232
|
+
if (node.type !== import_types80.NodeType.RouteNode) return;
|
|
18504
19233
|
const route = attrs;
|
|
18505
19234
|
if (route.service !== serviceName) return;
|
|
18506
19235
|
out.push({
|
|
@@ -18585,7 +19314,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
18585
19314
|
function createRenderResolveTarget(config) {
|
|
18586
19315
|
return (signal) => {
|
|
18587
19316
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
18588
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
19317
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
|
|
18589
19318
|
}
|
|
18590
19319
|
return null;
|
|
18591
19320
|
};
|
|
@@ -18723,21 +19452,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
18723
19452
|
|
|
18724
19453
|
// src/connectors/planetscale/resolve.ts
|
|
18725
19454
|
init_cjs_shims();
|
|
18726
|
-
var
|
|
19455
|
+
var import_types84 = require("@neat.is/types");
|
|
18727
19456
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
18728
19457
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
18729
19458
|
const databaseName = `${config.organization}/${config.database}`;
|
|
18730
19459
|
return (signal, _ctx) => {
|
|
18731
19460
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18732
|
-
const tableId = (0,
|
|
19461
|
+
const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
|
|
18733
19462
|
if (graph.hasNode(tableId)) {
|
|
18734
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
19463
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
|
|
18735
19464
|
}
|
|
18736
|
-
const providerId = (0,
|
|
19465
|
+
const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
18737
19466
|
return {
|
|
18738
19467
|
targetNodeId: providerId,
|
|
18739
19468
|
serviceName: config.serviceName,
|
|
18740
|
-
edgeType:
|
|
19469
|
+
edgeType: import_types84.EdgeType.CALLS,
|
|
18741
19470
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
18742
19471
|
};
|
|
18743
19472
|
};
|
|
@@ -19002,7 +19731,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19002
19731
|
|
|
19003
19732
|
// src/connectors/eas/resolve.ts
|
|
19004
19733
|
init_cjs_shims();
|
|
19005
|
-
var
|
|
19734
|
+
var import_types89 = require("@neat.is/types");
|
|
19006
19735
|
var NO_ENV2 = "unknown";
|
|
19007
19736
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19008
19737
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19018,8 +19747,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19018
19747
|
function configNodeService(graph, configNodeId) {
|
|
19019
19748
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19020
19749
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19021
|
-
if (edge.type !==
|
|
19022
|
-
const parsed = (0,
|
|
19750
|
+
if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
|
|
19751
|
+
const parsed = (0, import_types89.parseFileId)(edge.source);
|
|
19023
19752
|
if (parsed) return parsed.service;
|
|
19024
19753
|
}
|
|
19025
19754
|
return null;
|
|
@@ -19030,7 +19759,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19030
19759
|
graph.forEachNode((id, attrs) => {
|
|
19031
19760
|
if (scoped) return;
|
|
19032
19761
|
const node = attrs;
|
|
19033
|
-
if (node.type !==
|
|
19762
|
+
if (node.type !== import_types89.NodeType.ConfigNode) return;
|
|
19034
19763
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19035
19764
|
if (anyMatch === null) anyMatch = id;
|
|
19036
19765
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19047,13 +19776,13 @@ function createEasResolveTarget(graph) {
|
|
|
19047
19776
|
if (basenames.length > 0) {
|
|
19048
19777
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19049
19778
|
if (configNodeId) {
|
|
19050
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
19779
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
|
|
19051
19780
|
}
|
|
19052
19781
|
}
|
|
19053
19782
|
return {
|
|
19054
19783
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19055
19784
|
serviceName,
|
|
19056
|
-
edgeType:
|
|
19785
|
+
edgeType: import_types89.EdgeType.CALLS
|
|
19057
19786
|
};
|
|
19058
19787
|
};
|
|
19059
19788
|
}
|
|
@@ -19778,11 +20507,11 @@ function registerRoutes(scope, ctx) {
|
|
|
19778
20507
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
19779
20508
|
const parsed = [];
|
|
19780
20509
|
for (const c of candidates) {
|
|
19781
|
-
const r =
|
|
20510
|
+
const r = import_types92.DivergenceTypeSchema.safeParse(c);
|
|
19782
20511
|
if (!r.success) {
|
|
19783
20512
|
return reply.code(400).send({
|
|
19784
20513
|
error: `unknown divergence type "${c}"`,
|
|
19785
|
-
allowed:
|
|
20514
|
+
allowed: import_types92.DivergenceTypeSchema.options
|
|
19786
20515
|
});
|
|
19787
20516
|
}
|
|
19788
20517
|
parsed.push(r.data);
|
|
@@ -20124,7 +20853,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20124
20853
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20125
20854
|
let violations = await log.readAll();
|
|
20126
20855
|
if (req2.query.severity) {
|
|
20127
|
-
const sev =
|
|
20856
|
+
const sev = import_types92.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
20128
20857
|
if (!sev.success) {
|
|
20129
20858
|
return reply.code(400).send({
|
|
20130
20859
|
error: "invalid severity",
|
|
@@ -20163,7 +20892,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20163
20892
|
scope.post("/policies/check", async (req2, reply) => {
|
|
20164
20893
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
20165
20894
|
if (!proj) return;
|
|
20166
|
-
const parsed =
|
|
20895
|
+
const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
20167
20896
|
if (!parsed.success) {
|
|
20168
20897
|
return reply.code(400).send({
|
|
20169
20898
|
error: "invalid /policies/check body",
|
|
@@ -20484,8 +21213,8 @@ init_auth();
|
|
|
20484
21213
|
|
|
20485
21214
|
// src/unrouted.ts
|
|
20486
21215
|
init_cjs_shims();
|
|
20487
|
-
var
|
|
20488
|
-
var
|
|
21216
|
+
var import_node_fs39 = require("fs");
|
|
21217
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
20489
21218
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
20490
21219
|
return {
|
|
20491
21220
|
timestamp: now.toISOString(),
|
|
@@ -20495,34 +21224,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
20495
21224
|
};
|
|
20496
21225
|
}
|
|
20497
21226
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
20498
|
-
const target =
|
|
20499
|
-
await
|
|
20500
|
-
await
|
|
21227
|
+
const target = import_node_path74.default.join(neatHome4, "errors.ndjson");
|
|
21228
|
+
await import_node_fs39.promises.mkdir(neatHome4, { recursive: true });
|
|
21229
|
+
await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
20501
21230
|
}
|
|
20502
21231
|
function unroutedErrorsPath(neatHome4) {
|
|
20503
|
-
return
|
|
21232
|
+
return import_node_path74.default.join(neatHome4, "errors.ndjson");
|
|
20504
21233
|
}
|
|
20505
21234
|
|
|
20506
21235
|
// src/daemon.ts
|
|
20507
|
-
var
|
|
21236
|
+
var import_types93 = require("@neat.is/types");
|
|
20508
21237
|
function daemonJsonPath(scanPath) {
|
|
20509
|
-
return
|
|
21238
|
+
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
20510
21239
|
}
|
|
20511
21240
|
function daemonsDiscoveryDir(home) {
|
|
20512
21241
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
20513
|
-
return
|
|
21242
|
+
return import_node_path75.default.join(base, "daemons");
|
|
20514
21243
|
}
|
|
20515
21244
|
function daemonDiscoveryPath(project, home) {
|
|
20516
|
-
return
|
|
21245
|
+
return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
20517
21246
|
}
|
|
20518
21247
|
function sanitizeDiscoveryName(project) {
|
|
20519
21248
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
20520
21249
|
}
|
|
20521
21250
|
function neatHomeFromEnv() {
|
|
20522
21251
|
const env = process.env.NEAT_HOME;
|
|
20523
|
-
if (env && env.length > 0) return
|
|
21252
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20524
21253
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20525
|
-
return
|
|
21254
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20526
21255
|
}
|
|
20527
21256
|
function resolveNeatVersion() {
|
|
20528
21257
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -20554,7 +21283,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
20554
21283
|
} catch {
|
|
20555
21284
|
}
|
|
20556
21285
|
try {
|
|
20557
|
-
await
|
|
21286
|
+
await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
20558
21287
|
} catch {
|
|
20559
21288
|
}
|
|
20560
21289
|
}
|
|
@@ -20563,12 +21292,12 @@ function reconcileDaemonRecordSync(record, home) {
|
|
|
20563
21292
|
const stopped = { ...record, status: "stopped" };
|
|
20564
21293
|
const target = daemonJsonPath(record.projectPath);
|
|
20565
21294
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
20566
|
-
(0,
|
|
20567
|
-
(0,
|
|
21295
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
|
|
21296
|
+
(0, import_node_fs40.renameSync)(tmp, target);
|
|
20568
21297
|
} catch {
|
|
20569
21298
|
}
|
|
20570
21299
|
try {
|
|
20571
|
-
(0,
|
|
21300
|
+
(0, import_node_fs40.unlinkSync)(daemonDiscoveryPath(record.project, home));
|
|
20572
21301
|
} catch {
|
|
20573
21302
|
}
|
|
20574
21303
|
}
|
|
@@ -20591,11 +21320,11 @@ function teardownSlot(slot) {
|
|
|
20591
21320
|
}
|
|
20592
21321
|
}
|
|
20593
21322
|
function neatHomeFor(opts) {
|
|
20594
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
21323
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
|
|
20595
21324
|
const env = process.env.NEAT_HOME;
|
|
20596
|
-
if (env && env.length > 0) return
|
|
21325
|
+
if (env && env.length > 0) return import_node_path75.default.resolve(env);
|
|
20597
21326
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20598
|
-
return
|
|
21327
|
+
return import_node_path75.default.join(home, ".neat");
|
|
20599
21328
|
}
|
|
20600
21329
|
function routeSpanToProject(serviceName, projects) {
|
|
20601
21330
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -20643,13 +21372,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
20643
21372
|
if (!serviceName) return true;
|
|
20644
21373
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
20645
21374
|
return graph.someNode(
|
|
20646
|
-
(_id, attrs) => attrs.type ===
|
|
21375
|
+
(_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
|
|
20647
21376
|
);
|
|
20648
21377
|
}
|
|
20649
21378
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
20650
|
-
const paths = pathsForProject(entry2.name,
|
|
21379
|
+
const paths = pathsForProject(entry2.name, import_node_path75.default.join(entry2.path, "neat-out"));
|
|
20651
21380
|
try {
|
|
20652
|
-
const stat = await
|
|
21381
|
+
const stat = await import_node_fs40.promises.stat(entry2.path);
|
|
20653
21382
|
if (!stat.isDirectory()) {
|
|
20654
21383
|
throw new Error(`registered path ${entry2.path} is not a directory`);
|
|
20655
21384
|
}
|
|
@@ -20767,7 +21496,7 @@ async function startDaemon(opts = {}) {
|
|
|
20767
21496
|
const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
|
|
20768
21497
|
const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
|
|
20769
21498
|
const singleProject = projectArg;
|
|
20770
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
21499
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
|
|
20771
21500
|
if (singleProject && !singleProjectPath) {
|
|
20772
21501
|
throw new Error(
|
|
20773
21502
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -20775,14 +21504,14 @@ async function startDaemon(opts = {}) {
|
|
|
20775
21504
|
}
|
|
20776
21505
|
if (!singleProject) {
|
|
20777
21506
|
try {
|
|
20778
|
-
await
|
|
21507
|
+
await import_node_fs40.promises.access(regPath);
|
|
20779
21508
|
} catch {
|
|
20780
21509
|
throw new Error(
|
|
20781
21510
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
20782
21511
|
);
|
|
20783
21512
|
}
|
|
20784
21513
|
}
|
|
20785
|
-
const pidPath =
|
|
21514
|
+
const pidPath = import_node_path75.default.join(home, "neatd.pid");
|
|
20786
21515
|
await writeAtomically(pidPath, `${process.pid}
|
|
20787
21516
|
`);
|
|
20788
21517
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -20994,7 +21723,7 @@ async function startDaemon(opts = {}) {
|
|
|
20994
21723
|
}
|
|
20995
21724
|
if (restApp) await restApp.close().catch(() => {
|
|
20996
21725
|
});
|
|
20997
|
-
await
|
|
21726
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
20998
21727
|
});
|
|
20999
21728
|
throw new Error(
|
|
21000
21729
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -21162,7 +21891,7 @@ async function startDaemon(opts = {}) {
|
|
|
21162
21891
|
});
|
|
21163
21892
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21164
21893
|
});
|
|
21165
|
-
await
|
|
21894
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21166
21895
|
});
|
|
21167
21896
|
throw new Error(
|
|
21168
21897
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -21197,7 +21926,7 @@ async function startDaemon(opts = {}) {
|
|
|
21197
21926
|
});
|
|
21198
21927
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
21199
21928
|
});
|
|
21200
|
-
await
|
|
21929
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21201
21930
|
});
|
|
21202
21931
|
throw new Error(
|
|
21203
21932
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -21244,9 +21973,9 @@ async function startDaemon(opts = {}) {
|
|
|
21244
21973
|
let registryWatcher = null;
|
|
21245
21974
|
let reloadTimer = null;
|
|
21246
21975
|
if (!singleProject) try {
|
|
21247
|
-
const regDir =
|
|
21248
|
-
const regBase =
|
|
21249
|
-
registryWatcher = (0,
|
|
21976
|
+
const regDir = import_node_path75.default.dirname(regPath);
|
|
21977
|
+
const regBase = import_node_path75.default.basename(regPath);
|
|
21978
|
+
registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
|
|
21250
21979
|
if (filename !== null && filename !== regBase) return;
|
|
21251
21980
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
21252
21981
|
reloadTimer = setTimeout(() => {
|
|
@@ -21295,7 +22024,7 @@ async function startDaemon(opts = {}) {
|
|
|
21295
22024
|
if (daemonRecord) {
|
|
21296
22025
|
await clearDaemonRecord(daemonRecord, home);
|
|
21297
22026
|
}
|
|
21298
|
-
await
|
|
22027
|
+
await import_node_fs40.promises.unlink(pidPath).catch(() => {
|
|
21299
22028
|
});
|
|
21300
22029
|
};
|
|
21301
22030
|
return {
|
|
@@ -21318,9 +22047,9 @@ init_auth();
|
|
|
21318
22047
|
// src/web-spawn.ts
|
|
21319
22048
|
init_cjs_shims();
|
|
21320
22049
|
var import_node_child_process2 = require("child_process");
|
|
21321
|
-
var
|
|
22050
|
+
var import_node_fs41 = require("fs");
|
|
21322
22051
|
var import_node_net = __toESM(require("net"), 1);
|
|
21323
|
-
var
|
|
22052
|
+
var import_node_path76 = __toESM(require("path"), 1);
|
|
21324
22053
|
var DEFAULT_WEB_PORT = 6328;
|
|
21325
22054
|
var DEFAULT_REST_PORT = 8080;
|
|
21326
22055
|
function asValidPort(value) {
|
|
@@ -21329,11 +22058,11 @@ function asValidPort(value) {
|
|
|
21329
22058
|
}
|
|
21330
22059
|
function projectRoot() {
|
|
21331
22060
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
21332
|
-
return
|
|
22061
|
+
return import_node_path76.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
21333
22062
|
}
|
|
21334
22063
|
async function readDaemonPorts(root) {
|
|
21335
22064
|
try {
|
|
21336
|
-
const raw = await
|
|
22065
|
+
const raw = await import_node_fs41.promises.readFile(import_node_path76.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
21337
22066
|
const parsed = JSON.parse(raw);
|
|
21338
22067
|
const ports = parsed?.ports ?? {};
|
|
21339
22068
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -21378,10 +22107,10 @@ function resolveWebPackageDir() {
|
|
|
21378
22107
|
eval("require")
|
|
21379
22108
|
);
|
|
21380
22109
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
21381
|
-
return
|
|
22110
|
+
return import_node_path76.default.dirname(pkgJsonPath);
|
|
21382
22111
|
}
|
|
21383
22112
|
function resolveStandaloneServerEntry(webDir) {
|
|
21384
|
-
return
|
|
22113
|
+
return import_node_path76.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
21385
22114
|
}
|
|
21386
22115
|
async function pickInternalPort() {
|
|
21387
22116
|
return new Promise((resolve, reject) => {
|
|
@@ -21434,7 +22163,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
21434
22163
|
NEAT_API_URL: apiUrl
|
|
21435
22164
|
};
|
|
21436
22165
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
21437
|
-
cwd:
|
|
22166
|
+
cwd: import_node_path76.default.dirname(serverEntry),
|
|
21438
22167
|
env,
|
|
21439
22168
|
stdio: ["ignore", "inherit", "inherit"],
|
|
21440
22169
|
detached: false
|
|
@@ -21561,15 +22290,15 @@ async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
|
|
|
21561
22290
|
}
|
|
21562
22291
|
function isLocalBehind(local, remote) {
|
|
21563
22292
|
if (local === remote) return false;
|
|
21564
|
-
const
|
|
22293
|
+
const parse11 = (v) => {
|
|
21565
22294
|
const core = v.split(/[-+]/)[0] ?? v;
|
|
21566
22295
|
return core.split(".").map((s) => {
|
|
21567
22296
|
const n = Number.parseInt(s, 10);
|
|
21568
22297
|
return Number.isFinite(n) ? n : 0;
|
|
21569
22298
|
});
|
|
21570
22299
|
};
|
|
21571
|
-
const a =
|
|
21572
|
-
const b =
|
|
22300
|
+
const a = parse11(local);
|
|
22301
|
+
const b = parse11(remote);
|
|
21573
22302
|
const len = Math.max(a.length, b.length);
|
|
21574
22303
|
for (let i = 0; i < len; i++) {
|
|
21575
22304
|
const av = a[i] ?? 0;
|
|
@@ -21610,14 +22339,14 @@ function localVersion() {
|
|
|
21610
22339
|
}
|
|
21611
22340
|
function neatHome3() {
|
|
21612
22341
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
21613
|
-
return
|
|
22342
|
+
return import_node_path77.default.resolve(process.env.NEAT_HOME);
|
|
21614
22343
|
}
|
|
21615
22344
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
21616
|
-
return
|
|
22345
|
+
return import_node_path77.default.join(home, ".neat");
|
|
21617
22346
|
}
|
|
21618
22347
|
async function readPid() {
|
|
21619
22348
|
try {
|
|
21620
|
-
const raw = await
|
|
22349
|
+
const raw = await import_node_fs42.promises.readFile(import_node_path77.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
21621
22350
|
const n = Number.parseInt(raw.trim(), 10);
|
|
21622
22351
|
return Number.isFinite(n) ? n : null;
|
|
21623
22352
|
} catch {
|