@neat.is/core 0.4.12 → 0.4.13
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-WDG4QEFO.js → chunk-5W7H35JJ.js} +557 -232
- package/dist/chunk-5W7H35JJ.js.map +1 -0
- package/dist/{chunk-7JY6F7BY.js → chunk-TWNJX26R.js} +2 -2
- package/dist/cli.cjs +784 -455
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +9 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +606 -284
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +613 -291
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +531 -209
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-WDG4QEFO.js.map +0 -1
- /package/dist/{chunk-7JY6F7BY.js.map → chunk-TWNJX26R.js.map} +0 -0
package/dist/server.cjs
CHANGED
|
@@ -56,9 +56,9 @@ function mountBearerAuth(app, opts) {
|
|
|
56
56
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
57
57
|
const publicRead = opts.publicRead === true;
|
|
58
58
|
app.addHook("preHandler", (req, reply, done) => {
|
|
59
|
-
const
|
|
59
|
+
const path42 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
60
60
|
for (const suffix of suffixes) {
|
|
61
|
-
if (
|
|
61
|
+
if (path42 === suffix || path42.endsWith(suffix)) {
|
|
62
62
|
done();
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
@@ -184,8 +184,8 @@ function reshapeGrpcRequest(req) {
|
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
186
|
function resolveProtoRoot() {
|
|
187
|
-
const here =
|
|
188
|
-
return
|
|
187
|
+
const here = import_node_path38.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
188
|
+
return import_node_path38.default.resolve(here, "..", "proto");
|
|
189
189
|
}
|
|
190
190
|
function loadTraceService() {
|
|
191
191
|
const protoRoot = resolveProtoRoot();
|
|
@@ -253,13 +253,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
253
253
|
})
|
|
254
254
|
};
|
|
255
255
|
}
|
|
256
|
-
var import_node_url,
|
|
256
|
+
var import_node_url, import_node_path38, import_node_crypto2, grpc, protoLoader;
|
|
257
257
|
var init_otel_grpc = __esm({
|
|
258
258
|
"src/otel-grpc.ts"() {
|
|
259
259
|
"use strict";
|
|
260
260
|
init_cjs_shims();
|
|
261
261
|
import_node_url = require("url");
|
|
262
|
-
|
|
262
|
+
import_node_path38 = __toESM(require("path"), 1);
|
|
263
263
|
import_node_crypto2 = require("crypto");
|
|
264
264
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
265
265
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -369,10 +369,10 @@ function parseOtlpRequest(body) {
|
|
|
369
369
|
return out;
|
|
370
370
|
}
|
|
371
371
|
function loadProtoRoot() {
|
|
372
|
-
const here =
|
|
373
|
-
const protoRoot =
|
|
372
|
+
const here = import_node_path39.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
373
|
+
const protoRoot = import_node_path39.default.resolve(here, "..", "proto");
|
|
374
374
|
const root = new import_protobufjs.default.Root();
|
|
375
|
-
root.resolvePath = (_origin, target) =>
|
|
375
|
+
root.resolvePath = (_origin, target) => import_node_path39.default.resolve(protoRoot, target);
|
|
376
376
|
root.loadSync(
|
|
377
377
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
378
378
|
{ keepCase: true }
|
|
@@ -565,12 +565,12 @@ async function buildOtelReceiver(opts) {
|
|
|
565
565
|
};
|
|
566
566
|
return decorated;
|
|
567
567
|
}
|
|
568
|
-
var
|
|
568
|
+
var import_node_path39, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
569
569
|
var init_otel = __esm({
|
|
570
570
|
"src/otel.ts"() {
|
|
571
571
|
"use strict";
|
|
572
572
|
init_cjs_shims();
|
|
573
|
-
|
|
573
|
+
import_node_path39 = __toESM(require("path"), 1);
|
|
574
574
|
import_node_url2 = require("url");
|
|
575
575
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
576
576
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -586,7 +586,7 @@ var init_otel = __esm({
|
|
|
586
586
|
|
|
587
587
|
// src/server.ts
|
|
588
588
|
init_cjs_shims();
|
|
589
|
-
var
|
|
589
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
590
590
|
|
|
591
591
|
// src/graph.ts
|
|
592
592
|
init_cjs_shims();
|
|
@@ -610,7 +610,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
610
610
|
init_cjs_shims();
|
|
611
611
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
612
612
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
613
|
-
var
|
|
613
|
+
var import_types24 = require("@neat.is/types");
|
|
614
614
|
|
|
615
615
|
// src/extend/index.ts
|
|
616
616
|
init_cjs_shims();
|
|
@@ -1353,19 +1353,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1353
1353
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1354
1354
|
let best = { path: [start], edges: [] };
|
|
1355
1355
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1356
|
-
function step(node,
|
|
1357
|
-
if (
|
|
1358
|
-
best = { path: [...
|
|
1356
|
+
function step(node, path42, edges) {
|
|
1357
|
+
if (path42.length > best.path.length) {
|
|
1358
|
+
best = { path: [...path42], edges: [...edges] };
|
|
1359
1359
|
}
|
|
1360
|
-
if (
|
|
1360
|
+
if (path42.length - 1 >= maxDepth) return;
|
|
1361
1361
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1362
1362
|
for (const [srcId, edge] of incoming) {
|
|
1363
1363
|
if (visited.has(srcId)) continue;
|
|
1364
1364
|
visited.add(srcId);
|
|
1365
|
-
|
|
1365
|
+
path42.push(srcId);
|
|
1366
1366
|
edges.push(edge);
|
|
1367
|
-
step(srcId,
|
|
1368
|
-
|
|
1367
|
+
step(srcId, path42, edges);
|
|
1368
|
+
path42.pop();
|
|
1369
1369
|
edges.pop();
|
|
1370
1370
|
visited.delete(srcId);
|
|
1371
1371
|
}
|
|
@@ -3564,10 +3564,8 @@ async function addServiceAliases(graph, scanPath, services) {
|
|
|
3564
3564
|
await collectK8sAliases(graph, scanPath, byName);
|
|
3565
3565
|
}
|
|
3566
3566
|
|
|
3567
|
-
// src/extract/
|
|
3567
|
+
// src/extract/files.ts
|
|
3568
3568
|
init_cjs_shims();
|
|
3569
|
-
var import_node_path20 = __toESM(require("path"), 1);
|
|
3570
|
-
var import_types9 = require("@neat.is/types");
|
|
3571
3569
|
|
|
3572
3570
|
// src/extract/calls/shared.ts
|
|
3573
3571
|
init_cjs_shims();
|
|
@@ -3666,11 +3664,333 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
|
|
|
3666
3664
|
return { fileNodeId, nodesAdded, edgesAdded };
|
|
3667
3665
|
}
|
|
3668
3666
|
|
|
3667
|
+
// src/extract/files.ts
|
|
3668
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
3669
|
+
async function addFiles(graph, services) {
|
|
3670
|
+
let nodesAdded = 0;
|
|
3671
|
+
let edgesAdded = 0;
|
|
3672
|
+
for (const service of services) {
|
|
3673
|
+
const filePaths = await walkSourceFiles(service.dir);
|
|
3674
|
+
for (const filePath of filePaths) {
|
|
3675
|
+
const relPath = toPosix2(import_node_path13.default.relative(service.dir, filePath));
|
|
3676
|
+
const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
3677
|
+
graph,
|
|
3678
|
+
service.pkg.name,
|
|
3679
|
+
service.node.id,
|
|
3680
|
+
relPath
|
|
3681
|
+
);
|
|
3682
|
+
nodesAdded += n;
|
|
3683
|
+
edgesAdded += e;
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
return { nodesAdded, edgesAdded };
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
// src/extract/imports.ts
|
|
3690
|
+
init_cjs_shims();
|
|
3691
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
3692
|
+
var import_node_fs13 = require("fs");
|
|
3693
|
+
var import_tree_sitter = __toESM(require("tree-sitter"), 1);
|
|
3694
|
+
var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
|
|
3695
|
+
var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
|
|
3696
|
+
var import_types9 = require("@neat.is/types");
|
|
3697
|
+
var PARSE_CHUNK = 16384;
|
|
3698
|
+
function parseSource(parser, source) {
|
|
3699
|
+
return parser.parse(
|
|
3700
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK)
|
|
3701
|
+
);
|
|
3702
|
+
}
|
|
3703
|
+
function makeJsParser() {
|
|
3704
|
+
const p = new import_tree_sitter.default();
|
|
3705
|
+
p.setLanguage(import_tree_sitter_javascript.default);
|
|
3706
|
+
return p;
|
|
3707
|
+
}
|
|
3708
|
+
function makePyParser() {
|
|
3709
|
+
const p = new import_tree_sitter.default();
|
|
3710
|
+
p.setLanguage(import_tree_sitter_python.default);
|
|
3711
|
+
return p;
|
|
3712
|
+
}
|
|
3713
|
+
function stringLiteralText(node) {
|
|
3714
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3715
|
+
const child = node.child(i);
|
|
3716
|
+
if (child?.type === "string_fragment") return child.text;
|
|
3717
|
+
}
|
|
3718
|
+
const raw = node.text;
|
|
3719
|
+
if (raw.length >= 2) return raw.slice(1, -1);
|
|
3720
|
+
return raw.length === 0 ? null : "";
|
|
3721
|
+
}
|
|
3722
|
+
function clipSnippet(text) {
|
|
3723
|
+
const oneLine = text.split("\n")[0] ?? text;
|
|
3724
|
+
return oneLine.length > 120 ? oneLine.slice(0, 120) : oneLine;
|
|
3725
|
+
}
|
|
3726
|
+
function collectJsImports(node, out) {
|
|
3727
|
+
if (node.type === "import_statement") {
|
|
3728
|
+
const source = node.childForFieldName("source");
|
|
3729
|
+
if (source) {
|
|
3730
|
+
const specifier = stringLiteralText(source);
|
|
3731
|
+
if (specifier) {
|
|
3732
|
+
out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3737
|
+
if (node.type === "call_expression") {
|
|
3738
|
+
const fn = node.childForFieldName("function");
|
|
3739
|
+
if (fn?.type === "identifier" && fn.text === "require") {
|
|
3740
|
+
const args = node.childForFieldName("arguments");
|
|
3741
|
+
const firstArg = args?.namedChild(0);
|
|
3742
|
+
if (firstArg?.type === "string") {
|
|
3743
|
+
const specifier = stringLiteralText(firstArg);
|
|
3744
|
+
if (specifier) {
|
|
3745
|
+
out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3751
|
+
const child = node.namedChild(i);
|
|
3752
|
+
if (child) collectJsImports(child, out);
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
function collectPyImports(node, out) {
|
|
3756
|
+
if (node.type === "import_from_statement") {
|
|
3757
|
+
let level = 0;
|
|
3758
|
+
let modulePath = "";
|
|
3759
|
+
let pastFrom = false;
|
|
3760
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3761
|
+
const child = node.child(i);
|
|
3762
|
+
if (!child) continue;
|
|
3763
|
+
if (!pastFrom) {
|
|
3764
|
+
if (child.type === "from") pastFrom = true;
|
|
3765
|
+
continue;
|
|
3766
|
+
}
|
|
3767
|
+
if (child.type === "import") break;
|
|
3768
|
+
if (child.type === "relative_import") {
|
|
3769
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
3770
|
+
const rc = child.child(j);
|
|
3771
|
+
if (!rc) continue;
|
|
3772
|
+
if (rc.type === "import_prefix") {
|
|
3773
|
+
for (let k = 0; k < rc.childCount; k++) {
|
|
3774
|
+
if (rc.child(k)?.type === ".") level++;
|
|
3775
|
+
}
|
|
3776
|
+
} else if (rc.type === "dotted_name") modulePath = rc.text;
|
|
3777
|
+
}
|
|
3778
|
+
break;
|
|
3779
|
+
}
|
|
3780
|
+
if (child.type === "dotted_name") {
|
|
3781
|
+
modulePath = child.text;
|
|
3782
|
+
break;
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
if (level > 0 || modulePath) {
|
|
3786
|
+
out.push({ modulePath, level, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3790
|
+
const child = node.namedChild(i);
|
|
3791
|
+
if (child) collectPyImports(child, out);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
async function fileExists2(p) {
|
|
3795
|
+
try {
|
|
3796
|
+
await import_node_fs13.promises.access(p);
|
|
3797
|
+
return true;
|
|
3798
|
+
} catch {
|
|
3799
|
+
return false;
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
function isWithinServiceDir(candidate, serviceDir) {
|
|
3803
|
+
const rel = import_node_path14.default.relative(serviceDir, candidate);
|
|
3804
|
+
return rel !== "" && !rel.startsWith("..") && !import_node_path14.default.isAbsolute(rel);
|
|
3805
|
+
}
|
|
3806
|
+
var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
3807
|
+
var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
|
|
3808
|
+
async function firstExistingCandidate(base, serviceDir) {
|
|
3809
|
+
for (const ext of JS_EXTENSIONS) {
|
|
3810
|
+
const candidate = base + ext;
|
|
3811
|
+
if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
|
|
3812
|
+
return toPosix2(import_node_path14.default.relative(serviceDir, candidate));
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
for (const indexFile of JS_INDEX_FILES) {
|
|
3816
|
+
const candidate = import_node_path14.default.join(base, indexFile);
|
|
3817
|
+
if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
|
|
3818
|
+
return toPosix2(import_node_path14.default.relative(serviceDir, candidate));
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
return null;
|
|
3822
|
+
}
|
|
3823
|
+
async function loadTsPathConfig(serviceDir) {
|
|
3824
|
+
const tsconfigPath = import_node_path14.default.join(serviceDir, "tsconfig.json");
|
|
3825
|
+
let raw;
|
|
3826
|
+
try {
|
|
3827
|
+
raw = await import_node_fs13.promises.readFile(tsconfigPath, "utf8");
|
|
3828
|
+
} catch {
|
|
3829
|
+
return null;
|
|
3830
|
+
}
|
|
3831
|
+
try {
|
|
3832
|
+
const parsed = JSON.parse(raw);
|
|
3833
|
+
const paths = parsed.compilerOptions?.paths;
|
|
3834
|
+
if (!paths || Object.keys(paths).length === 0) return null;
|
|
3835
|
+
const baseUrl = parsed.compilerOptions?.baseUrl;
|
|
3836
|
+
return { paths, baseDir: baseUrl ? import_node_path14.default.resolve(serviceDir, baseUrl) : serviceDir };
|
|
3837
|
+
} catch (err) {
|
|
3838
|
+
recordExtractionError("import alias resolution", tsconfigPath, err);
|
|
3839
|
+
return null;
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
async function resolveTsAlias(specifier, config, serviceDir) {
|
|
3843
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
3844
|
+
let suffix = null;
|
|
3845
|
+
if (pattern === specifier) {
|
|
3846
|
+
suffix = "";
|
|
3847
|
+
} else if (pattern.endsWith("/*")) {
|
|
3848
|
+
const prefix = pattern.slice(0, -1);
|
|
3849
|
+
if (specifier.startsWith(prefix)) suffix = specifier.slice(prefix.length);
|
|
3850
|
+
}
|
|
3851
|
+
if (suffix === null) continue;
|
|
3852
|
+
for (const target of targets) {
|
|
3853
|
+
const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
|
|
3854
|
+
const resolvedBase = import_node_path14.default.resolve(config.baseDir, targetBase, suffix);
|
|
3855
|
+
const hit = await firstExistingCandidate(resolvedBase, serviceDir);
|
|
3856
|
+
if (hit) return hit;
|
|
3857
|
+
if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists2(resolvedBase)) {
|
|
3858
|
+
return toPosix2(import_node_path14.default.relative(serviceDir, resolvedBase));
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
return null;
|
|
3863
|
+
}
|
|
3864
|
+
async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
|
|
3865
|
+
if (!specifier) return null;
|
|
3866
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
3867
|
+
const base = import_node_path14.default.resolve(importerDir, specifier);
|
|
3868
|
+
const ext = import_node_path14.default.extname(specifier);
|
|
3869
|
+
if (ext) {
|
|
3870
|
+
if (ext === ".js" || ext === ".jsx") {
|
|
3871
|
+
const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
|
|
3872
|
+
const tsSibling = base.slice(0, -ext.length) + tsExt;
|
|
3873
|
+
if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists2(tsSibling)) {
|
|
3874
|
+
return toPosix2(import_node_path14.default.relative(serviceDir, tsSibling));
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
if (isWithinServiceDir(base, serviceDir) && await fileExists2(base)) {
|
|
3878
|
+
return toPosix2(import_node_path14.default.relative(serviceDir, base));
|
|
3879
|
+
}
|
|
3880
|
+
return null;
|
|
3881
|
+
}
|
|
3882
|
+
return firstExistingCandidate(base, serviceDir);
|
|
3883
|
+
}
|
|
3884
|
+
if (tsPaths) return resolveTsAlias(specifier, tsPaths, serviceDir);
|
|
3885
|
+
return null;
|
|
3886
|
+
}
|
|
3887
|
+
async function resolvePyImport(imp, importerPath, serviceDir) {
|
|
3888
|
+
if (!imp.modulePath) return null;
|
|
3889
|
+
const relPath = imp.modulePath.split(".").join("/");
|
|
3890
|
+
let baseDir;
|
|
3891
|
+
if (imp.level > 0) {
|
|
3892
|
+
baseDir = import_node_path14.default.dirname(importerPath);
|
|
3893
|
+
for (let i = 1; i < imp.level; i++) baseDir = import_node_path14.default.dirname(baseDir);
|
|
3894
|
+
} else {
|
|
3895
|
+
baseDir = serviceDir;
|
|
3896
|
+
}
|
|
3897
|
+
const candidates = [import_node_path14.default.join(baseDir, `${relPath}.py`), import_node_path14.default.join(baseDir, relPath, "__init__.py")];
|
|
3898
|
+
for (const candidate of candidates) {
|
|
3899
|
+
if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
|
|
3900
|
+
return toPosix2(import_node_path14.default.relative(serviceDir, candidate));
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
return null;
|
|
3904
|
+
}
|
|
3905
|
+
function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
|
|
3906
|
+
const importeeFileId = (0, import_types9.fileId)(serviceName, importeeRelPath);
|
|
3907
|
+
if (!graph.hasNode(importeeFileId)) return 0;
|
|
3908
|
+
const edgeId = (0, import_types9.extractedEdgeId)(importerFileId, importeeFileId, import_types9.EdgeType.IMPORTS);
|
|
3909
|
+
if (graph.hasEdge(edgeId)) return 0;
|
|
3910
|
+
const edge = {
|
|
3911
|
+
id: edgeId,
|
|
3912
|
+
source: importerFileId,
|
|
3913
|
+
target: importeeFileId,
|
|
3914
|
+
type: import_types9.EdgeType.IMPORTS,
|
|
3915
|
+
provenance: import_types9.Provenance.EXTRACTED,
|
|
3916
|
+
confidence: (0, import_types9.confidenceForExtracted)("structural"),
|
|
3917
|
+
evidence: { file: importerRelPath, line, snippet: snippet2 }
|
|
3918
|
+
};
|
|
3919
|
+
graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
|
|
3920
|
+
return 1;
|
|
3921
|
+
}
|
|
3922
|
+
async function addImports(graph, services) {
|
|
3923
|
+
const jsParser = makeJsParser();
|
|
3924
|
+
const pyParser = makePyParser();
|
|
3925
|
+
let edgesAdded = 0;
|
|
3926
|
+
for (const service of services) {
|
|
3927
|
+
const tsPaths = await loadTsPathConfig(service.dir);
|
|
3928
|
+
const files = await loadSourceFiles(service.dir);
|
|
3929
|
+
for (const file of files) {
|
|
3930
|
+
if (isTestPath(file.path)) continue;
|
|
3931
|
+
const relFile = toPosix2(import_node_path14.default.relative(service.dir, file.path));
|
|
3932
|
+
const importerFileId = (0, import_types9.fileId)(service.pkg.name, relFile);
|
|
3933
|
+
const isPython = import_node_path14.default.extname(file.path) === ".py";
|
|
3934
|
+
if (isPython) {
|
|
3935
|
+
let pyImports = [];
|
|
3936
|
+
try {
|
|
3937
|
+
const tree = parseSource(pyParser, file.content);
|
|
3938
|
+
collectPyImports(tree.rootNode, pyImports);
|
|
3939
|
+
} catch (err) {
|
|
3940
|
+
recordExtractionError("import extraction", file.path, err);
|
|
3941
|
+
continue;
|
|
3942
|
+
}
|
|
3943
|
+
for (const imp of pyImports) {
|
|
3944
|
+
const resolved = await resolvePyImport(imp, file.path, service.dir);
|
|
3945
|
+
if (!resolved) continue;
|
|
3946
|
+
edgesAdded += emitImportEdge(
|
|
3947
|
+
graph,
|
|
3948
|
+
service.pkg.name,
|
|
3949
|
+
importerFileId,
|
|
3950
|
+
relFile,
|
|
3951
|
+
resolved,
|
|
3952
|
+
imp.line,
|
|
3953
|
+
imp.snippet
|
|
3954
|
+
);
|
|
3955
|
+
}
|
|
3956
|
+
continue;
|
|
3957
|
+
}
|
|
3958
|
+
let jsImports = [];
|
|
3959
|
+
try {
|
|
3960
|
+
const tree = parseSource(jsParser, file.content);
|
|
3961
|
+
collectJsImports(tree.rootNode, jsImports);
|
|
3962
|
+
} catch (err) {
|
|
3963
|
+
recordExtractionError("import extraction", file.path, err);
|
|
3964
|
+
continue;
|
|
3965
|
+
}
|
|
3966
|
+
for (const imp of jsImports) {
|
|
3967
|
+
const resolved = await resolveJsImport(imp.specifier, import_node_path14.default.dirname(file.path), service.dir, tsPaths);
|
|
3968
|
+
if (!resolved) continue;
|
|
3969
|
+
edgesAdded += emitImportEdge(
|
|
3970
|
+
graph,
|
|
3971
|
+
service.pkg.name,
|
|
3972
|
+
importerFileId,
|
|
3973
|
+
relFile,
|
|
3974
|
+
resolved,
|
|
3975
|
+
imp.line,
|
|
3976
|
+
imp.snippet
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
return { nodesAdded: 0, edgesAdded };
|
|
3982
|
+
}
|
|
3983
|
+
|
|
3984
|
+
// src/extract/databases/index.ts
|
|
3985
|
+
init_cjs_shims();
|
|
3986
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
3987
|
+
var import_types10 = require("@neat.is/types");
|
|
3988
|
+
|
|
3669
3989
|
// src/extract/databases/db-config-yaml.ts
|
|
3670
3990
|
init_cjs_shims();
|
|
3671
|
-
var
|
|
3991
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
3672
3992
|
async function parse(serviceDir) {
|
|
3673
|
-
const yamlPath =
|
|
3993
|
+
const yamlPath = import_node_path15.default.join(serviceDir, "db-config.yaml");
|
|
3674
3994
|
if (!await exists2(yamlPath)) return [];
|
|
3675
3995
|
const raw = await readYaml(yamlPath);
|
|
3676
3996
|
return [
|
|
@@ -3688,13 +4008,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
|
|
|
3688
4008
|
|
|
3689
4009
|
// src/extract/databases/dotenv.ts
|
|
3690
4010
|
init_cjs_shims();
|
|
3691
|
-
var
|
|
3692
|
-
var
|
|
4011
|
+
var import_node_fs15 = require("fs");
|
|
4012
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
3693
4013
|
|
|
3694
4014
|
// src/extract/databases/shared.ts
|
|
3695
4015
|
init_cjs_shims();
|
|
3696
|
-
var
|
|
3697
|
-
var
|
|
4016
|
+
var import_node_fs14 = require("fs");
|
|
4017
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
3698
4018
|
function schemeToEngine(scheme) {
|
|
3699
4019
|
const s = scheme.toLowerCase().split("+")[0];
|
|
3700
4020
|
switch (s) {
|
|
@@ -3733,14 +4053,14 @@ function parseConnectionString(url) {
|
|
|
3733
4053
|
}
|
|
3734
4054
|
async function readIfExists(filePath) {
|
|
3735
4055
|
try {
|
|
3736
|
-
return await
|
|
4056
|
+
return await import_node_fs14.promises.readFile(filePath, "utf8");
|
|
3737
4057
|
} catch {
|
|
3738
4058
|
return null;
|
|
3739
4059
|
}
|
|
3740
4060
|
}
|
|
3741
4061
|
async function findFirst(serviceDir, candidates) {
|
|
3742
4062
|
for (const rel of candidates) {
|
|
3743
|
-
const abs =
|
|
4063
|
+
const abs = import_node_path16.default.join(serviceDir, rel);
|
|
3744
4064
|
const content = await readIfExists(abs);
|
|
3745
4065
|
if (content !== null) return abs;
|
|
3746
4066
|
}
|
|
@@ -3791,15 +4111,15 @@ function parseDotenvLine(line) {
|
|
|
3791
4111
|
return { key, value };
|
|
3792
4112
|
}
|
|
3793
4113
|
async function parse2(serviceDir) {
|
|
3794
|
-
const entries = await
|
|
4114
|
+
const entries = await import_node_fs15.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
3795
4115
|
const configs = [];
|
|
3796
4116
|
const seen = /* @__PURE__ */ new Set();
|
|
3797
4117
|
for (const entry of entries) {
|
|
3798
4118
|
if (!entry.isFile()) continue;
|
|
3799
4119
|
const match = isConfigFile(entry.name);
|
|
3800
4120
|
if (!match.match || match.fileType !== "env") continue;
|
|
3801
|
-
const filePath =
|
|
3802
|
-
const content = await
|
|
4121
|
+
const filePath = import_node_path17.default.join(serviceDir, entry.name);
|
|
4122
|
+
const content = await import_node_fs15.promises.readFile(filePath, "utf8");
|
|
3803
4123
|
for (const line of content.split("\n")) {
|
|
3804
4124
|
const parsed = parseDotenvLine(line);
|
|
3805
4125
|
if (!parsed) continue;
|
|
@@ -3818,9 +4138,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
|
|
|
3818
4138
|
|
|
3819
4139
|
// src/extract/databases/prisma.ts
|
|
3820
4140
|
init_cjs_shims();
|
|
3821
|
-
var
|
|
4141
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
3822
4142
|
async function parse3(serviceDir) {
|
|
3823
|
-
const schemaPath =
|
|
4143
|
+
const schemaPath = import_node_path18.default.join(serviceDir, "prisma", "schema.prisma");
|
|
3824
4144
|
const content = await readIfExists(schemaPath);
|
|
3825
4145
|
if (!content) return [];
|
|
3826
4146
|
const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
|
|
@@ -3952,10 +4272,10 @@ var knexParser = { name: "knex", parse: parse5 };
|
|
|
3952
4272
|
|
|
3953
4273
|
// src/extract/databases/ormconfig.ts
|
|
3954
4274
|
init_cjs_shims();
|
|
3955
|
-
var
|
|
4275
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
3956
4276
|
async function parse6(serviceDir) {
|
|
3957
4277
|
for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
|
|
3958
|
-
const abs =
|
|
4278
|
+
const abs = import_node_path19.default.join(serviceDir, candidate);
|
|
3959
4279
|
if (!await exists2(abs)) continue;
|
|
3960
4280
|
const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
|
|
3961
4281
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
@@ -4015,9 +4335,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
|
|
|
4015
4335
|
|
|
4016
4336
|
// src/extract/databases/sequelize.ts
|
|
4017
4337
|
init_cjs_shims();
|
|
4018
|
-
var
|
|
4338
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
4019
4339
|
async function parse8(serviceDir) {
|
|
4020
|
-
const configPath =
|
|
4340
|
+
const configPath = import_node_path20.default.join(serviceDir, "config", "config.json");
|
|
4021
4341
|
if (!await exists2(configPath)) return [];
|
|
4022
4342
|
const raw = await readJson(configPath);
|
|
4023
4343
|
const out = [];
|
|
@@ -4044,7 +4364,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
|
4044
4364
|
|
|
4045
4365
|
// src/extract/databases/docker-compose.ts
|
|
4046
4366
|
init_cjs_shims();
|
|
4047
|
-
var
|
|
4367
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
4048
4368
|
function portFromService(svc) {
|
|
4049
4369
|
for (const raw of svc.ports ?? []) {
|
|
4050
4370
|
const str = String(raw);
|
|
@@ -4071,7 +4391,7 @@ function databaseFromEnv(svc) {
|
|
|
4071
4391
|
}
|
|
4072
4392
|
async function parse9(serviceDir) {
|
|
4073
4393
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4074
|
-
const abs =
|
|
4394
|
+
const abs = import_node_path21.default.join(serviceDir, name);
|
|
4075
4395
|
if (!await exists2(abs)) continue;
|
|
4076
4396
|
const raw = await readYaml(abs);
|
|
4077
4397
|
if (!raw?.services) return [];
|
|
@@ -4112,8 +4432,8 @@ function compatibleDriversFor(engine) {
|
|
|
4112
4432
|
}
|
|
4113
4433
|
function toDatabaseNode(config) {
|
|
4114
4434
|
return {
|
|
4115
|
-
id: (0,
|
|
4116
|
-
type:
|
|
4435
|
+
id: (0, import_types10.databaseId)(config.host),
|
|
4436
|
+
type: import_types10.NodeType.DatabaseNode,
|
|
4117
4437
|
name: config.database || config.host,
|
|
4118
4438
|
engine: config.engine,
|
|
4119
4439
|
engineVersion: config.engineVersion,
|
|
@@ -4243,7 +4563,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
4243
4563
|
discoveredVia: mergedDiscoveredVia
|
|
4244
4564
|
});
|
|
4245
4565
|
}
|
|
4246
|
-
const relConfigFile = toPosix2(
|
|
4566
|
+
const relConfigFile = toPosix2(import_node_path22.default.relative(service.dir, config.sourceFile));
|
|
4247
4567
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4248
4568
|
graph,
|
|
4249
4569
|
service.pkg.name,
|
|
@@ -4252,14 +4572,14 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
4252
4572
|
);
|
|
4253
4573
|
nodesAdded += fn;
|
|
4254
4574
|
edgesAdded += fe;
|
|
4255
|
-
const evidenceFile = toPosix2(
|
|
4575
|
+
const evidenceFile = toPosix2(import_node_path22.default.relative(scanPath, config.sourceFile));
|
|
4256
4576
|
const edge = {
|
|
4257
|
-
id: (0, import_types5.extractedEdgeId)(fileNodeId, dbNode.id,
|
|
4577
|
+
id: (0, import_types5.extractedEdgeId)(fileNodeId, dbNode.id, import_types10.EdgeType.CONNECTS_TO),
|
|
4258
4578
|
source: fileNodeId,
|
|
4259
4579
|
target: dbNode.id,
|
|
4260
|
-
type:
|
|
4261
|
-
provenance:
|
|
4262
|
-
confidence: (0,
|
|
4580
|
+
type: import_types10.EdgeType.CONNECTS_TO,
|
|
4581
|
+
provenance: import_types10.Provenance.EXTRACTED,
|
|
4582
|
+
confidence: (0, import_types10.confidenceForExtracted)("structural"),
|
|
4263
4583
|
evidence: { file: evidenceFile }
|
|
4264
4584
|
};
|
|
4265
4585
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -4286,15 +4606,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
4286
4606
|
|
|
4287
4607
|
// src/extract/configs.ts
|
|
4288
4608
|
init_cjs_shims();
|
|
4289
|
-
var
|
|
4290
|
-
var
|
|
4291
|
-
var
|
|
4609
|
+
var import_node_fs16 = require("fs");
|
|
4610
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
4611
|
+
var import_types11 = require("@neat.is/types");
|
|
4292
4612
|
async function walkConfigFiles(dir) {
|
|
4293
4613
|
const out = [];
|
|
4294
4614
|
async function walk(current) {
|
|
4295
|
-
const entries = await
|
|
4615
|
+
const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true });
|
|
4296
4616
|
for (const entry of entries) {
|
|
4297
|
-
const full =
|
|
4617
|
+
const full = import_node_path23.default.join(current, entry.name);
|
|
4298
4618
|
if (entry.isDirectory()) {
|
|
4299
4619
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4300
4620
|
if (await isPythonVenvDir(full)) continue;
|
|
@@ -4313,19 +4633,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4313
4633
|
for (const service of services) {
|
|
4314
4634
|
const configFiles = await walkConfigFiles(service.dir);
|
|
4315
4635
|
for (const file of configFiles) {
|
|
4316
|
-
const relPath =
|
|
4636
|
+
const relPath = import_node_path23.default.relative(scanPath, file);
|
|
4317
4637
|
const node = {
|
|
4318
|
-
id: (0,
|
|
4319
|
-
type:
|
|
4320
|
-
name:
|
|
4638
|
+
id: (0, import_types11.configId)(relPath),
|
|
4639
|
+
type: import_types11.NodeType.ConfigNode,
|
|
4640
|
+
name: import_node_path23.default.basename(file),
|
|
4321
4641
|
path: relPath,
|
|
4322
|
-
fileType: isConfigFile(
|
|
4642
|
+
fileType: isConfigFile(import_node_path23.default.basename(file)).fileType
|
|
4323
4643
|
};
|
|
4324
4644
|
if (!graph.hasNode(node.id)) {
|
|
4325
4645
|
graph.addNode(node.id, node);
|
|
4326
4646
|
nodesAdded++;
|
|
4327
4647
|
}
|
|
4328
|
-
const relToService = toPosix2(
|
|
4648
|
+
const relToService = toPosix2(import_node_path23.default.relative(service.dir, file));
|
|
4329
4649
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4330
4650
|
graph,
|
|
4331
4651
|
service.pkg.name,
|
|
@@ -4335,13 +4655,13 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4335
4655
|
nodesAdded += fn;
|
|
4336
4656
|
edgesAdded += fe;
|
|
4337
4657
|
const edge = {
|
|
4338
|
-
id: (0, import_types5.extractedEdgeId)(fileNodeId, node.id,
|
|
4658
|
+
id: (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types11.EdgeType.CONFIGURED_BY),
|
|
4339
4659
|
source: fileNodeId,
|
|
4340
4660
|
target: node.id,
|
|
4341
|
-
type:
|
|
4342
|
-
provenance:
|
|
4343
|
-
confidence: (0,
|
|
4344
|
-
evidence: { file: relPath.split(
|
|
4661
|
+
type: import_types11.EdgeType.CONFIGURED_BY,
|
|
4662
|
+
provenance: import_types11.Provenance.EXTRACTED,
|
|
4663
|
+
confidence: (0, import_types11.confidenceForExtracted)("structural"),
|
|
4664
|
+
evidence: { file: relPath.split(import_node_path23.default.sep).join("/") }
|
|
4345
4665
|
};
|
|
4346
4666
|
if (!graph.hasEdge(edge.id)) {
|
|
4347
4667
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -4354,15 +4674,15 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4354
4674
|
|
|
4355
4675
|
// src/extract/calls/index.ts
|
|
4356
4676
|
init_cjs_shims();
|
|
4357
|
-
var
|
|
4677
|
+
var import_types17 = require("@neat.is/types");
|
|
4358
4678
|
|
|
4359
4679
|
// src/extract/calls/http.ts
|
|
4360
4680
|
init_cjs_shims();
|
|
4361
|
-
var
|
|
4362
|
-
var
|
|
4363
|
-
var
|
|
4364
|
-
var
|
|
4365
|
-
var
|
|
4681
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
4682
|
+
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
4683
|
+
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
4684
|
+
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
4685
|
+
var import_types12 = require("@neat.is/types");
|
|
4366
4686
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
4367
4687
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
4368
4688
|
function isInsideJsxExternalLink(node) {
|
|
@@ -4390,14 +4710,14 @@ function collectStringLiterals(node, out) {
|
|
|
4390
4710
|
if (child) collectStringLiterals(child, out);
|
|
4391
4711
|
}
|
|
4392
4712
|
}
|
|
4393
|
-
var
|
|
4394
|
-
function
|
|
4713
|
+
var PARSE_CHUNK2 = 16384;
|
|
4714
|
+
function parseSource2(parser, source) {
|
|
4395
4715
|
return parser.parse(
|
|
4396
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
4716
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK2)
|
|
4397
4717
|
);
|
|
4398
4718
|
}
|
|
4399
4719
|
function callsFromSource(source, parser, knownHosts) {
|
|
4400
|
-
const tree =
|
|
4720
|
+
const tree = parseSource2(parser, source);
|
|
4401
4721
|
const literals = [];
|
|
4402
4722
|
collectStringLiterals(tree.rootNode, literals);
|
|
4403
4723
|
const out = [];
|
|
@@ -4411,25 +4731,25 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
4411
4731
|
}
|
|
4412
4732
|
return out;
|
|
4413
4733
|
}
|
|
4414
|
-
function
|
|
4415
|
-
const p = new
|
|
4416
|
-
p.setLanguage(
|
|
4734
|
+
function makeJsParser2() {
|
|
4735
|
+
const p = new import_tree_sitter2.default();
|
|
4736
|
+
p.setLanguage(import_tree_sitter_javascript2.default);
|
|
4417
4737
|
return p;
|
|
4418
4738
|
}
|
|
4419
|
-
function
|
|
4420
|
-
const p = new
|
|
4421
|
-
p.setLanguage(
|
|
4739
|
+
function makePyParser2() {
|
|
4740
|
+
const p = new import_tree_sitter2.default();
|
|
4741
|
+
p.setLanguage(import_tree_sitter_python2.default);
|
|
4422
4742
|
return p;
|
|
4423
4743
|
}
|
|
4424
4744
|
async function addHttpCallEdges(graph, services) {
|
|
4425
|
-
const jsParser =
|
|
4426
|
-
const pyParser =
|
|
4745
|
+
const jsParser = makeJsParser2();
|
|
4746
|
+
const pyParser = makePyParser2();
|
|
4427
4747
|
const knownHosts = /* @__PURE__ */ new Set();
|
|
4428
4748
|
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
4429
4749
|
for (const service of services) {
|
|
4430
|
-
knownHosts.add(
|
|
4750
|
+
knownHosts.add(import_node_path24.default.basename(service.dir));
|
|
4431
4751
|
knownHosts.add(service.pkg.name);
|
|
4432
|
-
hostToNodeId.set(
|
|
4752
|
+
hostToNodeId.set(import_node_path24.default.basename(service.dir), service.node.id);
|
|
4433
4753
|
hostToNodeId.set(service.pkg.name, service.node.id);
|
|
4434
4754
|
}
|
|
4435
4755
|
let nodesAdded = 0;
|
|
@@ -4439,7 +4759,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
4439
4759
|
const seen = /* @__PURE__ */ new Set();
|
|
4440
4760
|
for (const file of files) {
|
|
4441
4761
|
if (isTestPath(file.path)) continue;
|
|
4442
|
-
const parser =
|
|
4762
|
+
const parser = import_node_path24.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
4443
4763
|
let sites;
|
|
4444
4764
|
try {
|
|
4445
4765
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -4448,14 +4768,14 @@ async function addHttpCallEdges(graph, services) {
|
|
|
4448
4768
|
continue;
|
|
4449
4769
|
}
|
|
4450
4770
|
if (sites.length === 0) continue;
|
|
4451
|
-
const relFile = toPosix2(
|
|
4771
|
+
const relFile = toPosix2(import_node_path24.default.relative(service.dir, file.path));
|
|
4452
4772
|
for (const site of sites) {
|
|
4453
4773
|
const targetId = hostToNodeId.get(site.host);
|
|
4454
4774
|
if (!targetId || targetId === service.node.id) continue;
|
|
4455
4775
|
const dedupKey = `${relFile}|${targetId}`;
|
|
4456
4776
|
if (seen.has(dedupKey)) continue;
|
|
4457
4777
|
seen.add(dedupKey);
|
|
4458
|
-
const confidence = (0,
|
|
4778
|
+
const confidence = (0, import_types12.confidenceForExtracted)("hostname-shape-match");
|
|
4459
4779
|
const ev = {
|
|
4460
4780
|
file: relFile,
|
|
4461
4781
|
line: site.line,
|
|
@@ -4469,25 +4789,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
4469
4789
|
);
|
|
4470
4790
|
nodesAdded += n;
|
|
4471
4791
|
edgesAdded += e;
|
|
4472
|
-
if (!(0,
|
|
4792
|
+
if (!(0, import_types12.passesExtractedFloor)(confidence)) {
|
|
4473
4793
|
noteExtractedDropped({
|
|
4474
4794
|
source: fileNodeId,
|
|
4475
4795
|
target: targetId,
|
|
4476
|
-
type:
|
|
4796
|
+
type: import_types12.EdgeType.CALLS,
|
|
4477
4797
|
confidence,
|
|
4478
4798
|
confidenceKind: "hostname-shape-match",
|
|
4479
4799
|
evidence: ev
|
|
4480
4800
|
});
|
|
4481
4801
|
continue;
|
|
4482
4802
|
}
|
|
4483
|
-
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, targetId,
|
|
4803
|
+
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, targetId, import_types12.EdgeType.CALLS);
|
|
4484
4804
|
if (!graph.hasEdge(edgeId)) {
|
|
4485
4805
|
const edge = {
|
|
4486
4806
|
id: edgeId,
|
|
4487
4807
|
source: fileNodeId,
|
|
4488
4808
|
target: targetId,
|
|
4489
|
-
type:
|
|
4490
|
-
provenance:
|
|
4809
|
+
type: import_types12.EdgeType.CALLS,
|
|
4810
|
+
provenance: import_types12.Provenance.EXTRACTED,
|
|
4491
4811
|
confidence,
|
|
4492
4812
|
evidence: ev
|
|
4493
4813
|
};
|
|
@@ -4502,8 +4822,8 @@ async function addHttpCallEdges(graph, services) {
|
|
|
4502
4822
|
|
|
4503
4823
|
// src/extract/calls/kafka.ts
|
|
4504
4824
|
init_cjs_shims();
|
|
4505
|
-
var
|
|
4506
|
-
var
|
|
4825
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
4826
|
+
var import_types13 = require("@neat.is/types");
|
|
4507
4827
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
4508
4828
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
4509
4829
|
function findAll(re, text) {
|
|
@@ -4524,7 +4844,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
4524
4844
|
seen.add(key);
|
|
4525
4845
|
const line = lineOf(file.content, topic);
|
|
4526
4846
|
out.push({
|
|
4527
|
-
infraId: (0,
|
|
4847
|
+
infraId: (0, import_types13.infraId)("kafka-topic", topic),
|
|
4528
4848
|
name: topic,
|
|
4529
4849
|
kind: "kafka-topic",
|
|
4530
4850
|
edgeType,
|
|
@@ -4533,7 +4853,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
4533
4853
|
// tier (ADR-066).
|
|
4534
4854
|
confidenceKind: "verified-call-site",
|
|
4535
4855
|
evidence: {
|
|
4536
|
-
file:
|
|
4856
|
+
file: import_node_path25.default.relative(serviceDir, file.path),
|
|
4537
4857
|
line,
|
|
4538
4858
|
snippet: snippet(file.content, line)
|
|
4539
4859
|
}
|
|
@@ -4546,8 +4866,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
4546
4866
|
|
|
4547
4867
|
// src/extract/calls/redis.ts
|
|
4548
4868
|
init_cjs_shims();
|
|
4549
|
-
var
|
|
4550
|
-
var
|
|
4869
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
4870
|
+
var import_types14 = require("@neat.is/types");
|
|
4551
4871
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
4552
4872
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
4553
4873
|
const out = [];
|
|
@@ -4560,7 +4880,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
4560
4880
|
seen.add(host);
|
|
4561
4881
|
const line = lineOf(file.content, host);
|
|
4562
4882
|
out.push({
|
|
4563
|
-
infraId: (0,
|
|
4883
|
+
infraId: (0, import_types14.infraId)("redis", host),
|
|
4564
4884
|
name: host,
|
|
4565
4885
|
kind: "redis",
|
|
4566
4886
|
edgeType: "CALLS",
|
|
@@ -4569,7 +4889,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
4569
4889
|
// support tier (ADR-066).
|
|
4570
4890
|
confidenceKind: "url-with-structural-support",
|
|
4571
4891
|
evidence: {
|
|
4572
|
-
file:
|
|
4892
|
+
file: import_node_path26.default.relative(serviceDir, file.path),
|
|
4573
4893
|
line,
|
|
4574
4894
|
snippet: snippet(file.content, line)
|
|
4575
4895
|
}
|
|
@@ -4580,8 +4900,8 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
4580
4900
|
|
|
4581
4901
|
// src/extract/calls/aws.ts
|
|
4582
4902
|
init_cjs_shims();
|
|
4583
|
-
var
|
|
4584
|
-
var
|
|
4903
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
4904
|
+
var import_types15 = require("@neat.is/types");
|
|
4585
4905
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
4586
4906
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
4587
4907
|
function hasMarker(text, markers) {
|
|
@@ -4605,7 +4925,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
4605
4925
|
seen.add(key);
|
|
4606
4926
|
const line = lineOf(file.content, name);
|
|
4607
4927
|
out.push({
|
|
4608
|
-
infraId: (0,
|
|
4928
|
+
infraId: (0, import_types15.infraId)(kind, name),
|
|
4609
4929
|
name,
|
|
4610
4930
|
kind,
|
|
4611
4931
|
edgeType: "CALLS",
|
|
@@ -4614,7 +4934,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
4614
4934
|
// (ADR-066).
|
|
4615
4935
|
confidenceKind: "verified-call-site",
|
|
4616
4936
|
evidence: {
|
|
4617
|
-
file:
|
|
4937
|
+
file: import_node_path27.default.relative(serviceDir, file.path),
|
|
4618
4938
|
line,
|
|
4619
4939
|
snippet: snippet(file.content, line)
|
|
4620
4940
|
}
|
|
@@ -4639,8 +4959,8 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
4639
4959
|
|
|
4640
4960
|
// src/extract/calls/grpc.ts
|
|
4641
4961
|
init_cjs_shims();
|
|
4642
|
-
var
|
|
4643
|
-
var
|
|
4962
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
4963
|
+
var import_types16 = require("@neat.is/types");
|
|
4644
4964
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
4645
4965
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
4646
4966
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -4689,7 +5009,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4689
5009
|
const { kind } = classified;
|
|
4690
5010
|
const line = lineOf(file.content, m[0]);
|
|
4691
5011
|
out.push({
|
|
4692
|
-
infraId: (0,
|
|
5012
|
+
infraId: (0, import_types16.infraId)(kind, name),
|
|
4693
5013
|
name,
|
|
4694
5014
|
kind,
|
|
4695
5015
|
edgeType: "CALLS",
|
|
@@ -4698,7 +5018,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4698
5018
|
// tier (ADR-066).
|
|
4699
5019
|
confidenceKind: "verified-call-site",
|
|
4700
5020
|
evidence: {
|
|
4701
|
-
file:
|
|
5021
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
4702
5022
|
line,
|
|
4703
5023
|
snippet: snippet(file.content, line)
|
|
4704
5024
|
}
|
|
@@ -4711,11 +5031,11 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4711
5031
|
function edgeTypeFromEndpoint(ep) {
|
|
4712
5032
|
switch (ep.edgeType) {
|
|
4713
5033
|
case "PUBLISHES_TO":
|
|
4714
|
-
return
|
|
5034
|
+
return import_types17.EdgeType.PUBLISHES_TO;
|
|
4715
5035
|
case "CONSUMES_FROM":
|
|
4716
|
-
return
|
|
5036
|
+
return import_types17.EdgeType.CONSUMES_FROM;
|
|
4717
5037
|
default:
|
|
4718
|
-
return
|
|
5038
|
+
return import_types17.EdgeType.CALLS;
|
|
4719
5039
|
}
|
|
4720
5040
|
}
|
|
4721
5041
|
function isAwsKind(kind) {
|
|
@@ -4742,7 +5062,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4742
5062
|
if (!graph.hasNode(ep.infraId)) {
|
|
4743
5063
|
const node = {
|
|
4744
5064
|
id: ep.infraId,
|
|
4745
|
-
type:
|
|
5065
|
+
type: import_types17.NodeType.InfraNode,
|
|
4746
5066
|
name: ep.name,
|
|
4747
5067
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4748
5068
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4754,7 +5074,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4754
5074
|
nodesAdded++;
|
|
4755
5075
|
}
|
|
4756
5076
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4757
|
-
const confidence = (0,
|
|
5077
|
+
const confidence = (0, import_types17.confidenceForExtracted)(ep.confidenceKind);
|
|
4758
5078
|
const relFile = toPosix2(ep.evidence.file);
|
|
4759
5079
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4760
5080
|
graph,
|
|
@@ -4764,7 +5084,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4764
5084
|
);
|
|
4765
5085
|
nodesAdded += n;
|
|
4766
5086
|
edgesAdded += e;
|
|
4767
|
-
if (!(0,
|
|
5087
|
+
if (!(0, import_types17.passesExtractedFloor)(confidence)) {
|
|
4768
5088
|
noteExtractedDropped({
|
|
4769
5089
|
source: fileNodeId,
|
|
4770
5090
|
target: ep.infraId,
|
|
@@ -4784,7 +5104,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4784
5104
|
source: fileNodeId,
|
|
4785
5105
|
target: ep.infraId,
|
|
4786
5106
|
type: edgeType,
|
|
4787
|
-
provenance:
|
|
5107
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
4788
5108
|
confidence,
|
|
4789
5109
|
evidence: ep.evidence
|
|
4790
5110
|
};
|
|
@@ -4809,16 +5129,16 @@ init_cjs_shims();
|
|
|
4809
5129
|
|
|
4810
5130
|
// src/extract/infra/docker-compose.ts
|
|
4811
5131
|
init_cjs_shims();
|
|
4812
|
-
var
|
|
4813
|
-
var
|
|
5132
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
5133
|
+
var import_types19 = require("@neat.is/types");
|
|
4814
5134
|
|
|
4815
5135
|
// src/extract/infra/shared.ts
|
|
4816
5136
|
init_cjs_shims();
|
|
4817
|
-
var
|
|
5137
|
+
var import_types18 = require("@neat.is/types");
|
|
4818
5138
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4819
5139
|
return {
|
|
4820
|
-
id: (0,
|
|
4821
|
-
type:
|
|
5140
|
+
id: (0, import_types18.infraId)(kind, name),
|
|
5141
|
+
type: import_types18.NodeType.InfraNode,
|
|
4822
5142
|
name,
|
|
4823
5143
|
provider,
|
|
4824
5144
|
kind,
|
|
@@ -4847,7 +5167,7 @@ function dependsOnList(value) {
|
|
|
4847
5167
|
}
|
|
4848
5168
|
function serviceNameToServiceNode(name, services) {
|
|
4849
5169
|
for (const s of services) {
|
|
4850
|
-
if (s.node.name === name ||
|
|
5170
|
+
if (s.node.name === name || import_node_path29.default.basename(s.dir) === name) return s.node.id;
|
|
4851
5171
|
}
|
|
4852
5172
|
return null;
|
|
4853
5173
|
}
|
|
@@ -4856,7 +5176,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4856
5176
|
let edgesAdded = 0;
|
|
4857
5177
|
let composePath = null;
|
|
4858
5178
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4859
|
-
const abs =
|
|
5179
|
+
const abs = import_node_path29.default.join(scanPath, name);
|
|
4860
5180
|
if (await exists2(abs)) {
|
|
4861
5181
|
composePath = abs;
|
|
4862
5182
|
break;
|
|
@@ -4869,13 +5189,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4869
5189
|
} catch (err) {
|
|
4870
5190
|
recordExtractionError(
|
|
4871
5191
|
"infra docker-compose",
|
|
4872
|
-
|
|
5192
|
+
import_node_path29.default.relative(scanPath, composePath),
|
|
4873
5193
|
err
|
|
4874
5194
|
);
|
|
4875
5195
|
return { nodesAdded, edgesAdded };
|
|
4876
5196
|
}
|
|
4877
5197
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4878
|
-
const evidenceFile =
|
|
5198
|
+
const evidenceFile = import_node_path29.default.relative(scanPath, composePath).split(import_node_path29.default.sep).join("/");
|
|
4879
5199
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4880
5200
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4881
5201
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4897,15 +5217,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4897
5217
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4898
5218
|
const targetId = composeNameToNodeId.get(dep);
|
|
4899
5219
|
if (!targetId) continue;
|
|
4900
|
-
const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId,
|
|
5220
|
+
const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types19.EdgeType.DEPENDS_ON);
|
|
4901
5221
|
if (graph.hasEdge(edgeId)) continue;
|
|
4902
5222
|
const edge = {
|
|
4903
5223
|
id: edgeId,
|
|
4904
5224
|
source: sourceId,
|
|
4905
5225
|
target: targetId,
|
|
4906
|
-
type:
|
|
4907
|
-
provenance:
|
|
4908
|
-
confidence: (0,
|
|
5226
|
+
type: import_types19.EdgeType.DEPENDS_ON,
|
|
5227
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
5228
|
+
confidence: (0, import_types19.confidenceForExtracted)("structural"),
|
|
4909
5229
|
evidence: { file: evidenceFile }
|
|
4910
5230
|
};
|
|
4911
5231
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4917,9 +5237,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4917
5237
|
|
|
4918
5238
|
// src/extract/infra/dockerfile.ts
|
|
4919
5239
|
init_cjs_shims();
|
|
4920
|
-
var
|
|
4921
|
-
var
|
|
4922
|
-
var
|
|
5240
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
5241
|
+
var import_node_fs17 = require("fs");
|
|
5242
|
+
var import_types20 = require("@neat.is/types");
|
|
4923
5243
|
function runtimeImage(content) {
|
|
4924
5244
|
const lines = content.split("\n");
|
|
4925
5245
|
let last = null;
|
|
@@ -4938,15 +5258,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4938
5258
|
let nodesAdded = 0;
|
|
4939
5259
|
let edgesAdded = 0;
|
|
4940
5260
|
for (const service of services) {
|
|
4941
|
-
const dockerfilePath =
|
|
5261
|
+
const dockerfilePath = import_node_path30.default.join(service.dir, "Dockerfile");
|
|
4942
5262
|
if (!await exists2(dockerfilePath)) continue;
|
|
4943
5263
|
let content;
|
|
4944
5264
|
try {
|
|
4945
|
-
content = await
|
|
5265
|
+
content = await import_node_fs17.promises.readFile(dockerfilePath, "utf8");
|
|
4946
5266
|
} catch (err) {
|
|
4947
5267
|
recordExtractionError(
|
|
4948
5268
|
"infra dockerfile",
|
|
4949
|
-
|
|
5269
|
+
import_node_path30.default.relative(scanPath, dockerfilePath),
|
|
4950
5270
|
err
|
|
4951
5271
|
);
|
|
4952
5272
|
continue;
|
|
@@ -4958,7 +5278,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4958
5278
|
graph.addNode(node.id, node);
|
|
4959
5279
|
nodesAdded++;
|
|
4960
5280
|
}
|
|
4961
|
-
const relDockerfile = toPosix2(
|
|
5281
|
+
const relDockerfile = toPosix2(import_node_path30.default.relative(service.dir, dockerfilePath));
|
|
4962
5282
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4963
5283
|
graph,
|
|
4964
5284
|
service.pkg.name,
|
|
@@ -4967,17 +5287,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4967
5287
|
);
|
|
4968
5288
|
nodesAdded += fn;
|
|
4969
5289
|
edgesAdded += fe;
|
|
4970
|
-
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id,
|
|
5290
|
+
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types20.EdgeType.RUNS_ON);
|
|
4971
5291
|
if (!graph.hasEdge(edgeId)) {
|
|
4972
5292
|
const edge = {
|
|
4973
5293
|
id: edgeId,
|
|
4974
5294
|
source: fileNodeId,
|
|
4975
5295
|
target: node.id,
|
|
4976
|
-
type:
|
|
4977
|
-
provenance:
|
|
4978
|
-
confidence: (0,
|
|
5296
|
+
type: import_types20.EdgeType.RUNS_ON,
|
|
5297
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
5298
|
+
confidence: (0, import_types20.confidenceForExtracted)("structural"),
|
|
4979
5299
|
evidence: {
|
|
4980
|
-
file: toPosix2(
|
|
5300
|
+
file: toPosix2(import_node_path30.default.relative(scanPath, dockerfilePath))
|
|
4981
5301
|
}
|
|
4982
5302
|
};
|
|
4983
5303
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4989,21 +5309,21 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4989
5309
|
|
|
4990
5310
|
// src/extract/infra/terraform.ts
|
|
4991
5311
|
init_cjs_shims();
|
|
4992
|
-
var
|
|
4993
|
-
var
|
|
5312
|
+
var import_node_fs18 = require("fs");
|
|
5313
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4994
5314
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4995
5315
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4996
5316
|
if (depth > max) return [];
|
|
4997
5317
|
const out = [];
|
|
4998
|
-
const entries = await
|
|
5318
|
+
const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
4999
5319
|
for (const entry of entries) {
|
|
5000
5320
|
if (entry.isDirectory()) {
|
|
5001
5321
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
5002
|
-
const child =
|
|
5322
|
+
const child = import_node_path31.default.join(start, entry.name);
|
|
5003
5323
|
if (await isPythonVenvDir(child)) continue;
|
|
5004
5324
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
5005
5325
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
5006
|
-
out.push(
|
|
5326
|
+
out.push(import_node_path31.default.join(start, entry.name));
|
|
5007
5327
|
}
|
|
5008
5328
|
}
|
|
5009
5329
|
return out;
|
|
@@ -5012,7 +5332,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
5012
5332
|
let nodesAdded = 0;
|
|
5013
5333
|
const files = await walkTfFiles(scanPath);
|
|
5014
5334
|
for (const file of files) {
|
|
5015
|
-
const content = await
|
|
5335
|
+
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
5016
5336
|
RESOURCE_RE.lastIndex = 0;
|
|
5017
5337
|
let m;
|
|
5018
5338
|
while ((m = RESOURCE_RE.exec(content)) !== null) {
|
|
@@ -5030,8 +5350,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
5030
5350
|
|
|
5031
5351
|
// src/extract/infra/k8s.ts
|
|
5032
5352
|
init_cjs_shims();
|
|
5033
|
-
var
|
|
5034
|
-
var
|
|
5353
|
+
var import_node_fs19 = require("fs");
|
|
5354
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
5035
5355
|
var import_yaml3 = require("yaml");
|
|
5036
5356
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
5037
5357
|
Service: "k8s-service",
|
|
@@ -5045,15 +5365,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
5045
5365
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
5046
5366
|
if (depth > max) return [];
|
|
5047
5367
|
const out = [];
|
|
5048
|
-
const entries = await
|
|
5368
|
+
const entries = await import_node_fs19.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
5049
5369
|
for (const entry of entries) {
|
|
5050
5370
|
if (entry.isDirectory()) {
|
|
5051
5371
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
5052
|
-
const child =
|
|
5372
|
+
const child = import_node_path32.default.join(start, entry.name);
|
|
5053
5373
|
if (await isPythonVenvDir(child)) continue;
|
|
5054
5374
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
5055
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
5056
|
-
out.push(
|
|
5375
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path32.default.extname(entry.name))) {
|
|
5376
|
+
out.push(import_node_path32.default.join(start, entry.name));
|
|
5057
5377
|
}
|
|
5058
5378
|
}
|
|
5059
5379
|
return out;
|
|
@@ -5062,7 +5382,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
5062
5382
|
let nodesAdded = 0;
|
|
5063
5383
|
const files = await walkYamlFiles2(scanPath);
|
|
5064
5384
|
for (const file of files) {
|
|
5065
|
-
const content = await
|
|
5385
|
+
const content = await import_node_fs19.promises.readFile(file, "utf8");
|
|
5066
5386
|
let docs;
|
|
5067
5387
|
try {
|
|
5068
5388
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -5097,17 +5417,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
5097
5417
|
}
|
|
5098
5418
|
|
|
5099
5419
|
// src/extract/index.ts
|
|
5100
|
-
var
|
|
5420
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5101
5421
|
|
|
5102
5422
|
// src/extract/retire.ts
|
|
5103
5423
|
init_cjs_shims();
|
|
5104
|
-
var
|
|
5105
|
-
var
|
|
5106
|
-
var
|
|
5424
|
+
var import_node_fs20 = require("fs");
|
|
5425
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
5426
|
+
var import_types21 = require("@neat.is/types");
|
|
5107
5427
|
function dropOrphanedFileNodes(graph) {
|
|
5108
5428
|
const orphans = [];
|
|
5109
5429
|
graph.forEachNode((id, attrs) => {
|
|
5110
|
-
if (attrs.type !==
|
|
5430
|
+
if (attrs.type !== import_types21.NodeType.FileNode) return;
|
|
5111
5431
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
5112
5432
|
orphans.push(id);
|
|
5113
5433
|
}
|
|
@@ -5120,14 +5440,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
5120
5440
|
const bases = [scanPath, ...serviceDirs];
|
|
5121
5441
|
graph.forEachEdge((id, attrs) => {
|
|
5122
5442
|
const edge = attrs;
|
|
5123
|
-
if (edge.provenance !==
|
|
5443
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
5124
5444
|
const evidenceFile = edge.evidence?.file;
|
|
5125
5445
|
if (!evidenceFile) return;
|
|
5126
|
-
if (
|
|
5127
|
-
if (!(0,
|
|
5446
|
+
if (import_node_path33.default.isAbsolute(evidenceFile)) {
|
|
5447
|
+
if (!(0, import_node_fs20.existsSync)(evidenceFile)) toDrop.push(id);
|
|
5128
5448
|
return;
|
|
5129
5449
|
}
|
|
5130
|
-
const found = bases.some((base) => (0,
|
|
5450
|
+
const found = bases.some((base) => (0, import_node_fs20.existsSync)(import_node_path33.default.join(base, evidenceFile)));
|
|
5131
5451
|
if (!found) toDrop.push(id);
|
|
5132
5452
|
});
|
|
5133
5453
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -5142,6 +5462,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5142
5462
|
const services = await discoverServices(scanPath);
|
|
5143
5463
|
const phase1Nodes = addServiceNodes(graph, services);
|
|
5144
5464
|
await addServiceAliases(graph, scanPath, services);
|
|
5465
|
+
const fileEnum = await addFiles(graph, services);
|
|
5466
|
+
const importGraph = await addImports(graph, services);
|
|
5145
5467
|
const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
|
|
5146
5468
|
const phase3 = await addConfigNodes(graph, services, scanPath);
|
|
5147
5469
|
const phase4 = await addCallEdges(graph, services);
|
|
@@ -5165,7 +5487,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5165
5487
|
}
|
|
5166
5488
|
const droppedEntries = drainDroppedExtracted();
|
|
5167
5489
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
5168
|
-
const rejectedPath =
|
|
5490
|
+
const rejectedPath = import_node_path34.default.join(import_node_path34.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
5169
5491
|
try {
|
|
5170
5492
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
5171
5493
|
} catch (err) {
|
|
@@ -5175,8 +5497,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5175
5497
|
}
|
|
5176
5498
|
}
|
|
5177
5499
|
const result = {
|
|
5178
|
-
nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
5179
|
-
edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
5500
|
+
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
5501
|
+
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
5180
5502
|
frontiersPromoted,
|
|
5181
5503
|
extractionErrors: errorEntries.length,
|
|
5182
5504
|
errorEntries,
|
|
@@ -5199,7 +5521,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5199
5521
|
|
|
5200
5522
|
// src/diff.ts
|
|
5201
5523
|
init_cjs_shims();
|
|
5202
|
-
var
|
|
5524
|
+
var import_node_fs21 = require("fs");
|
|
5203
5525
|
async function loadSnapshotForDiff(target) {
|
|
5204
5526
|
if (/^https?:\/\//i.test(target)) {
|
|
5205
5527
|
const res = await fetch(target);
|
|
@@ -5208,7 +5530,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
5208
5530
|
}
|
|
5209
5531
|
return await res.json();
|
|
5210
5532
|
}
|
|
5211
|
-
const raw = await
|
|
5533
|
+
const raw = await import_node_fs21.promises.readFile(target, "utf8");
|
|
5212
5534
|
return JSON.parse(raw);
|
|
5213
5535
|
}
|
|
5214
5536
|
function indexEntries(entries) {
|
|
@@ -5276,9 +5598,9 @@ function canonicalJson(value) {
|
|
|
5276
5598
|
|
|
5277
5599
|
// src/persist.ts
|
|
5278
5600
|
init_cjs_shims();
|
|
5279
|
-
var
|
|
5280
|
-
var
|
|
5281
|
-
var
|
|
5601
|
+
var import_node_fs22 = require("fs");
|
|
5602
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
5603
|
+
var import_types22 = require("@neat.is/types");
|
|
5282
5604
|
var SCHEMA_VERSION = 4;
|
|
5283
5605
|
function migrateV1ToV2(payload) {
|
|
5284
5606
|
const nodes = payload.graph.nodes;
|
|
@@ -5300,12 +5622,12 @@ function migrateV2ToV3(payload) {
|
|
|
5300
5622
|
for (const edge of edges) {
|
|
5301
5623
|
const attrs = edge.attributes;
|
|
5302
5624
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
5303
|
-
attrs.provenance =
|
|
5625
|
+
attrs.provenance = import_types22.Provenance.OBSERVED;
|
|
5304
5626
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
5305
5627
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
5306
5628
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
5307
5629
|
if (type && source && target) {
|
|
5308
|
-
const newId = (0,
|
|
5630
|
+
const newId = (0, import_types22.observedEdgeId)(source, target, type);
|
|
5309
5631
|
attrs.id = newId;
|
|
5310
5632
|
if (edge.key) edge.key = newId;
|
|
5311
5633
|
}
|
|
@@ -5314,7 +5636,7 @@ function migrateV2ToV3(payload) {
|
|
|
5314
5636
|
return { ...payload, schemaVersion: 3 };
|
|
5315
5637
|
}
|
|
5316
5638
|
async function ensureDir(filePath) {
|
|
5317
|
-
await
|
|
5639
|
+
await import_node_fs22.promises.mkdir(import_node_path35.default.dirname(filePath), { recursive: true });
|
|
5318
5640
|
}
|
|
5319
5641
|
async function saveGraphToDisk(graph, outPath) {
|
|
5320
5642
|
await ensureDir(outPath);
|
|
@@ -5324,13 +5646,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
5324
5646
|
graph: graph.export()
|
|
5325
5647
|
};
|
|
5326
5648
|
const tmp = `${outPath}.tmp`;
|
|
5327
|
-
await
|
|
5328
|
-
await
|
|
5649
|
+
await import_node_fs22.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
5650
|
+
await import_node_fs22.promises.rename(tmp, outPath);
|
|
5329
5651
|
}
|
|
5330
5652
|
async function loadGraphFromDisk(graph, outPath) {
|
|
5331
5653
|
let raw;
|
|
5332
5654
|
try {
|
|
5333
|
-
raw = await
|
|
5655
|
+
raw = await import_node_fs22.promises.readFile(outPath, "utf8");
|
|
5334
5656
|
} catch (err) {
|
|
5335
5657
|
if (err.code === "ENOENT") return;
|
|
5336
5658
|
throw err;
|
|
@@ -5389,23 +5711,23 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5389
5711
|
|
|
5390
5712
|
// src/projects.ts
|
|
5391
5713
|
init_cjs_shims();
|
|
5392
|
-
var
|
|
5714
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5393
5715
|
function pathsForProject(project, baseDir) {
|
|
5394
5716
|
if (project === DEFAULT_PROJECT) {
|
|
5395
5717
|
return {
|
|
5396
|
-
snapshotPath:
|
|
5397
|
-
errorsPath:
|
|
5398
|
-
staleEventsPath:
|
|
5399
|
-
embeddingsCachePath:
|
|
5400
|
-
policyViolationsPath:
|
|
5718
|
+
snapshotPath: import_node_path36.default.join(baseDir, "graph.json"),
|
|
5719
|
+
errorsPath: import_node_path36.default.join(baseDir, "errors.ndjson"),
|
|
5720
|
+
staleEventsPath: import_node_path36.default.join(baseDir, "stale-events.ndjson"),
|
|
5721
|
+
embeddingsCachePath: import_node_path36.default.join(baseDir, "embeddings.json"),
|
|
5722
|
+
policyViolationsPath: import_node_path36.default.join(baseDir, "policy-violations.ndjson")
|
|
5401
5723
|
};
|
|
5402
5724
|
}
|
|
5403
5725
|
return {
|
|
5404
|
-
snapshotPath:
|
|
5405
|
-
errorsPath:
|
|
5406
|
-
staleEventsPath:
|
|
5407
|
-
embeddingsCachePath:
|
|
5408
|
-
policyViolationsPath:
|
|
5726
|
+
snapshotPath: import_node_path36.default.join(baseDir, `${project}.json`),
|
|
5727
|
+
errorsPath: import_node_path36.default.join(baseDir, `errors.${project}.ndjson`),
|
|
5728
|
+
staleEventsPath: import_node_path36.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
5729
|
+
embeddingsCachePath: import_node_path36.default.join(baseDir, `embeddings.${project}.json`),
|
|
5730
|
+
policyViolationsPath: import_node_path36.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
5409
5731
|
};
|
|
5410
5732
|
}
|
|
5411
5733
|
var Projects = class {
|
|
@@ -5445,23 +5767,23 @@ function parseExtraProjects(raw) {
|
|
|
5445
5767
|
|
|
5446
5768
|
// src/registry.ts
|
|
5447
5769
|
init_cjs_shims();
|
|
5448
|
-
var
|
|
5770
|
+
var import_node_fs23 = require("fs");
|
|
5449
5771
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
5450
|
-
var
|
|
5451
|
-
var
|
|
5772
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
5773
|
+
var import_types23 = require("@neat.is/types");
|
|
5452
5774
|
function neatHome() {
|
|
5453
5775
|
const override = process.env.NEAT_HOME;
|
|
5454
|
-
if (override && override.length > 0) return
|
|
5455
|
-
return
|
|
5776
|
+
if (override && override.length > 0) return import_node_path37.default.resolve(override);
|
|
5777
|
+
return import_node_path37.default.join(import_node_os3.default.homedir(), ".neat");
|
|
5456
5778
|
}
|
|
5457
5779
|
function registryPath() {
|
|
5458
|
-
return
|
|
5780
|
+
return import_node_path37.default.join(neatHome(), "projects.json");
|
|
5459
5781
|
}
|
|
5460
5782
|
async function readRegistry() {
|
|
5461
5783
|
const file = registryPath();
|
|
5462
5784
|
let raw;
|
|
5463
5785
|
try {
|
|
5464
|
-
raw = await
|
|
5786
|
+
raw = await import_node_fs23.promises.readFile(file, "utf8");
|
|
5465
5787
|
} catch (err) {
|
|
5466
5788
|
if (err.code === "ENOENT") {
|
|
5467
5789
|
return { version: 1, projects: [] };
|
|
@@ -5469,7 +5791,7 @@ async function readRegistry() {
|
|
|
5469
5791
|
throw err;
|
|
5470
5792
|
}
|
|
5471
5793
|
const parsed = JSON.parse(raw);
|
|
5472
|
-
return
|
|
5794
|
+
return import_types23.RegistryFileSchema.parse(parsed);
|
|
5473
5795
|
}
|
|
5474
5796
|
async function getProject(name) {
|
|
5475
5797
|
const reg = await readRegistry();
|
|
@@ -5671,11 +5993,11 @@ function registerRoutes(scope, ctx) {
|
|
|
5671
5993
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
5672
5994
|
const parsed = [];
|
|
5673
5995
|
for (const c of candidates) {
|
|
5674
|
-
const r =
|
|
5996
|
+
const r = import_types24.DivergenceTypeSchema.safeParse(c);
|
|
5675
5997
|
if (!r.success) {
|
|
5676
5998
|
return reply.code(400).send({
|
|
5677
5999
|
error: `unknown divergence type "${c}"`,
|
|
5678
|
-
allowed:
|
|
6000
|
+
allowed: import_types24.DivergenceTypeSchema.options
|
|
5679
6001
|
});
|
|
5680
6002
|
}
|
|
5681
6003
|
parsed.push(r.data);
|
|
@@ -5888,7 +6210,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5888
6210
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
5889
6211
|
let violations = await log.readAll();
|
|
5890
6212
|
if (req.query.severity) {
|
|
5891
|
-
const sev =
|
|
6213
|
+
const sev = import_types24.PolicySeveritySchema.safeParse(req.query.severity);
|
|
5892
6214
|
if (!sev.success) {
|
|
5893
6215
|
return reply.code(400).send({
|
|
5894
6216
|
error: "invalid severity",
|
|
@@ -5905,7 +6227,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5905
6227
|
scope.post("/policies/check", async (req, reply) => {
|
|
5906
6228
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
5907
6229
|
if (!proj) return;
|
|
5908
|
-
const parsed =
|
|
6230
|
+
const parsed = import_types24.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
5909
6231
|
if (!parsed.success) {
|
|
5910
6232
|
return reply.code(400).send({
|
|
5911
6233
|
error: "invalid /policies/check body",
|
|
@@ -6147,8 +6469,8 @@ init_otel_grpc();
|
|
|
6147
6469
|
|
|
6148
6470
|
// src/search.ts
|
|
6149
6471
|
init_cjs_shims();
|
|
6150
|
-
var
|
|
6151
|
-
var
|
|
6472
|
+
var import_node_fs24 = require("fs");
|
|
6473
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
6152
6474
|
var import_node_crypto3 = require("crypto");
|
|
6153
6475
|
var DEFAULT_LIMIT = 10;
|
|
6154
6476
|
var NOMIC_DIM = 768;
|
|
@@ -6278,7 +6600,7 @@ async function pickEmbedder() {
|
|
|
6278
6600
|
}
|
|
6279
6601
|
async function readCache(cachePath) {
|
|
6280
6602
|
try {
|
|
6281
|
-
const raw = await
|
|
6603
|
+
const raw = await import_node_fs24.promises.readFile(cachePath, "utf8");
|
|
6282
6604
|
const parsed = JSON.parse(raw);
|
|
6283
6605
|
if (parsed.version !== 1) return null;
|
|
6284
6606
|
return parsed;
|
|
@@ -6287,8 +6609,8 @@ async function readCache(cachePath) {
|
|
|
6287
6609
|
}
|
|
6288
6610
|
}
|
|
6289
6611
|
async function writeCache(cachePath, cache) {
|
|
6290
|
-
await
|
|
6291
|
-
await
|
|
6612
|
+
await import_node_fs24.promises.mkdir(import_node_path40.default.dirname(cachePath), { recursive: true });
|
|
6613
|
+
await import_node_fs24.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
6292
6614
|
}
|
|
6293
6615
|
var VectorIndex = class {
|
|
6294
6616
|
constructor(embedder, cachePath) {
|
|
@@ -6468,14 +6790,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
6468
6790
|
async function main() {
|
|
6469
6791
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
6470
6792
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
6471
|
-
const baseDir = baseDirEnv ?
|
|
6472
|
-
const defaultScanPath =
|
|
6793
|
+
const baseDir = baseDirEnv ? import_node_path41.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path41.default.resolve(import_node_path41.default.dirname(legacyOutPath)) : import_node_path41.default.resolve("./neat-out");
|
|
6794
|
+
const defaultScanPath = import_node_path41.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
6473
6795
|
const registry = new Projects();
|
|
6474
6796
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
6475
6797
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
6476
6798
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
6477
6799
|
const projectScan = process.env[envKey];
|
|
6478
|
-
await bootProject(registry, name, projectScan ?
|
|
6800
|
+
await bootProject(registry, name, projectScan ? import_node_path41.default.resolve(projectScan) : void 0, baseDir);
|
|
6479
6801
|
}
|
|
6480
6802
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
6481
6803
|
const port = Number(process.env.PORT ?? 8080);
|