@neat.is/core 0.4.6 → 0.4.7
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-RBWL4HRB.js → chunk-7FTK47JQ.js} +277 -103
- package/dist/chunk-7FTK47JQ.js.map +1 -0
- package/dist/{chunk-NON5AXOR.js → chunk-LS6NS72S.js} +2 -2
- package/dist/cli.cjs +402 -145
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +107 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +280 -115
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +280 -115
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +263 -98
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-RBWL4HRB.js.map +0 -1
- /package/dist/{chunk-NON5AXOR.js.map → chunk-LS6NS72S.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1673,6 +1673,86 @@ function hostFromUrl(u) {
|
|
|
1673
1673
|
function pickAddress(span) {
|
|
1674
1674
|
return pickAttr(span, "server.address", "net.peer.name", "net.host.name") ?? hostFromUrl(pickAttr(span, "url.full", "http.url"));
|
|
1675
1675
|
}
|
|
1676
|
+
var CODE_FILEPATH_ATTR = "code.filepath";
|
|
1677
|
+
var CODE_LINENO_ATTR = "code.lineno";
|
|
1678
|
+
var CODE_FUNCTION_ATTR = "code.function";
|
|
1679
|
+
function toPosix(p) {
|
|
1680
|
+
return p.split("\\").join("/");
|
|
1681
|
+
}
|
|
1682
|
+
function languageForExt(relPath) {
|
|
1683
|
+
const dot = relPath.lastIndexOf(".");
|
|
1684
|
+
if (dot === -1) return void 0;
|
|
1685
|
+
switch (relPath.slice(dot).toLowerCase()) {
|
|
1686
|
+
case ".py":
|
|
1687
|
+
return "python";
|
|
1688
|
+
case ".ts":
|
|
1689
|
+
case ".tsx":
|
|
1690
|
+
return "typescript";
|
|
1691
|
+
case ".js":
|
|
1692
|
+
case ".jsx":
|
|
1693
|
+
case ".mjs":
|
|
1694
|
+
case ".cjs":
|
|
1695
|
+
return "javascript";
|
|
1696
|
+
default:
|
|
1697
|
+
return void 0;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
function relPathForRuntimeFile(filepath, serviceNode) {
|
|
1701
|
+
let p = toPosix(filepath).replace(/^file:\/\//, "");
|
|
1702
|
+
const root = serviceNode?.repoPath;
|
|
1703
|
+
if (root && root !== "." && root.length > 0) {
|
|
1704
|
+
const rootPosix = toPosix(root);
|
|
1705
|
+
const anchor = `/${rootPosix}/`;
|
|
1706
|
+
const idx = p.lastIndexOf(anchor);
|
|
1707
|
+
if (idx !== -1) return p.slice(idx + anchor.length);
|
|
1708
|
+
const base = rootPosix.split("/").filter(Boolean).pop();
|
|
1709
|
+
if (base) {
|
|
1710
|
+
const baseAnchor = `/${base}/`;
|
|
1711
|
+
const bidx = p.lastIndexOf(baseAnchor);
|
|
1712
|
+
if (bidx !== -1) return p.slice(bidx + baseAnchor.length);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
p = p.replace(/^[A-Za-z]:/, "").replace(/^\/+/, "");
|
|
1716
|
+
return p.length > 0 ? p : null;
|
|
1717
|
+
}
|
|
1718
|
+
function callSiteFromSpan(span, serviceNode) {
|
|
1719
|
+
const filepath = span.attributes[CODE_FILEPATH_ATTR];
|
|
1720
|
+
if (typeof filepath !== "string" || filepath.length === 0) return null;
|
|
1721
|
+
const relPath = relPathForRuntimeFile(filepath, serviceNode);
|
|
1722
|
+
if (!relPath) return null;
|
|
1723
|
+
const linenoRaw = span.attributes[CODE_LINENO_ATTR];
|
|
1724
|
+
const line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
|
|
1725
|
+
const fnRaw = span.attributes[CODE_FUNCTION_ATTR];
|
|
1726
|
+
const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
|
|
1727
|
+
return { relPath, ...line !== void 0 ? { line } : {}, ...fn ? { fn } : {} };
|
|
1728
|
+
}
|
|
1729
|
+
function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
|
|
1730
|
+
const fileNodeId = (0, import_types3.fileId)(serviceName, callSite.relPath);
|
|
1731
|
+
if (!graph.hasNode(fileNodeId)) {
|
|
1732
|
+
const language = languageForExt(callSite.relPath);
|
|
1733
|
+
const node = {
|
|
1734
|
+
id: fileNodeId,
|
|
1735
|
+
type: import_types3.NodeType.FileNode,
|
|
1736
|
+
service: serviceName,
|
|
1737
|
+
path: callSite.relPath,
|
|
1738
|
+
...language ? { language } : {},
|
|
1739
|
+
discoveredVia: "otel"
|
|
1740
|
+
};
|
|
1741
|
+
graph.addNode(fileNodeId, node);
|
|
1742
|
+
}
|
|
1743
|
+
const containsId = makeObservedEdgeId(import_types3.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
|
|
1744
|
+
if (!graph.hasEdge(containsId)) {
|
|
1745
|
+
const edge = {
|
|
1746
|
+
id: containsId,
|
|
1747
|
+
source: serviceNodeId,
|
|
1748
|
+
target: fileNodeId,
|
|
1749
|
+
type: import_types3.EdgeType.CONTAINS,
|
|
1750
|
+
provenance: import_types3.Provenance.OBSERVED
|
|
1751
|
+
};
|
|
1752
|
+
graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
|
|
1753
|
+
}
|
|
1754
|
+
return fileNodeId;
|
|
1755
|
+
}
|
|
1676
1756
|
function makeObservedEdgeId(type, source, target) {
|
|
1677
1757
|
return (0, import_types3.observedEdgeId)(source, target, type);
|
|
1678
1758
|
}
|
|
@@ -1915,6 +1995,9 @@ async function handleSpan(ctx, span) {
|
|
|
1915
1995
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
1916
1996
|
const isError = span.statusCode === 2;
|
|
1917
1997
|
cacheSpanService(span, nowMs);
|
|
1998
|
+
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
1999
|
+
const callSite = callSiteFromSpan(span, sourceServiceNode);
|
|
2000
|
+
const observedSource = () => callSite ? ensureObservedFileNode(ctx.graph, span.service, sourceId, callSite) : sourceId;
|
|
1918
2001
|
let affectedNode = sourceId;
|
|
1919
2002
|
if (span.dbSystem) {
|
|
1920
2003
|
const host = pickAddress(span);
|
|
@@ -1924,7 +2007,7 @@ async function handleSpan(ctx, span) {
|
|
|
1924
2007
|
const result = upsertObservedEdge(
|
|
1925
2008
|
ctx.graph,
|
|
1926
2009
|
import_types3.EdgeType.CONNECTS_TO,
|
|
1927
|
-
|
|
2010
|
+
observedSource(),
|
|
1928
2011
|
targetId,
|
|
1929
2012
|
ts,
|
|
1930
2013
|
isError
|
|
@@ -1940,7 +2023,7 @@ async function handleSpan(ctx, span) {
|
|
|
1940
2023
|
upsertObservedEdge(
|
|
1941
2024
|
ctx.graph,
|
|
1942
2025
|
import_types3.EdgeType.CALLS,
|
|
1943
|
-
|
|
2026
|
+
observedSource(),
|
|
1944
2027
|
targetId,
|
|
1945
2028
|
ts,
|
|
1946
2029
|
isError
|
|
@@ -1952,7 +2035,7 @@ async function handleSpan(ctx, span) {
|
|
|
1952
2035
|
upsertObservedEdge(
|
|
1953
2036
|
ctx.graph,
|
|
1954
2037
|
import_types3.EdgeType.CALLS,
|
|
1955
|
-
|
|
2038
|
+
observedSource(),
|
|
1956
2039
|
frontierNodeId,
|
|
1957
2040
|
ts,
|
|
1958
2041
|
isError
|
|
@@ -3583,7 +3666,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
3583
3666
|
|
|
3584
3667
|
// src/extract/calls/index.ts
|
|
3585
3668
|
init_cjs_shims();
|
|
3586
|
-
var
|
|
3669
|
+
var import_types15 = require("@neat.is/types");
|
|
3587
3670
|
|
|
3588
3671
|
// src/extract/calls/http.ts
|
|
3589
3672
|
init_cjs_shims();
|
|
@@ -3591,12 +3674,13 @@ var import_node_path20 = __toESM(require("path"), 1);
|
|
|
3591
3674
|
var import_tree_sitter = __toESM(require("tree-sitter"), 1);
|
|
3592
3675
|
var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
|
|
3593
3676
|
var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
|
|
3594
|
-
var
|
|
3677
|
+
var import_types10 = require("@neat.is/types");
|
|
3595
3678
|
|
|
3596
3679
|
// src/extract/calls/shared.ts
|
|
3597
3680
|
init_cjs_shims();
|
|
3598
3681
|
var import_node_fs13 = require("fs");
|
|
3599
3682
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
3683
|
+
var import_types9 = require("@neat.is/types");
|
|
3600
3684
|
async function walkSourceFiles(dir) {
|
|
3601
3685
|
const out = [];
|
|
3602
3686
|
async function walk(current) {
|
|
@@ -3636,6 +3720,58 @@ function snippet(text, line) {
|
|
|
3636
3720
|
const lines = text.split("\n");
|
|
3637
3721
|
return (lines[line - 1] ?? "").trim();
|
|
3638
3722
|
}
|
|
3723
|
+
function toPosix2(p) {
|
|
3724
|
+
return p.split("\\").join("/");
|
|
3725
|
+
}
|
|
3726
|
+
function languageForPath(relPath) {
|
|
3727
|
+
switch (import_node_path19.default.extname(relPath).toLowerCase()) {
|
|
3728
|
+
case ".py":
|
|
3729
|
+
return "python";
|
|
3730
|
+
case ".ts":
|
|
3731
|
+
case ".tsx":
|
|
3732
|
+
return "typescript";
|
|
3733
|
+
case ".js":
|
|
3734
|
+
case ".jsx":
|
|
3735
|
+
case ".mjs":
|
|
3736
|
+
case ".cjs":
|
|
3737
|
+
return "javascript";
|
|
3738
|
+
default:
|
|
3739
|
+
return void 0;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
|
|
3743
|
+
let nodesAdded = 0;
|
|
3744
|
+
let edgesAdded = 0;
|
|
3745
|
+
const fileNodeId = (0, import_types9.fileId)(serviceName, relPath);
|
|
3746
|
+
if (!graph.hasNode(fileNodeId)) {
|
|
3747
|
+
const language = languageForPath(relPath);
|
|
3748
|
+
const node = {
|
|
3749
|
+
id: fileNodeId,
|
|
3750
|
+
type: import_types9.NodeType.FileNode,
|
|
3751
|
+
service: serviceName,
|
|
3752
|
+
path: relPath,
|
|
3753
|
+
...language ? { language } : {},
|
|
3754
|
+
discoveredVia: "static"
|
|
3755
|
+
};
|
|
3756
|
+
graph.addNode(fileNodeId, node);
|
|
3757
|
+
nodesAdded++;
|
|
3758
|
+
}
|
|
3759
|
+
const containsId = (0, import_types9.extractedEdgeId)(serviceNodeId, fileNodeId, import_types9.EdgeType.CONTAINS);
|
|
3760
|
+
if (!graph.hasEdge(containsId)) {
|
|
3761
|
+
const edge = {
|
|
3762
|
+
id: containsId,
|
|
3763
|
+
source: serviceNodeId,
|
|
3764
|
+
target: fileNodeId,
|
|
3765
|
+
type: import_types9.EdgeType.CONTAINS,
|
|
3766
|
+
provenance: import_types9.Provenance.EXTRACTED,
|
|
3767
|
+
confidence: (0, import_types9.confidenceForExtracted)("structural"),
|
|
3768
|
+
evidence: { file: relPath }
|
|
3769
|
+
};
|
|
3770
|
+
graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
|
|
3771
|
+
edgesAdded++;
|
|
3772
|
+
}
|
|
3773
|
+
return { fileNodeId, nodesAdded, edgesAdded };
|
|
3774
|
+
}
|
|
3639
3775
|
|
|
3640
3776
|
// src/extract/calls/http.ts
|
|
3641
3777
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
@@ -3669,16 +3805,16 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
3669
3805
|
const tree = parser.parse(source);
|
|
3670
3806
|
const literals = [];
|
|
3671
3807
|
collectStringLiterals(tree.rootNode, literals);
|
|
3672
|
-
const
|
|
3808
|
+
const out = [];
|
|
3673
3809
|
for (const lit of literals) {
|
|
3674
3810
|
if (isInsideJsxExternalLink(lit.node)) continue;
|
|
3675
3811
|
for (const host of knownHosts) {
|
|
3676
3812
|
if (urlMatchesHost(lit.text, host)) {
|
|
3677
|
-
|
|
3813
|
+
out.push({ host, line: lit.node.startPosition.row + 1 });
|
|
3678
3814
|
}
|
|
3679
3815
|
}
|
|
3680
3816
|
}
|
|
3681
|
-
return
|
|
3817
|
+
return out;
|
|
3682
3818
|
}
|
|
3683
3819
|
function makeJsParser() {
|
|
3684
3820
|
const p = new import_tree_sitter.default();
|
|
@@ -3701,71 +3837,78 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3701
3837
|
hostToNodeId.set(import_node_path20.default.basename(service.dir), service.node.id);
|
|
3702
3838
|
hostToNodeId.set(service.pkg.name, service.node.id);
|
|
3703
3839
|
}
|
|
3840
|
+
let nodesAdded = 0;
|
|
3704
3841
|
let edgesAdded = 0;
|
|
3705
3842
|
for (const service of services) {
|
|
3706
3843
|
const files = await loadSourceFiles(service.dir);
|
|
3707
|
-
const
|
|
3844
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3708
3845
|
for (const file of files) {
|
|
3709
3846
|
if (isTestPath(file.path)) continue;
|
|
3710
3847
|
const parser = import_node_path20.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
3711
|
-
let
|
|
3848
|
+
let sites;
|
|
3712
3849
|
try {
|
|
3713
|
-
|
|
3850
|
+
sites = callsFromSource(file.content, parser, knownHosts);
|
|
3714
3851
|
} catch (err) {
|
|
3715
3852
|
recordExtractionError("http call extraction", file.path, err);
|
|
3716
3853
|
continue;
|
|
3717
3854
|
}
|
|
3718
|
-
|
|
3719
|
-
|
|
3855
|
+
if (sites.length === 0) continue;
|
|
3856
|
+
const relFile = toPosix2(import_node_path20.default.relative(service.dir, file.path));
|
|
3857
|
+
for (const site of sites) {
|
|
3858
|
+
const targetId = hostToNodeId.get(site.host);
|
|
3720
3859
|
if (!targetId || targetId === service.node.id) continue;
|
|
3721
|
-
|
|
3722
|
-
|
|
3860
|
+
const dedupKey = `${relFile}|${targetId}`;
|
|
3861
|
+
if (seen.has(dedupKey)) continue;
|
|
3862
|
+
seen.add(dedupKey);
|
|
3863
|
+
const confidence = (0, import_types10.confidenceForExtracted)("hostname-shape-match");
|
|
3864
|
+
const ev = {
|
|
3865
|
+
file: relFile,
|
|
3866
|
+
line: site.line,
|
|
3867
|
+
snippet: snippet(file.content, site.line)
|
|
3868
|
+
};
|
|
3869
|
+
if (!(0, import_types10.passesExtractedFloor)(confidence)) {
|
|
3870
|
+
noteExtractedDropped({
|
|
3871
|
+
source: service.node.id,
|
|
3872
|
+
target: targetId,
|
|
3873
|
+
type: import_types10.EdgeType.CALLS,
|
|
3874
|
+
confidence,
|
|
3875
|
+
confidenceKind: "hostname-shape-match",
|
|
3876
|
+
evidence: ev
|
|
3877
|
+
});
|
|
3878
|
+
continue;
|
|
3879
|
+
}
|
|
3880
|
+
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
3881
|
+
graph,
|
|
3882
|
+
service.pkg.name,
|
|
3883
|
+
service.node.id,
|
|
3884
|
+
relFile
|
|
3885
|
+
);
|
|
3886
|
+
nodesAdded += n;
|
|
3887
|
+
edgesAdded += e;
|
|
3888
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types10.EdgeType.CALLS);
|
|
3889
|
+
if (!graph.hasEdge(edgeId)) {
|
|
3890
|
+
const edge = {
|
|
3891
|
+
id: edgeId,
|
|
3892
|
+
source: fileNodeId,
|
|
3893
|
+
target: targetId,
|
|
3894
|
+
type: import_types10.EdgeType.CALLS,
|
|
3895
|
+
provenance: import_types10.Provenance.EXTRACTED,
|
|
3896
|
+
confidence,
|
|
3897
|
+
evidence: ev
|
|
3898
|
+
};
|
|
3899
|
+
graph.addEdgeWithKey(edgeId, fileNodeId, targetId, edge);
|
|
3900
|
+
edgesAdded++;
|
|
3723
3901
|
}
|
|
3724
|
-
}
|
|
3725
|
-
}
|
|
3726
|
-
for (const [targetId, evidenceFile] of seenTargets) {
|
|
3727
|
-
const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? "";
|
|
3728
|
-
const line = lineOf(fileContent, `//${evidenceFile.host}`);
|
|
3729
|
-
const confidence = (0, import_types9.confidenceForExtracted)("hostname-shape-match");
|
|
3730
|
-
const ev = {
|
|
3731
|
-
file: import_node_path20.default.relative(service.dir, evidenceFile.file),
|
|
3732
|
-
line,
|
|
3733
|
-
snippet: snippet(fileContent, line)
|
|
3734
|
-
};
|
|
3735
|
-
const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, targetId, import_types9.EdgeType.CALLS);
|
|
3736
|
-
if (!(0, import_types9.passesExtractedFloor)(confidence)) {
|
|
3737
|
-
noteExtractedDropped({
|
|
3738
|
-
source: service.node.id,
|
|
3739
|
-
target: targetId,
|
|
3740
|
-
type: import_types9.EdgeType.CALLS,
|
|
3741
|
-
confidence,
|
|
3742
|
-
confidenceKind: "hostname-shape-match",
|
|
3743
|
-
evidence: ev
|
|
3744
|
-
});
|
|
3745
|
-
continue;
|
|
3746
|
-
}
|
|
3747
|
-
const edge = {
|
|
3748
|
-
id: edgeId,
|
|
3749
|
-
source: service.node.id,
|
|
3750
|
-
target: targetId,
|
|
3751
|
-
type: import_types9.EdgeType.CALLS,
|
|
3752
|
-
provenance: import_types9.Provenance.EXTRACTED,
|
|
3753
|
-
confidence,
|
|
3754
|
-
evidence: ev
|
|
3755
|
-
};
|
|
3756
|
-
if (!graph.hasEdge(edge.id)) {
|
|
3757
|
-
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
3758
|
-
edgesAdded++;
|
|
3759
3902
|
}
|
|
3760
3903
|
}
|
|
3761
3904
|
}
|
|
3762
|
-
return edgesAdded;
|
|
3905
|
+
return { nodesAdded, edgesAdded };
|
|
3763
3906
|
}
|
|
3764
3907
|
|
|
3765
3908
|
// src/extract/calls/kafka.ts
|
|
3766
3909
|
init_cjs_shims();
|
|
3767
3910
|
var import_node_path21 = __toESM(require("path"), 1);
|
|
3768
|
-
var
|
|
3911
|
+
var import_types11 = require("@neat.is/types");
|
|
3769
3912
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
3770
3913
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
3771
3914
|
function findAll(re, text) {
|
|
@@ -3786,7 +3929,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3786
3929
|
seen.add(key);
|
|
3787
3930
|
const line = lineOf(file.content, topic);
|
|
3788
3931
|
out.push({
|
|
3789
|
-
infraId: (0,
|
|
3932
|
+
infraId: (0, import_types11.infraId)("kafka-topic", topic),
|
|
3790
3933
|
name: topic,
|
|
3791
3934
|
kind: "kafka-topic",
|
|
3792
3935
|
edgeType,
|
|
@@ -3809,7 +3952,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3809
3952
|
// src/extract/calls/redis.ts
|
|
3810
3953
|
init_cjs_shims();
|
|
3811
3954
|
var import_node_path22 = __toESM(require("path"), 1);
|
|
3812
|
-
var
|
|
3955
|
+
var import_types12 = require("@neat.is/types");
|
|
3813
3956
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
3814
3957
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
3815
3958
|
const out = [];
|
|
@@ -3822,7 +3965,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3822
3965
|
seen.add(host);
|
|
3823
3966
|
const line = lineOf(file.content, host);
|
|
3824
3967
|
out.push({
|
|
3825
|
-
infraId: (0,
|
|
3968
|
+
infraId: (0, import_types12.infraId)("redis", host),
|
|
3826
3969
|
name: host,
|
|
3827
3970
|
kind: "redis",
|
|
3828
3971
|
edgeType: "CALLS",
|
|
@@ -3843,7 +3986,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3843
3986
|
// src/extract/calls/aws.ts
|
|
3844
3987
|
init_cjs_shims();
|
|
3845
3988
|
var import_node_path23 = __toESM(require("path"), 1);
|
|
3846
|
-
var
|
|
3989
|
+
var import_types13 = require("@neat.is/types");
|
|
3847
3990
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
3848
3991
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
3849
3992
|
function hasMarker(text, markers) {
|
|
@@ -3867,7 +4010,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3867
4010
|
seen.add(key);
|
|
3868
4011
|
const line = lineOf(file.content, name);
|
|
3869
4012
|
out.push({
|
|
3870
|
-
infraId: (0,
|
|
4013
|
+
infraId: (0, import_types13.infraId)(kind, name),
|
|
3871
4014
|
name,
|
|
3872
4015
|
kind,
|
|
3873
4016
|
edgeType: "CALLS",
|
|
@@ -3902,7 +4045,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3902
4045
|
// src/extract/calls/grpc.ts
|
|
3903
4046
|
init_cjs_shims();
|
|
3904
4047
|
var import_node_path24 = __toESM(require("path"), 1);
|
|
3905
|
-
var
|
|
4048
|
+
var import_types14 = require("@neat.is/types");
|
|
3906
4049
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
3907
4050
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
3908
4051
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -3950,7 +4093,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3950
4093
|
const { kind } = classified;
|
|
3951
4094
|
const line = lineOf(file.content, m[0]);
|
|
3952
4095
|
out.push({
|
|
3953
|
-
infraId: (0,
|
|
4096
|
+
infraId: (0, import_types14.infraId)(kind, name),
|
|
3954
4097
|
name,
|
|
3955
4098
|
kind,
|
|
3956
4099
|
edgeType: "CALLS",
|
|
@@ -3972,11 +4115,11 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3972
4115
|
function edgeTypeFromEndpoint(ep) {
|
|
3973
4116
|
switch (ep.edgeType) {
|
|
3974
4117
|
case "PUBLISHES_TO":
|
|
3975
|
-
return
|
|
4118
|
+
return import_types15.EdgeType.PUBLISHES_TO;
|
|
3976
4119
|
case "CONSUMES_FROM":
|
|
3977
|
-
return
|
|
4120
|
+
return import_types15.EdgeType.CONSUMES_FROM;
|
|
3978
4121
|
default:
|
|
3979
|
-
return
|
|
4122
|
+
return import_types15.EdgeType.CALLS;
|
|
3980
4123
|
}
|
|
3981
4124
|
}
|
|
3982
4125
|
function isAwsKind(kind) {
|
|
@@ -4003,7 +4146,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4003
4146
|
if (!graph.hasNode(ep.infraId)) {
|
|
4004
4147
|
const node = {
|
|
4005
4148
|
id: ep.infraId,
|
|
4006
|
-
type:
|
|
4149
|
+
type: import_types15.NodeType.InfraNode,
|
|
4007
4150
|
name: ep.name,
|
|
4008
4151
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4009
4152
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4015,11 +4158,8 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4015
4158
|
nodesAdded++;
|
|
4016
4159
|
}
|
|
4017
4160
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4018
|
-
const
|
|
4019
|
-
if (
|
|
4020
|
-
seenEdges.add(edgeId);
|
|
4021
|
-
const confidence = (0, import_types14.confidenceForExtracted)(ep.confidenceKind);
|
|
4022
|
-
if (!(0, import_types14.passesExtractedFloor)(confidence)) {
|
|
4161
|
+
const confidence = (0, import_types15.confidenceForExtracted)(ep.confidenceKind);
|
|
4162
|
+
if (!(0, import_types15.passesExtractedFloor)(confidence)) {
|
|
4023
4163
|
noteExtractedDropped({
|
|
4024
4164
|
source: service.node.id,
|
|
4025
4165
|
target: ep.infraId,
|
|
@@ -4030,13 +4170,25 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4030
4170
|
});
|
|
4031
4171
|
continue;
|
|
4032
4172
|
}
|
|
4173
|
+
const relFile = toPosix2(ep.evidence.file);
|
|
4174
|
+
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4175
|
+
graph,
|
|
4176
|
+
service.pkg.name,
|
|
4177
|
+
service.node.id,
|
|
4178
|
+
relFile
|
|
4179
|
+
);
|
|
4180
|
+
nodesAdded += n;
|
|
4181
|
+
edgesAdded += e;
|
|
4182
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, ep.infraId, edgeType);
|
|
4183
|
+
if (seenEdges.has(edgeId)) continue;
|
|
4184
|
+
seenEdges.add(edgeId);
|
|
4033
4185
|
if (!graph.hasEdge(edgeId)) {
|
|
4034
4186
|
const edge = {
|
|
4035
4187
|
id: edgeId,
|
|
4036
|
-
source:
|
|
4188
|
+
source: fileNodeId,
|
|
4037
4189
|
target: ep.infraId,
|
|
4038
4190
|
type: edgeType,
|
|
4039
|
-
provenance:
|
|
4191
|
+
provenance: import_types15.Provenance.EXTRACTED,
|
|
4040
4192
|
confidence,
|
|
4041
4193
|
evidence: ep.evidence
|
|
4042
4194
|
};
|
|
@@ -4048,11 +4200,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4048
4200
|
return { nodesAdded, edgesAdded };
|
|
4049
4201
|
}
|
|
4050
4202
|
async function addCallEdges(graph, services) {
|
|
4051
|
-
const
|
|
4203
|
+
const http = await addHttpCallEdges(graph, services);
|
|
4052
4204
|
const ext = await addExternalEndpointEdges(graph, services);
|
|
4053
4205
|
return {
|
|
4054
|
-
nodesAdded: ext.nodesAdded,
|
|
4055
|
-
edgesAdded:
|
|
4206
|
+
nodesAdded: http.nodesAdded + ext.nodesAdded,
|
|
4207
|
+
edgesAdded: http.edgesAdded + ext.edgesAdded
|
|
4056
4208
|
};
|
|
4057
4209
|
}
|
|
4058
4210
|
|
|
@@ -4062,15 +4214,15 @@ init_cjs_shims();
|
|
|
4062
4214
|
// src/extract/infra/docker-compose.ts
|
|
4063
4215
|
init_cjs_shims();
|
|
4064
4216
|
var import_node_path25 = __toESM(require("path"), 1);
|
|
4065
|
-
var
|
|
4217
|
+
var import_types17 = require("@neat.is/types");
|
|
4066
4218
|
|
|
4067
4219
|
// src/extract/infra/shared.ts
|
|
4068
4220
|
init_cjs_shims();
|
|
4069
|
-
var
|
|
4221
|
+
var import_types16 = require("@neat.is/types");
|
|
4070
4222
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4071
4223
|
return {
|
|
4072
|
-
id: (0,
|
|
4073
|
-
type:
|
|
4224
|
+
id: (0, import_types16.infraId)(kind, name),
|
|
4225
|
+
type: import_types16.NodeType.InfraNode,
|
|
4074
4226
|
name,
|
|
4075
4227
|
provider,
|
|
4076
4228
|
kind,
|
|
@@ -4149,15 +4301,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4149
4301
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4150
4302
|
const targetId = composeNameToNodeId.get(dep);
|
|
4151
4303
|
if (!targetId) continue;
|
|
4152
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
4304
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types17.EdgeType.DEPENDS_ON);
|
|
4153
4305
|
if (graph.hasEdge(edgeId)) continue;
|
|
4154
4306
|
const edge = {
|
|
4155
4307
|
id: edgeId,
|
|
4156
4308
|
source: sourceId,
|
|
4157
4309
|
target: targetId,
|
|
4158
|
-
type:
|
|
4159
|
-
provenance:
|
|
4160
|
-
confidence: (0,
|
|
4310
|
+
type: import_types17.EdgeType.DEPENDS_ON,
|
|
4311
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
4312
|
+
confidence: (0, import_types17.confidenceForExtracted)("structural"),
|
|
4161
4313
|
evidence: { file: evidenceFile }
|
|
4162
4314
|
};
|
|
4163
4315
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4171,7 +4323,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4171
4323
|
init_cjs_shims();
|
|
4172
4324
|
var import_node_path26 = __toESM(require("path"), 1);
|
|
4173
4325
|
var import_node_fs14 = require("fs");
|
|
4174
|
-
var
|
|
4326
|
+
var import_types18 = require("@neat.is/types");
|
|
4175
4327
|
function runtimeImage(content) {
|
|
4176
4328
|
const lines = content.split("\n");
|
|
4177
4329
|
let last = null;
|
|
@@ -4210,15 +4362,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4210
4362
|
graph.addNode(node.id, node);
|
|
4211
4363
|
nodesAdded++;
|
|
4212
4364
|
}
|
|
4213
|
-
const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, node.id,
|
|
4365
|
+
const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, node.id, import_types18.EdgeType.RUNS_ON);
|
|
4214
4366
|
if (!graph.hasEdge(edgeId)) {
|
|
4215
4367
|
const edge = {
|
|
4216
4368
|
id: edgeId,
|
|
4217
4369
|
source: service.node.id,
|
|
4218
4370
|
target: node.id,
|
|
4219
|
-
type:
|
|
4220
|
-
provenance:
|
|
4221
|
-
confidence: (0,
|
|
4371
|
+
type: import_types18.EdgeType.RUNS_ON,
|
|
4372
|
+
provenance: import_types18.Provenance.EXTRACTED,
|
|
4373
|
+
confidence: (0, import_types18.confidenceForExtracted)("structural"),
|
|
4222
4374
|
evidence: {
|
|
4223
4375
|
file: import_node_path26.default.relative(scanPath, dockerfilePath).split(import_node_path26.default.sep).join("/")
|
|
4224
4376
|
}
|
|
@@ -4346,13 +4498,24 @@ var import_node_path30 = __toESM(require("path"), 1);
|
|
|
4346
4498
|
init_cjs_shims();
|
|
4347
4499
|
var import_node_fs17 = require("fs");
|
|
4348
4500
|
var import_node_path29 = __toESM(require("path"), 1);
|
|
4349
|
-
var
|
|
4501
|
+
var import_types19 = require("@neat.is/types");
|
|
4502
|
+
function dropOrphanedFileNodes(graph) {
|
|
4503
|
+
const orphans = [];
|
|
4504
|
+
graph.forEachNode((id, attrs) => {
|
|
4505
|
+
if (attrs.type !== import_types19.NodeType.FileNode) return;
|
|
4506
|
+
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
4507
|
+
orphans.push(id);
|
|
4508
|
+
}
|
|
4509
|
+
});
|
|
4510
|
+
for (const id of orphans) graph.dropNode(id);
|
|
4511
|
+
return orphans.length;
|
|
4512
|
+
}
|
|
4350
4513
|
function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
4351
4514
|
const toDrop = [];
|
|
4352
4515
|
const bases = [scanPath, ...serviceDirs];
|
|
4353
4516
|
graph.forEachEdge((id, attrs) => {
|
|
4354
4517
|
const edge = attrs;
|
|
4355
|
-
if (edge.provenance !==
|
|
4518
|
+
if (edge.provenance !== import_types19.Provenance.EXTRACTED) return;
|
|
4356
4519
|
const evidenceFile = edge.evidence?.file;
|
|
4357
4520
|
if (!evidenceFile) return;
|
|
4358
4521
|
if (import_node_path29.default.isAbsolute(evidenceFile)) {
|
|
@@ -4363,6 +4526,7 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
4363
4526
|
if (!found) toDrop.push(id);
|
|
4364
4527
|
});
|
|
4365
4528
|
for (const id of toDrop) graph.dropEdge(id);
|
|
4529
|
+
dropOrphanedFileNodes(graph);
|
|
4366
4530
|
return toDrop.length;
|
|
4367
4531
|
}
|
|
4368
4532
|
|
|
@@ -4432,7 +4596,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4432
4596
|
init_cjs_shims();
|
|
4433
4597
|
var import_node_fs18 = require("fs");
|
|
4434
4598
|
var import_node_path31 = __toESM(require("path"), 1);
|
|
4435
|
-
var
|
|
4599
|
+
var import_types20 = require("@neat.is/types");
|
|
4436
4600
|
var SCHEMA_VERSION = 4;
|
|
4437
4601
|
function migrateV1ToV2(payload) {
|
|
4438
4602
|
const nodes = payload.graph.nodes;
|
|
@@ -4454,12 +4618,12 @@ function migrateV2ToV3(payload) {
|
|
|
4454
4618
|
for (const edge of edges) {
|
|
4455
4619
|
const attrs = edge.attributes;
|
|
4456
4620
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
4457
|
-
attrs.provenance =
|
|
4621
|
+
attrs.provenance = import_types20.Provenance.OBSERVED;
|
|
4458
4622
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
4459
4623
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
4460
4624
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
4461
4625
|
if (type && source && target) {
|
|
4462
|
-
const newId = (0,
|
|
4626
|
+
const newId = (0, import_types20.observedEdgeId)(source, target, type);
|
|
4463
4627
|
attrs.id = newId;
|
|
4464
4628
|
if (edge.key) edge.key = newId;
|
|
4465
4629
|
}
|
|
@@ -4545,11 +4709,11 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
4545
4709
|
init_cjs_shims();
|
|
4546
4710
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
4547
4711
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
4548
|
-
var
|
|
4712
|
+
var import_types23 = require("@neat.is/types");
|
|
4549
4713
|
|
|
4550
4714
|
// src/divergences.ts
|
|
4551
4715
|
init_cjs_shims();
|
|
4552
|
-
var
|
|
4716
|
+
var import_types21 = require("@neat.is/types");
|
|
4553
4717
|
function bucketKey(source, target, type) {
|
|
4554
4718
|
return `${type}|${source}|${target}`;
|
|
4555
4719
|
}
|
|
@@ -4557,22 +4721,22 @@ function bucketEdges(graph) {
|
|
|
4557
4721
|
const buckets = /* @__PURE__ */ new Map();
|
|
4558
4722
|
graph.forEachEdge((id, attrs) => {
|
|
4559
4723
|
const e = attrs;
|
|
4560
|
-
const parsed = (0,
|
|
4724
|
+
const parsed = (0, import_types21.parseEdgeId)(id);
|
|
4561
4725
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
4562
4726
|
const key = bucketKey(e.source, e.target, e.type);
|
|
4563
4727
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
4564
4728
|
switch (provenance) {
|
|
4565
|
-
case
|
|
4729
|
+
case import_types21.Provenance.EXTRACTED:
|
|
4566
4730
|
cur.extracted = e;
|
|
4567
4731
|
break;
|
|
4568
|
-
case
|
|
4732
|
+
case import_types21.Provenance.OBSERVED:
|
|
4569
4733
|
cur.observed = e;
|
|
4570
4734
|
break;
|
|
4571
|
-
case
|
|
4735
|
+
case import_types21.Provenance.INFERRED:
|
|
4572
4736
|
cur.inferred = e;
|
|
4573
4737
|
break;
|
|
4574
4738
|
default:
|
|
4575
|
-
if (e.provenance ===
|
|
4739
|
+
if (e.provenance === import_types21.Provenance.STALE) cur.stale = e;
|
|
4576
4740
|
}
|
|
4577
4741
|
buckets.set(key, cur);
|
|
4578
4742
|
});
|
|
@@ -4581,7 +4745,7 @@ function bucketEdges(graph) {
|
|
|
4581
4745
|
function nodeIsFrontier(graph, nodeId) {
|
|
4582
4746
|
if (!graph.hasNode(nodeId)) return false;
|
|
4583
4747
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4584
|
-
return attrs.type ===
|
|
4748
|
+
return attrs.type === import_types21.NodeType.FrontierNode;
|
|
4585
4749
|
}
|
|
4586
4750
|
function clampConfidence(n) {
|
|
4587
4751
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -4602,6 +4766,7 @@ function gradedConfidence(edge) {
|
|
|
4602
4766
|
}
|
|
4603
4767
|
function detectMissingDivergences(graph, bucket) {
|
|
4604
4768
|
const out = [];
|
|
4769
|
+
if (bucket.type === import_types21.EdgeType.CONTAINS) return out;
|
|
4605
4770
|
if (bucket.extracted && !bucket.observed) {
|
|
4606
4771
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
4607
4772
|
out.push({
|
|
@@ -4642,7 +4807,7 @@ function declaredHostFor(svc) {
|
|
|
4642
4807
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
4643
4808
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4644
4809
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4645
|
-
if (e.type ===
|
|
4810
|
+
if (e.type === import_types21.EdgeType.CONFIGURED_BY && e.provenance === import_types21.Provenance.EXTRACTED) {
|
|
4646
4811
|
return true;
|
|
4647
4812
|
}
|
|
4648
4813
|
}
|
|
@@ -4655,10 +4820,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
4655
4820
|
const out = [];
|
|
4656
4821
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4657
4822
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4658
|
-
if (edge.type !==
|
|
4659
|
-
if (edge.provenance !==
|
|
4823
|
+
if (edge.type !== import_types21.EdgeType.CONNECTS_TO) continue;
|
|
4824
|
+
if (edge.provenance !== import_types21.Provenance.OBSERVED) continue;
|
|
4660
4825
|
const target = graph.getNodeAttributes(edge.target);
|
|
4661
|
-
if (target.type !==
|
|
4826
|
+
if (target.type !== import_types21.NodeType.DatabaseNode) continue;
|
|
4662
4827
|
const observedHost = target.host?.trim();
|
|
4663
4828
|
if (!observedHost) continue;
|
|
4664
4829
|
if (observedHost === declaredHost) continue;
|
|
@@ -4680,10 +4845,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
4680
4845
|
const deps = svc.dependencies ?? {};
|
|
4681
4846
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4682
4847
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4683
|
-
if (edge.type !==
|
|
4684
|
-
if (edge.provenance !==
|
|
4848
|
+
if (edge.type !== import_types21.EdgeType.CONNECTS_TO) continue;
|
|
4849
|
+
if (edge.provenance !== import_types21.Provenance.OBSERVED) continue;
|
|
4685
4850
|
const target = graph.getNodeAttributes(edge.target);
|
|
4686
|
-
if (target.type !==
|
|
4851
|
+
if (target.type !== import_types21.NodeType.DatabaseNode) continue;
|
|
4687
4852
|
for (const pair of compatPairs()) {
|
|
4688
4853
|
if (pair.engine !== target.engine) continue;
|
|
4689
4854
|
const declared = deps[pair.driver];
|
|
@@ -4744,7 +4909,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4744
4909
|
}
|
|
4745
4910
|
graph.forEachNode((nodeId, attrs) => {
|
|
4746
4911
|
const n = attrs;
|
|
4747
|
-
if (n.type !==
|
|
4912
|
+
if (n.type !== import_types21.NodeType.ServiceNode) return;
|
|
4748
4913
|
const svc = n;
|
|
4749
4914
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
4750
4915
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -4777,7 +4942,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4777
4942
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
4778
4943
|
return a.target.localeCompare(b.target);
|
|
4779
4944
|
});
|
|
4780
|
-
return
|
|
4945
|
+
return import_types21.DivergenceResultSchema.parse({
|
|
4781
4946
|
divergences: filtered,
|
|
4782
4947
|
totalAffected: filtered.length,
|
|
4783
4948
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4918,7 +5083,7 @@ init_cjs_shims();
|
|
|
4918
5083
|
var import_node_fs20 = require("fs");
|
|
4919
5084
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
4920
5085
|
var import_node_path33 = __toESM(require("path"), 1);
|
|
4921
|
-
var
|
|
5086
|
+
var import_types22 = require("@neat.is/types");
|
|
4922
5087
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
4923
5088
|
var LOCK_RETRY_MS = 50;
|
|
4924
5089
|
function neatHome() {
|
|
@@ -4997,10 +5162,10 @@ async function readRegistry() {
|
|
|
4997
5162
|
throw err;
|
|
4998
5163
|
}
|
|
4999
5164
|
const parsed = JSON.parse(raw);
|
|
5000
|
-
return
|
|
5165
|
+
return import_types22.RegistryFileSchema.parse(parsed);
|
|
5001
5166
|
}
|
|
5002
5167
|
async function writeRegistry(reg) {
|
|
5003
|
-
const validated =
|
|
5168
|
+
const validated = import_types22.RegistryFileSchema.parse(reg);
|
|
5004
5169
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
5005
5170
|
}
|
|
5006
5171
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -5272,11 +5437,11 @@ function registerRoutes(scope, ctx) {
|
|
|
5272
5437
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
5273
5438
|
const parsed = [];
|
|
5274
5439
|
for (const c of candidates) {
|
|
5275
|
-
const r =
|
|
5440
|
+
const r = import_types23.DivergenceTypeSchema.safeParse(c);
|
|
5276
5441
|
if (!r.success) {
|
|
5277
5442
|
return reply.code(400).send({
|
|
5278
5443
|
error: `unknown divergence type "${c}"`,
|
|
5279
|
-
allowed:
|
|
5444
|
+
allowed: import_types23.DivergenceTypeSchema.options
|
|
5280
5445
|
});
|
|
5281
5446
|
}
|
|
5282
5447
|
parsed.push(r.data);
|
|
@@ -5489,7 +5654,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5489
5654
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
5490
5655
|
let violations = await log.readAll();
|
|
5491
5656
|
if (req.query.severity) {
|
|
5492
|
-
const sev =
|
|
5657
|
+
const sev = import_types23.PolicySeveritySchema.safeParse(req.query.severity);
|
|
5493
5658
|
if (!sev.success) {
|
|
5494
5659
|
return reply.code(400).send({
|
|
5495
5660
|
error: "invalid severity",
|
|
@@ -5506,7 +5671,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5506
5671
|
scope.post("/policies/check", async (req, reply) => {
|
|
5507
5672
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
5508
5673
|
if (!proj) return;
|
|
5509
|
-
const parsed =
|
|
5674
|
+
const parsed = import_types23.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
5510
5675
|
if (!parsed.success) {
|
|
5511
5676
|
return reply.code(400).send({
|
|
5512
5677
|
error: "invalid /policies/check body",
|