@neat.is/core 0.2.8 → 0.2.9
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-FTKDVKBC.js → chunk-5KX7EI4F.js} +2 -2
- package/dist/{chunk-HWO746IM.js → chunk-BVF7MVR5.js} +2 -2
- package/dist/{chunk-GAYTAGEH.js → chunk-IRPH6KL4.js} +266 -146
- package/dist/chunk-IRPH6KL4.js.map +1 -0
- package/dist/cli.cjs +356 -235
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +287 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/neatd.cjs +271 -151
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +277 -156
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-GAYTAGEH.js.map +0 -1
- /package/dist/{chunk-FTKDVKBC.js.map → chunk-5KX7EI4F.js.map} +0 -0
- /package/dist/{chunk-HWO746IM.js.map → chunk-BVF7MVR5.js.map} +0 -0
package/dist/server.cjs
CHANGED
|
@@ -99,8 +99,8 @@ function reshapeGrpcRequest(req) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
function resolveProtoRoot() {
|
|
102
|
-
const here =
|
|
103
|
-
return
|
|
102
|
+
const here = import_node_path31.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
103
|
+
return import_node_path31.default.resolve(here, "..", "proto");
|
|
104
104
|
}
|
|
105
105
|
function loadTraceService() {
|
|
106
106
|
const protoRoot = resolveProtoRoot();
|
|
@@ -155,13 +155,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
155
155
|
})
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
var import_node_url,
|
|
158
|
+
var import_node_url, import_node_path31, grpc, protoLoader;
|
|
159
159
|
var init_otel_grpc = __esm({
|
|
160
160
|
"src/otel-grpc.ts"() {
|
|
161
161
|
"use strict";
|
|
162
162
|
init_cjs_shims();
|
|
163
163
|
import_node_url = require("url");
|
|
164
|
-
|
|
164
|
+
import_node_path31 = __toESM(require("path"), 1);
|
|
165
165
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
166
166
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
167
167
|
init_otel();
|
|
@@ -258,10 +258,10 @@ function parseOtlpRequest(body) {
|
|
|
258
258
|
}
|
|
259
259
|
function loadProtobufDecoder() {
|
|
260
260
|
if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
|
|
261
|
-
const here =
|
|
262
|
-
const protoRoot =
|
|
261
|
+
const here = import_node_path32.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
262
|
+
const protoRoot = import_node_path32.default.resolve(here, "..", "proto");
|
|
263
263
|
const root = new import_protobufjs.default.Root();
|
|
264
|
-
root.resolvePath = (_origin, target) =>
|
|
264
|
+
root.resolvePath = (_origin, target) => import_node_path32.default.resolve(protoRoot, target);
|
|
265
265
|
root.loadSync(
|
|
266
266
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
267
267
|
{ keepCase: true }
|
|
@@ -353,12 +353,12 @@ async function buildOtelReceiver(opts) {
|
|
|
353
353
|
};
|
|
354
354
|
return decorated;
|
|
355
355
|
}
|
|
356
|
-
var
|
|
356
|
+
var import_node_path32, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
|
|
357
357
|
var init_otel = __esm({
|
|
358
358
|
"src/otel.ts"() {
|
|
359
359
|
"use strict";
|
|
360
360
|
init_cjs_shims();
|
|
361
|
-
|
|
361
|
+
import_node_path32 = __toESM(require("path"), 1);
|
|
362
362
|
import_node_url2 = require("url");
|
|
363
363
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
364
364
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -368,7 +368,7 @@ var init_otel = __esm({
|
|
|
368
368
|
|
|
369
369
|
// src/server.ts
|
|
370
370
|
init_cjs_shims();
|
|
371
|
-
var
|
|
371
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
372
372
|
|
|
373
373
|
// src/graph.ts
|
|
374
374
|
init_cjs_shims();
|
|
@@ -812,19 +812,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
812
812
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
813
813
|
let best = { path: [start], edges: [] };
|
|
814
814
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
815
|
-
function step(node,
|
|
816
|
-
if (
|
|
817
|
-
best = { path: [...
|
|
815
|
+
function step(node, path35, edges) {
|
|
816
|
+
if (path35.length > best.path.length) {
|
|
817
|
+
best = { path: [...path35], edges: [...edges] };
|
|
818
818
|
}
|
|
819
|
-
if (
|
|
819
|
+
if (path35.length - 1 >= maxDepth) return;
|
|
820
820
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
821
821
|
for (const [srcId, edge] of incoming) {
|
|
822
822
|
if (visited.has(srcId)) continue;
|
|
823
823
|
visited.add(srcId);
|
|
824
|
-
|
|
824
|
+
path35.push(srcId);
|
|
825
825
|
edges.push(edge);
|
|
826
|
-
step(srcId,
|
|
827
|
-
|
|
826
|
+
step(srcId, path35, edges);
|
|
827
|
+
path35.pop();
|
|
828
828
|
edges.pop();
|
|
829
829
|
visited.delete(srcId);
|
|
830
830
|
}
|
|
@@ -1847,10 +1847,10 @@ async function readErrorEvents(errorsPath) {
|
|
|
1847
1847
|
|
|
1848
1848
|
// src/extract/services.ts
|
|
1849
1849
|
init_cjs_shims();
|
|
1850
|
-
var
|
|
1851
|
-
var
|
|
1850
|
+
var import_node_fs7 = require("fs");
|
|
1851
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
1852
1852
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
1853
|
-
var
|
|
1853
|
+
var import_minimatch2 = require("minimatch");
|
|
1854
1854
|
var import_types5 = require("@neat.is/types");
|
|
1855
1855
|
|
|
1856
1856
|
// src/extract/shared.ts
|
|
@@ -1963,6 +1963,73 @@ function pythonToPackage(service) {
|
|
|
1963
1963
|
};
|
|
1964
1964
|
}
|
|
1965
1965
|
|
|
1966
|
+
// src/extract/owners.ts
|
|
1967
|
+
init_cjs_shims();
|
|
1968
|
+
var import_node_fs6 = require("fs");
|
|
1969
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1970
|
+
var import_minimatch = require("minimatch");
|
|
1971
|
+
async function loadCodeowners(scanPath) {
|
|
1972
|
+
const candidates = [
|
|
1973
|
+
import_node_path6.default.join(scanPath, "CODEOWNERS"),
|
|
1974
|
+
import_node_path6.default.join(scanPath, ".github", "CODEOWNERS")
|
|
1975
|
+
];
|
|
1976
|
+
for (const file of candidates) {
|
|
1977
|
+
if (await exists(file)) {
|
|
1978
|
+
const raw = await import_node_fs6.promises.readFile(file, "utf8");
|
|
1979
|
+
return parseCodeowners(raw);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return null;
|
|
1983
|
+
}
|
|
1984
|
+
function parseCodeowners(raw) {
|
|
1985
|
+
const rules = [];
|
|
1986
|
+
for (const line of raw.split("\n")) {
|
|
1987
|
+
const trimmed = line.trim();
|
|
1988
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1989
|
+
const match = /^(\S+)\s+(.+)$/.exec(trimmed);
|
|
1990
|
+
if (!match) continue;
|
|
1991
|
+
rules.push({ pattern: match[1], owners: match[2].trim() });
|
|
1992
|
+
}
|
|
1993
|
+
return { rules };
|
|
1994
|
+
}
|
|
1995
|
+
function matchOwner(file, repoPath) {
|
|
1996
|
+
const normalized = repoPath.split(import_node_path6.default.sep).join("/");
|
|
1997
|
+
for (const rule of file.rules) {
|
|
1998
|
+
if (matchesPattern(rule.pattern, normalized)) return rule.owners;
|
|
1999
|
+
}
|
|
2000
|
+
return null;
|
|
2001
|
+
}
|
|
2002
|
+
function matchesPattern(rawPattern, repoPath) {
|
|
2003
|
+
let pattern = rawPattern.startsWith("/") ? rawPattern.slice(1) : rawPattern;
|
|
2004
|
+
if (pattern === "*") return !repoPath.includes("/");
|
|
2005
|
+
if (pattern === "**" || pattern === "") return true;
|
|
2006
|
+
if (pattern.endsWith("/")) pattern = pattern + "**";
|
|
2007
|
+
if ((0, import_minimatch.minimatch)(repoPath, pattern, { dot: true })) return true;
|
|
2008
|
+
if (!pattern.includes("*") && (0, import_minimatch.minimatch)(repoPath, pattern + "/**", { dot: true })) return true;
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
async function readPackageJsonAuthor(serviceDir) {
|
|
2012
|
+
const pkgPath = import_node_path6.default.join(serviceDir, "package.json");
|
|
2013
|
+
if (!await exists(pkgPath)) return null;
|
|
2014
|
+
try {
|
|
2015
|
+
const pkg = await readJson(pkgPath);
|
|
2016
|
+
if (!pkg.author) return null;
|
|
2017
|
+
if (typeof pkg.author === "string") return pkg.author;
|
|
2018
|
+
if (typeof pkg.author === "object" && typeof pkg.author.name === "string") return pkg.author.name;
|
|
2019
|
+
return null;
|
|
2020
|
+
} catch {
|
|
2021
|
+
return null;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
async function computeServiceOwner(codeowners, repoPath, serviceDir) {
|
|
2025
|
+
if (codeowners && repoPath !== void 0) {
|
|
2026
|
+
const owner = matchOwner(codeowners, repoPath);
|
|
2027
|
+
if (owner) return owner;
|
|
2028
|
+
}
|
|
2029
|
+
const author = await readPackageJsonAuthor(serviceDir);
|
|
2030
|
+
return author ?? void 0;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1966
2033
|
// src/extract/services.ts
|
|
1967
2034
|
var DEFAULT_SCAN_DEPTH = 5;
|
|
1968
2035
|
function parseScanDepth() {
|
|
@@ -1979,21 +2046,21 @@ function workspaceGlobs(pkg) {
|
|
|
1979
2046
|
return null;
|
|
1980
2047
|
}
|
|
1981
2048
|
async function loadGitignore(scanPath) {
|
|
1982
|
-
const gitignorePath =
|
|
2049
|
+
const gitignorePath = import_node_path7.default.join(scanPath, ".gitignore");
|
|
1983
2050
|
if (!await exists(gitignorePath)) return null;
|
|
1984
|
-
const raw = await
|
|
2051
|
+
const raw = await import_node_fs7.promises.readFile(gitignorePath, "utf8");
|
|
1985
2052
|
return (0, import_ignore.default)().add(raw);
|
|
1986
2053
|
}
|
|
1987
2054
|
async function walkDirs(start, scanPath, options, visit) {
|
|
1988
2055
|
async function recurse(current, depth) {
|
|
1989
2056
|
if (depth > options.maxDepth) return;
|
|
1990
|
-
const entries = await
|
|
2057
|
+
const entries = await import_node_fs7.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
1991
2058
|
for (const entry of entries) {
|
|
1992
2059
|
if (!entry.isDirectory()) continue;
|
|
1993
2060
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
1994
|
-
const child =
|
|
2061
|
+
const child = import_node_path7.default.join(current, entry.name);
|
|
1995
2062
|
if (options.ig) {
|
|
1996
|
-
const rel =
|
|
2063
|
+
const rel = import_node_path7.default.relative(scanPath, child).split(import_node_path7.default.sep).join("/");
|
|
1997
2064
|
if (rel && options.ig.ignores(rel + "/")) continue;
|
|
1998
2065
|
}
|
|
1999
2066
|
await visit(child);
|
|
@@ -2008,8 +2075,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
2008
2075
|
for (const raw of globs) {
|
|
2009
2076
|
const pattern = raw.replace(/^\.\//, "");
|
|
2010
2077
|
if (!pattern.includes("*")) {
|
|
2011
|
-
const candidate =
|
|
2012
|
-
if (await exists(
|
|
2078
|
+
const candidate = import_node_path7.default.join(scanPath, pattern);
|
|
2079
|
+
if (await exists(import_node_path7.default.join(candidate, "package.json"))) found.add(candidate);
|
|
2013
2080
|
continue;
|
|
2014
2081
|
}
|
|
2015
2082
|
const segments = pattern.split("/");
|
|
@@ -2018,13 +2085,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
2018
2085
|
if (seg.includes("*")) break;
|
|
2019
2086
|
staticSegments.push(seg);
|
|
2020
2087
|
}
|
|
2021
|
-
const start =
|
|
2088
|
+
const start = import_node_path7.default.join(scanPath, ...staticSegments);
|
|
2022
2089
|
if (!await exists(start)) continue;
|
|
2023
2090
|
const hasDoubleStar = pattern.includes("**");
|
|
2024
2091
|
const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
|
|
2025
2092
|
await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
|
|
2026
|
-
const rel =
|
|
2027
|
-
if ((0,
|
|
2093
|
+
const rel = import_node_path7.default.relative(scanPath, dir).split(import_node_path7.default.sep).join("/");
|
|
2094
|
+
if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path7.default.join(dir, "package.json"))) {
|
|
2028
2095
|
found.add(dir);
|
|
2029
2096
|
}
|
|
2030
2097
|
});
|
|
@@ -2032,9 +2099,17 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
2032
2099
|
return [...found];
|
|
2033
2100
|
}
|
|
2034
2101
|
async function discoverNodeService(scanPath, dir) {
|
|
2035
|
-
const pkgPath =
|
|
2102
|
+
const pkgPath = import_node_path7.default.join(dir, "package.json");
|
|
2036
2103
|
if (!await exists(pkgPath)) return null;
|
|
2037
|
-
|
|
2104
|
+
let pkg;
|
|
2105
|
+
try {
|
|
2106
|
+
pkg = await readJson(pkgPath);
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
console.warn(
|
|
2109
|
+
`[neat] services skipped ${import_node_path7.default.relative(scanPath, pkgPath)}: ${err.message}`
|
|
2110
|
+
);
|
|
2111
|
+
return null;
|
|
2112
|
+
}
|
|
2038
2113
|
if (!pkg.name) return null;
|
|
2039
2114
|
const node = {
|
|
2040
2115
|
id: (0, import_types5.serviceId)(pkg.name),
|
|
@@ -2043,7 +2118,7 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
2043
2118
|
language: "javascript",
|
|
2044
2119
|
version: pkg.version,
|
|
2045
2120
|
dependencies: pkg.dependencies ?? {},
|
|
2046
|
-
repoPath:
|
|
2121
|
+
repoPath: import_node_path7.default.relative(scanPath, dir),
|
|
2047
2122
|
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {}
|
|
2048
2123
|
};
|
|
2049
2124
|
return { pkg, dir, node };
|
|
@@ -2059,13 +2134,22 @@ async function discoverPyService(scanPath, dir) {
|
|
|
2059
2134
|
language: "python",
|
|
2060
2135
|
version: py.version,
|
|
2061
2136
|
dependencies: py.dependencies,
|
|
2062
|
-
repoPath:
|
|
2137
|
+
repoPath: import_node_path7.default.relative(scanPath, dir)
|
|
2063
2138
|
};
|
|
2064
2139
|
return { pkg, dir, node };
|
|
2065
2140
|
}
|
|
2066
2141
|
async function discoverServices(scanPath) {
|
|
2067
|
-
const rootPkgPath =
|
|
2068
|
-
|
|
2142
|
+
const rootPkgPath = import_node_path7.default.join(scanPath, "package.json");
|
|
2143
|
+
let rootPkg = null;
|
|
2144
|
+
if (await exists(rootPkgPath)) {
|
|
2145
|
+
try {
|
|
2146
|
+
rootPkg = await readJson(rootPkgPath);
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
console.warn(
|
|
2149
|
+
`[neat] services workspaces skipped ${import_node_path7.default.relative(scanPath, rootPkgPath)}: ${err.message}`
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2069
2153
|
const wsGlobs = rootPkg ? workspaceGlobs(rootPkg) : null;
|
|
2070
2154
|
const candidateDirs = [];
|
|
2071
2155
|
if (wsGlobs) {
|
|
@@ -2078,9 +2162,9 @@ async function discoverServices(scanPath) {
|
|
|
2078
2162
|
scanPath,
|
|
2079
2163
|
{ maxDepth: parseScanDepth(), ig },
|
|
2080
2164
|
async (dir) => {
|
|
2081
|
-
if (await exists(
|
|
2165
|
+
if (await exists(import_node_path7.default.join(dir, "package.json"))) {
|
|
2082
2166
|
candidateDirs.push(dir);
|
|
2083
|
-
} else if (await exists(
|
|
2167
|
+
} else if (await exists(import_node_path7.default.join(dir, "pyproject.toml")) || await exists(import_node_path7.default.join(dir, "requirements.txt")) || await exists(import_node_path7.default.join(dir, "setup.py"))) {
|
|
2084
2168
|
candidateDirs.push(dir);
|
|
2085
2169
|
}
|
|
2086
2170
|
}
|
|
@@ -2094,8 +2178,8 @@ async function discoverServices(scanPath) {
|
|
|
2094
2178
|
if (!service) continue;
|
|
2095
2179
|
const existingDir = seen.get(service.node.name);
|
|
2096
2180
|
if (existingDir !== void 0) {
|
|
2097
|
-
const a =
|
|
2098
|
-
const b =
|
|
2181
|
+
const a = import_node_path7.default.relative(scanPath, existingDir) || ".";
|
|
2182
|
+
const b = import_node_path7.default.relative(scanPath, dir) || ".";
|
|
2099
2183
|
console.warn(
|
|
2100
2184
|
`[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
|
|
2101
2185
|
);
|
|
@@ -2104,6 +2188,11 @@ async function discoverServices(scanPath) {
|
|
|
2104
2188
|
seen.set(service.node.name, dir);
|
|
2105
2189
|
out.push(service);
|
|
2106
2190
|
}
|
|
2191
|
+
const codeowners = await loadCodeowners(scanPath);
|
|
2192
|
+
for (const service of out) {
|
|
2193
|
+
const owner = await computeServiceOwner(codeowners, service.node.repoPath, service.dir);
|
|
2194
|
+
if (owner !== void 0) service.node.owner = owner;
|
|
2195
|
+
}
|
|
2107
2196
|
return out;
|
|
2108
2197
|
}
|
|
2109
2198
|
function addServiceNodes(graph, services) {
|
|
@@ -2127,8 +2216,8 @@ function addServiceNodes(graph, services) {
|
|
|
2127
2216
|
|
|
2128
2217
|
// src/extract/aliases.ts
|
|
2129
2218
|
init_cjs_shims();
|
|
2130
|
-
var
|
|
2131
|
-
var
|
|
2219
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
2220
|
+
var import_node_fs8 = require("fs");
|
|
2132
2221
|
var import_yaml2 = require("yaml");
|
|
2133
2222
|
var import_types6 = require("@neat.is/types");
|
|
2134
2223
|
var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
@@ -2155,21 +2244,29 @@ function indexServicesByName(services) {
|
|
|
2155
2244
|
const map = /* @__PURE__ */ new Map();
|
|
2156
2245
|
for (const s of services) {
|
|
2157
2246
|
map.set(s.node.name, s.node.id);
|
|
2158
|
-
map.set(
|
|
2247
|
+
map.set(import_node_path8.default.basename(s.dir), s.node.id);
|
|
2159
2248
|
}
|
|
2160
2249
|
return map;
|
|
2161
2250
|
}
|
|
2162
2251
|
async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
2163
2252
|
let composePath = null;
|
|
2164
2253
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
2165
|
-
const abs =
|
|
2254
|
+
const abs = import_node_path8.default.join(scanPath, name);
|
|
2166
2255
|
if (await exists(abs)) {
|
|
2167
2256
|
composePath = abs;
|
|
2168
2257
|
break;
|
|
2169
2258
|
}
|
|
2170
2259
|
}
|
|
2171
2260
|
if (!composePath) return;
|
|
2172
|
-
|
|
2261
|
+
let compose;
|
|
2262
|
+
try {
|
|
2263
|
+
compose = await readYaml(composePath);
|
|
2264
|
+
} catch (err) {
|
|
2265
|
+
console.warn(
|
|
2266
|
+
`[neat] aliases compose skipped ${import_node_path8.default.relative(scanPath, composePath)}: ${err.message}`
|
|
2267
|
+
);
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2173
2270
|
if (!compose?.services) return;
|
|
2174
2271
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
2175
2272
|
const serviceId3 = serviceIndex.get(composeName);
|
|
@@ -2208,9 +2305,17 @@ function parseDockerfileLabels(content) {
|
|
|
2208
2305
|
}
|
|
2209
2306
|
async function collectDockerfileAliases(graph, services) {
|
|
2210
2307
|
for (const service of services) {
|
|
2211
|
-
const dockerfilePath =
|
|
2308
|
+
const dockerfilePath = import_node_path8.default.join(service.dir, "Dockerfile");
|
|
2212
2309
|
if (!await exists(dockerfilePath)) continue;
|
|
2213
|
-
|
|
2310
|
+
let content;
|
|
2311
|
+
try {
|
|
2312
|
+
content = await import_node_fs8.promises.readFile(dockerfilePath, "utf8");
|
|
2313
|
+
} catch (err) {
|
|
2314
|
+
console.warn(
|
|
2315
|
+
`[neat] aliases dockerfile skipped ${dockerfilePath}: ${err.message}`
|
|
2316
|
+
);
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2214
2319
|
const aliases = parseDockerfileLabels(content);
|
|
2215
2320
|
if (aliases.length > 0) addAliases(graph, service.node.id, aliases);
|
|
2216
2321
|
}
|
|
@@ -2218,13 +2323,13 @@ async function collectDockerfileAliases(graph, services) {
|
|
|
2218
2323
|
async function walkYamlFiles(start, depth = 0, max = 5) {
|
|
2219
2324
|
if (depth > max) return [];
|
|
2220
2325
|
const out = [];
|
|
2221
|
-
const entries = await
|
|
2326
|
+
const entries = await import_node_fs8.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
2222
2327
|
for (const entry of entries) {
|
|
2223
2328
|
if (entry.isDirectory()) {
|
|
2224
2329
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2225
|
-
out.push(...await walkYamlFiles(
|
|
2226
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
2227
|
-
out.push(
|
|
2330
|
+
out.push(...await walkYamlFiles(import_node_path8.default.join(start, entry.name), depth + 1, max));
|
|
2331
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path8.default.extname(entry.name))) {
|
|
2332
|
+
out.push(import_node_path8.default.join(start, entry.name));
|
|
2228
2333
|
}
|
|
2229
2334
|
}
|
|
2230
2335
|
return out;
|
|
@@ -2251,7 +2356,7 @@ function k8sServiceTarget(doc, byName) {
|
|
|
2251
2356
|
async function collectK8sAliases(graph, scanPath, serviceIndex) {
|
|
2252
2357
|
const files = await walkYamlFiles(scanPath);
|
|
2253
2358
|
for (const file of files) {
|
|
2254
|
-
const content = await
|
|
2359
|
+
const content = await import_node_fs8.promises.readFile(file, "utf8");
|
|
2255
2360
|
let docs;
|
|
2256
2361
|
try {
|
|
2257
2362
|
docs = (0, import_yaml2.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -2276,14 +2381,14 @@ async function addServiceAliases(graph, scanPath, services) {
|
|
|
2276
2381
|
|
|
2277
2382
|
// src/extract/databases/index.ts
|
|
2278
2383
|
init_cjs_shims();
|
|
2279
|
-
var
|
|
2384
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
2280
2385
|
var import_types7 = require("@neat.is/types");
|
|
2281
2386
|
|
|
2282
2387
|
// src/extract/databases/db-config-yaml.ts
|
|
2283
2388
|
init_cjs_shims();
|
|
2284
|
-
var
|
|
2389
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
2285
2390
|
async function parse(serviceDir) {
|
|
2286
|
-
const yamlPath =
|
|
2391
|
+
const yamlPath = import_node_path9.default.join(serviceDir, "db-config.yaml");
|
|
2287
2392
|
if (!await exists(yamlPath)) return [];
|
|
2288
2393
|
const raw = await readYaml(yamlPath);
|
|
2289
2394
|
return [
|
|
@@ -2301,13 +2406,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
|
|
|
2301
2406
|
|
|
2302
2407
|
// src/extract/databases/dotenv.ts
|
|
2303
2408
|
init_cjs_shims();
|
|
2304
|
-
var
|
|
2305
|
-
var
|
|
2409
|
+
var import_node_fs10 = require("fs");
|
|
2410
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
2306
2411
|
|
|
2307
2412
|
// src/extract/databases/shared.ts
|
|
2308
2413
|
init_cjs_shims();
|
|
2309
|
-
var
|
|
2310
|
-
var
|
|
2414
|
+
var import_node_fs9 = require("fs");
|
|
2415
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
2311
2416
|
function schemeToEngine(scheme) {
|
|
2312
2417
|
const s = scheme.toLowerCase().split("+")[0];
|
|
2313
2418
|
switch (s) {
|
|
@@ -2346,14 +2451,14 @@ function parseConnectionString(url) {
|
|
|
2346
2451
|
}
|
|
2347
2452
|
async function readIfExists(filePath) {
|
|
2348
2453
|
try {
|
|
2349
|
-
return await
|
|
2454
|
+
return await import_node_fs9.promises.readFile(filePath, "utf8");
|
|
2350
2455
|
} catch {
|
|
2351
2456
|
return null;
|
|
2352
2457
|
}
|
|
2353
2458
|
}
|
|
2354
2459
|
async function findFirst(serviceDir, candidates) {
|
|
2355
2460
|
for (const rel of candidates) {
|
|
2356
|
-
const abs =
|
|
2461
|
+
const abs = import_node_path10.default.join(serviceDir, rel);
|
|
2357
2462
|
const content = await readIfExists(abs);
|
|
2358
2463
|
if (content !== null) return abs;
|
|
2359
2464
|
}
|
|
@@ -2404,15 +2509,15 @@ function parseDotenvLine(line) {
|
|
|
2404
2509
|
return { key, value };
|
|
2405
2510
|
}
|
|
2406
2511
|
async function parse2(serviceDir) {
|
|
2407
|
-
const entries = await
|
|
2512
|
+
const entries = await import_node_fs10.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
2408
2513
|
const configs = [];
|
|
2409
2514
|
const seen = /* @__PURE__ */ new Set();
|
|
2410
2515
|
for (const entry of entries) {
|
|
2411
2516
|
if (!entry.isFile()) continue;
|
|
2412
2517
|
const match = isConfigFile(entry.name);
|
|
2413
2518
|
if (!match.match || match.fileType !== "env") continue;
|
|
2414
|
-
const filePath =
|
|
2415
|
-
const content = await
|
|
2519
|
+
const filePath = import_node_path11.default.join(serviceDir, entry.name);
|
|
2520
|
+
const content = await import_node_fs10.promises.readFile(filePath, "utf8");
|
|
2416
2521
|
for (const line of content.split("\n")) {
|
|
2417
2522
|
const parsed = parseDotenvLine(line);
|
|
2418
2523
|
if (!parsed) continue;
|
|
@@ -2431,9 +2536,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
|
|
|
2431
2536
|
|
|
2432
2537
|
// src/extract/databases/prisma.ts
|
|
2433
2538
|
init_cjs_shims();
|
|
2434
|
-
var
|
|
2539
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
2435
2540
|
async function parse3(serviceDir) {
|
|
2436
|
-
const schemaPath =
|
|
2541
|
+
const schemaPath = import_node_path12.default.join(serviceDir, "prisma", "schema.prisma");
|
|
2437
2542
|
const content = await readIfExists(schemaPath);
|
|
2438
2543
|
if (!content) return [];
|
|
2439
2544
|
const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
|
|
@@ -2565,10 +2670,10 @@ var knexParser = { name: "knex", parse: parse5 };
|
|
|
2565
2670
|
|
|
2566
2671
|
// src/extract/databases/ormconfig.ts
|
|
2567
2672
|
init_cjs_shims();
|
|
2568
|
-
var
|
|
2673
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
2569
2674
|
async function parse6(serviceDir) {
|
|
2570
2675
|
for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
|
|
2571
|
-
const abs =
|
|
2676
|
+
const abs = import_node_path13.default.join(serviceDir, candidate);
|
|
2572
2677
|
if (!await exists(abs)) continue;
|
|
2573
2678
|
const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
|
|
2574
2679
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
@@ -2628,9 +2733,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
|
|
|
2628
2733
|
|
|
2629
2734
|
// src/extract/databases/sequelize.ts
|
|
2630
2735
|
init_cjs_shims();
|
|
2631
|
-
var
|
|
2736
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
2632
2737
|
async function parse8(serviceDir) {
|
|
2633
|
-
const configPath =
|
|
2738
|
+
const configPath = import_node_path14.default.join(serviceDir, "config", "config.json");
|
|
2634
2739
|
if (!await exists(configPath)) return [];
|
|
2635
2740
|
const raw = await readJson(configPath);
|
|
2636
2741
|
const out = [];
|
|
@@ -2657,7 +2762,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
|
2657
2762
|
|
|
2658
2763
|
// src/extract/databases/docker-compose.ts
|
|
2659
2764
|
init_cjs_shims();
|
|
2660
|
-
var
|
|
2765
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
2661
2766
|
function portFromService(svc) {
|
|
2662
2767
|
for (const raw of svc.ports ?? []) {
|
|
2663
2768
|
const str = String(raw);
|
|
@@ -2684,7 +2789,7 @@ function databaseFromEnv(svc) {
|
|
|
2684
2789
|
}
|
|
2685
2790
|
async function parse9(serviceDir) {
|
|
2686
2791
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
2687
|
-
const abs =
|
|
2792
|
+
const abs = import_node_path15.default.join(serviceDir, name);
|
|
2688
2793
|
if (!await exists(abs)) continue;
|
|
2689
2794
|
const raw = await readYaml(abs);
|
|
2690
2795
|
if (!raw?.services) return [];
|
|
@@ -2864,7 +2969,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
2864
2969
|
provenance: import_types7.Provenance.EXTRACTED,
|
|
2865
2970
|
...config.sourceFile ? {
|
|
2866
2971
|
evidence: {
|
|
2867
|
-
file:
|
|
2972
|
+
file: import_node_path16.default.relative(scanPath, config.sourceFile).split(import_node_path16.default.sep).join("/")
|
|
2868
2973
|
}
|
|
2869
2974
|
} : {}
|
|
2870
2975
|
};
|
|
@@ -2892,15 +2997,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
2892
2997
|
|
|
2893
2998
|
// src/extract/configs.ts
|
|
2894
2999
|
init_cjs_shims();
|
|
2895
|
-
var
|
|
2896
|
-
var
|
|
3000
|
+
var import_node_fs11 = require("fs");
|
|
3001
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
2897
3002
|
var import_types8 = require("@neat.is/types");
|
|
2898
3003
|
async function walkConfigFiles(dir) {
|
|
2899
3004
|
const out = [];
|
|
2900
3005
|
async function walk(current) {
|
|
2901
|
-
const entries = await
|
|
3006
|
+
const entries = await import_node_fs11.promises.readdir(current, { withFileTypes: true });
|
|
2902
3007
|
for (const entry of entries) {
|
|
2903
|
-
const full =
|
|
3008
|
+
const full = import_node_path17.default.join(current, entry.name);
|
|
2904
3009
|
if (entry.isDirectory()) {
|
|
2905
3010
|
if (!IGNORED_DIRS.has(entry.name)) await walk(full);
|
|
2906
3011
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
@@ -2917,13 +3022,13 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
2917
3022
|
for (const service of services) {
|
|
2918
3023
|
const configFiles = await walkConfigFiles(service.dir);
|
|
2919
3024
|
for (const file of configFiles) {
|
|
2920
|
-
const relPath =
|
|
3025
|
+
const relPath = import_node_path17.default.relative(scanPath, file);
|
|
2921
3026
|
const node = {
|
|
2922
3027
|
id: (0, import_types8.configId)(relPath),
|
|
2923
3028
|
type: import_types8.NodeType.ConfigNode,
|
|
2924
|
-
name:
|
|
3029
|
+
name: import_node_path17.default.basename(file),
|
|
2925
3030
|
path: relPath,
|
|
2926
|
-
fileType: isConfigFile(
|
|
3031
|
+
fileType: isConfigFile(import_node_path17.default.basename(file)).fileType
|
|
2927
3032
|
};
|
|
2928
3033
|
if (!graph.hasNode(node.id)) {
|
|
2929
3034
|
graph.addNode(node.id, node);
|
|
@@ -2935,7 +3040,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
2935
3040
|
target: node.id,
|
|
2936
3041
|
type: import_types8.EdgeType.CONFIGURED_BY,
|
|
2937
3042
|
provenance: import_types8.Provenance.EXTRACTED,
|
|
2938
|
-
evidence: { file: relPath.split(
|
|
3043
|
+
evidence: { file: relPath.split(import_node_path17.default.sep).join("/") }
|
|
2939
3044
|
};
|
|
2940
3045
|
if (!graph.hasEdge(edge.id)) {
|
|
2941
3046
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -2952,7 +3057,7 @@ var import_types14 = require("@neat.is/types");
|
|
|
2952
3057
|
|
|
2953
3058
|
// src/extract/calls/http.ts
|
|
2954
3059
|
init_cjs_shims();
|
|
2955
|
-
var
|
|
3060
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
2956
3061
|
var import_tree_sitter = __toESM(require("tree-sitter"), 1);
|
|
2957
3062
|
var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
|
|
2958
3063
|
var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
|
|
@@ -2960,17 +3065,17 @@ var import_types9 = require("@neat.is/types");
|
|
|
2960
3065
|
|
|
2961
3066
|
// src/extract/calls/shared.ts
|
|
2962
3067
|
init_cjs_shims();
|
|
2963
|
-
var
|
|
2964
|
-
var
|
|
3068
|
+
var import_node_fs12 = require("fs");
|
|
3069
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
2965
3070
|
async function walkSourceFiles(dir) {
|
|
2966
3071
|
const out = [];
|
|
2967
3072
|
async function walk(current) {
|
|
2968
|
-
const entries = await
|
|
3073
|
+
const entries = await import_node_fs12.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2969
3074
|
for (const entry of entries) {
|
|
2970
|
-
const full =
|
|
3075
|
+
const full = import_node_path18.default.join(current, entry.name);
|
|
2971
3076
|
if (entry.isDirectory()) {
|
|
2972
3077
|
if (!IGNORED_DIRS.has(entry.name)) await walk(full);
|
|
2973
|
-
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(
|
|
3078
|
+
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path18.default.extname(entry.name))) {
|
|
2974
3079
|
out.push(full);
|
|
2975
3080
|
}
|
|
2976
3081
|
}
|
|
@@ -2983,7 +3088,7 @@ async function loadSourceFiles(dir) {
|
|
|
2983
3088
|
const out = [];
|
|
2984
3089
|
for (const p of paths) {
|
|
2985
3090
|
try {
|
|
2986
|
-
const content = await
|
|
3091
|
+
const content = await import_node_fs12.promises.readFile(p, "utf8");
|
|
2987
3092
|
out.push({ path: p, content });
|
|
2988
3093
|
} catch {
|
|
2989
3094
|
}
|
|
@@ -3039,9 +3144,9 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3039
3144
|
const knownHosts = /* @__PURE__ */ new Set();
|
|
3040
3145
|
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
3041
3146
|
for (const service of services) {
|
|
3042
|
-
knownHosts.add(
|
|
3147
|
+
knownHosts.add(import_node_path19.default.basename(service.dir));
|
|
3043
3148
|
knownHosts.add(service.pkg.name);
|
|
3044
|
-
hostToNodeId.set(
|
|
3149
|
+
hostToNodeId.set(import_node_path19.default.basename(service.dir), service.node.id);
|
|
3045
3150
|
hostToNodeId.set(service.pkg.name, service.node.id);
|
|
3046
3151
|
}
|
|
3047
3152
|
let edgesAdded = 0;
|
|
@@ -3049,7 +3154,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3049
3154
|
const files = await loadSourceFiles(service.dir);
|
|
3050
3155
|
const seenTargets = /* @__PURE__ */ new Map();
|
|
3051
3156
|
for (const file of files) {
|
|
3052
|
-
const parser =
|
|
3157
|
+
const parser = import_node_path19.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
3053
3158
|
let targets;
|
|
3054
3159
|
try {
|
|
3055
3160
|
targets = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -3077,7 +3182,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3077
3182
|
type: import_types9.EdgeType.CALLS,
|
|
3078
3183
|
provenance: import_types9.Provenance.EXTRACTED,
|
|
3079
3184
|
evidence: {
|
|
3080
|
-
file:
|
|
3185
|
+
file: import_node_path19.default.relative(service.dir, evidenceFile.file),
|
|
3081
3186
|
line,
|
|
3082
3187
|
snippet: snippet(fileContent, line)
|
|
3083
3188
|
}
|
|
@@ -3093,7 +3198,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3093
3198
|
|
|
3094
3199
|
// src/extract/calls/kafka.ts
|
|
3095
3200
|
init_cjs_shims();
|
|
3096
|
-
var
|
|
3201
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
3097
3202
|
var import_types10 = require("@neat.is/types");
|
|
3098
3203
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
3099
3204
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -3120,7 +3225,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3120
3225
|
kind: "kafka-topic",
|
|
3121
3226
|
edgeType,
|
|
3122
3227
|
evidence: {
|
|
3123
|
-
file:
|
|
3228
|
+
file: import_node_path20.default.relative(serviceDir, file.path),
|
|
3124
3229
|
line,
|
|
3125
3230
|
snippet: snippet(file.content, line)
|
|
3126
3231
|
}
|
|
@@ -3133,7 +3238,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3133
3238
|
|
|
3134
3239
|
// src/extract/calls/redis.ts
|
|
3135
3240
|
init_cjs_shims();
|
|
3136
|
-
var
|
|
3241
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
3137
3242
|
var import_types11 = require("@neat.is/types");
|
|
3138
3243
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
3139
3244
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -3152,7 +3257,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3152
3257
|
kind: "redis",
|
|
3153
3258
|
edgeType: "CALLS",
|
|
3154
3259
|
evidence: {
|
|
3155
|
-
file:
|
|
3260
|
+
file: import_node_path21.default.relative(serviceDir, file.path),
|
|
3156
3261
|
line,
|
|
3157
3262
|
snippet: snippet(file.content, line)
|
|
3158
3263
|
}
|
|
@@ -3163,7 +3268,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3163
3268
|
|
|
3164
3269
|
// src/extract/calls/aws.ts
|
|
3165
3270
|
init_cjs_shims();
|
|
3166
|
-
var
|
|
3271
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
3167
3272
|
var import_types12 = require("@neat.is/types");
|
|
3168
3273
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
3169
3274
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -3193,7 +3298,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3193
3298
|
kind,
|
|
3194
3299
|
edgeType: "CALLS",
|
|
3195
3300
|
evidence: {
|
|
3196
|
-
file:
|
|
3301
|
+
file: import_node_path22.default.relative(serviceDir, file.path),
|
|
3197
3302
|
line,
|
|
3198
3303
|
snippet: snippet(file.content, line)
|
|
3199
3304
|
}
|
|
@@ -3218,7 +3323,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3218
3323
|
|
|
3219
3324
|
// src/extract/calls/grpc.ts
|
|
3220
3325
|
init_cjs_shims();
|
|
3221
|
-
var
|
|
3326
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
3222
3327
|
var import_types13 = require("@neat.is/types");
|
|
3223
3328
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
3224
3329
|
function isLikelyAddress(value) {
|
|
@@ -3243,7 +3348,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3243
3348
|
kind: "grpc-service",
|
|
3244
3349
|
edgeType: "CALLS",
|
|
3245
3350
|
evidence: {
|
|
3246
|
-
file:
|
|
3351
|
+
file: import_node_path23.default.relative(serviceDir, file.path),
|
|
3247
3352
|
line,
|
|
3248
3353
|
snippet: snippet(file.content, line)
|
|
3249
3354
|
}
|
|
@@ -3323,7 +3428,7 @@ init_cjs_shims();
|
|
|
3323
3428
|
|
|
3324
3429
|
// src/extract/infra/docker-compose.ts
|
|
3325
3430
|
init_cjs_shims();
|
|
3326
|
-
var
|
|
3431
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
3327
3432
|
var import_types16 = require("@neat.is/types");
|
|
3328
3433
|
|
|
3329
3434
|
// src/extract/infra/shared.ts
|
|
@@ -3361,7 +3466,7 @@ function dependsOnList(value) {
|
|
|
3361
3466
|
}
|
|
3362
3467
|
function serviceNameToServiceNode(name, services) {
|
|
3363
3468
|
for (const s of services) {
|
|
3364
|
-
if (s.node.name === name ||
|
|
3469
|
+
if (s.node.name === name || import_node_path24.default.basename(s.dir) === name) return s.node.id;
|
|
3365
3470
|
}
|
|
3366
3471
|
return null;
|
|
3367
3472
|
}
|
|
@@ -3370,16 +3475,24 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3370
3475
|
let edgesAdded = 0;
|
|
3371
3476
|
let composePath = null;
|
|
3372
3477
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
3373
|
-
const abs =
|
|
3478
|
+
const abs = import_node_path24.default.join(scanPath, name);
|
|
3374
3479
|
if (await exists(abs)) {
|
|
3375
3480
|
composePath = abs;
|
|
3376
3481
|
break;
|
|
3377
3482
|
}
|
|
3378
3483
|
}
|
|
3379
3484
|
if (!composePath) return { nodesAdded, edgesAdded };
|
|
3380
|
-
|
|
3485
|
+
let compose;
|
|
3486
|
+
try {
|
|
3487
|
+
compose = await readYaml(composePath);
|
|
3488
|
+
} catch (err) {
|
|
3489
|
+
console.warn(
|
|
3490
|
+
`[neat] infra docker-compose skipped ${import_node_path24.default.relative(scanPath, composePath)}: ${err.message}`
|
|
3491
|
+
);
|
|
3492
|
+
return { nodesAdded, edgesAdded };
|
|
3493
|
+
}
|
|
3381
3494
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
3382
|
-
const evidenceFile =
|
|
3495
|
+
const evidenceFile = import_node_path24.default.relative(scanPath, composePath).split(import_node_path24.default.sep).join("/");
|
|
3383
3496
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
3384
3497
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3385
3498
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -3420,8 +3533,8 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3420
3533
|
|
|
3421
3534
|
// src/extract/infra/dockerfile.ts
|
|
3422
3535
|
init_cjs_shims();
|
|
3423
|
-
var
|
|
3424
|
-
var
|
|
3536
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
3537
|
+
var import_node_fs13 = require("fs");
|
|
3425
3538
|
var import_types17 = require("@neat.is/types");
|
|
3426
3539
|
function runtimeImage(content) {
|
|
3427
3540
|
const lines = content.split("\n");
|
|
@@ -3441,9 +3554,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
3441
3554
|
let nodesAdded = 0;
|
|
3442
3555
|
let edgesAdded = 0;
|
|
3443
3556
|
for (const service of services) {
|
|
3444
|
-
const dockerfilePath =
|
|
3557
|
+
const dockerfilePath = import_node_path25.default.join(service.dir, "Dockerfile");
|
|
3445
3558
|
if (!await exists(dockerfilePath)) continue;
|
|
3446
|
-
|
|
3559
|
+
let content;
|
|
3560
|
+
try {
|
|
3561
|
+
content = await import_node_fs13.promises.readFile(dockerfilePath, "utf8");
|
|
3562
|
+
} catch (err) {
|
|
3563
|
+
console.warn(
|
|
3564
|
+
`[neat] infra dockerfile skipped ${import_node_path25.default.relative(scanPath, dockerfilePath)}: ${err.message}`
|
|
3565
|
+
);
|
|
3566
|
+
continue;
|
|
3567
|
+
}
|
|
3447
3568
|
const image = runtimeImage(content);
|
|
3448
3569
|
if (!image) continue;
|
|
3449
3570
|
const node = makeInfraNode("container-image", image);
|
|
@@ -3460,7 +3581,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
3460
3581
|
type: import_types17.EdgeType.RUNS_ON,
|
|
3461
3582
|
provenance: import_types17.Provenance.EXTRACTED,
|
|
3462
3583
|
evidence: {
|
|
3463
|
-
file:
|
|
3584
|
+
file: import_node_path25.default.relative(scanPath, dockerfilePath).split(import_node_path25.default.sep).join("/")
|
|
3464
3585
|
}
|
|
3465
3586
|
};
|
|
3466
3587
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -3472,19 +3593,19 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
3472
3593
|
|
|
3473
3594
|
// src/extract/infra/terraform.ts
|
|
3474
3595
|
init_cjs_shims();
|
|
3475
|
-
var
|
|
3476
|
-
var
|
|
3596
|
+
var import_node_fs14 = require("fs");
|
|
3597
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
3477
3598
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
3478
3599
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
3479
3600
|
if (depth > max) return [];
|
|
3480
3601
|
const out = [];
|
|
3481
|
-
const entries = await
|
|
3602
|
+
const entries = await import_node_fs14.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
3482
3603
|
for (const entry of entries) {
|
|
3483
3604
|
if (entry.isDirectory()) {
|
|
3484
3605
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
3485
|
-
out.push(...await walkTfFiles(
|
|
3606
|
+
out.push(...await walkTfFiles(import_node_path26.default.join(start, entry.name), depth + 1, max));
|
|
3486
3607
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
3487
|
-
out.push(
|
|
3608
|
+
out.push(import_node_path26.default.join(start, entry.name));
|
|
3488
3609
|
}
|
|
3489
3610
|
}
|
|
3490
3611
|
return out;
|
|
@@ -3493,7 +3614,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
3493
3614
|
let nodesAdded = 0;
|
|
3494
3615
|
const files = await walkTfFiles(scanPath);
|
|
3495
3616
|
for (const file of files) {
|
|
3496
|
-
const content = await
|
|
3617
|
+
const content = await import_node_fs14.promises.readFile(file, "utf8");
|
|
3497
3618
|
RESOURCE_RE.lastIndex = 0;
|
|
3498
3619
|
let m;
|
|
3499
3620
|
while ((m = RESOURCE_RE.exec(content)) !== null) {
|
|
@@ -3511,8 +3632,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
3511
3632
|
|
|
3512
3633
|
// src/extract/infra/k8s.ts
|
|
3513
3634
|
init_cjs_shims();
|
|
3514
|
-
var
|
|
3515
|
-
var
|
|
3635
|
+
var import_node_fs15 = require("fs");
|
|
3636
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
3516
3637
|
var import_yaml3 = require("yaml");
|
|
3517
3638
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
3518
3639
|
Service: "k8s-service",
|
|
@@ -3526,13 +3647,13 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
3526
3647
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
3527
3648
|
if (depth > max) return [];
|
|
3528
3649
|
const out = [];
|
|
3529
|
-
const entries = await
|
|
3650
|
+
const entries = await import_node_fs15.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
3530
3651
|
for (const entry of entries) {
|
|
3531
3652
|
if (entry.isDirectory()) {
|
|
3532
3653
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
3533
|
-
out.push(...await walkYamlFiles2(
|
|
3534
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
3535
|
-
out.push(
|
|
3654
|
+
out.push(...await walkYamlFiles2(import_node_path27.default.join(start, entry.name), depth + 1, max));
|
|
3655
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path27.default.extname(entry.name))) {
|
|
3656
|
+
out.push(import_node_path27.default.join(start, entry.name));
|
|
3536
3657
|
}
|
|
3537
3658
|
}
|
|
3538
3659
|
return out;
|
|
@@ -3541,7 +3662,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
3541
3662
|
let nodesAdded = 0;
|
|
3542
3663
|
const files = await walkYamlFiles2(scanPath);
|
|
3543
3664
|
for (const file of files) {
|
|
3544
|
-
const content = await
|
|
3665
|
+
const content = await import_node_fs15.promises.readFile(file, "utf8");
|
|
3545
3666
|
let docs;
|
|
3546
3667
|
try {
|
|
3547
3668
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -3607,7 +3728,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
3607
3728
|
|
|
3608
3729
|
// src/diff.ts
|
|
3609
3730
|
init_cjs_shims();
|
|
3610
|
-
var
|
|
3731
|
+
var import_node_fs16 = require("fs");
|
|
3611
3732
|
async function loadSnapshotForDiff(target) {
|
|
3612
3733
|
if (/^https?:\/\//i.test(target)) {
|
|
3613
3734
|
const res = await fetch(target);
|
|
@@ -3616,7 +3737,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
3616
3737
|
}
|
|
3617
3738
|
return await res.json();
|
|
3618
3739
|
}
|
|
3619
|
-
const raw = await
|
|
3740
|
+
const raw = await import_node_fs16.promises.readFile(target, "utf8");
|
|
3620
3741
|
return JSON.parse(raw);
|
|
3621
3742
|
}
|
|
3622
3743
|
function indexEntries(entries) {
|
|
@@ -3684,23 +3805,23 @@ function canonicalJson(value) {
|
|
|
3684
3805
|
|
|
3685
3806
|
// src/projects.ts
|
|
3686
3807
|
init_cjs_shims();
|
|
3687
|
-
var
|
|
3808
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
3688
3809
|
function pathsForProject(project, baseDir) {
|
|
3689
3810
|
if (project === DEFAULT_PROJECT) {
|
|
3690
3811
|
return {
|
|
3691
|
-
snapshotPath:
|
|
3692
|
-
errorsPath:
|
|
3693
|
-
staleEventsPath:
|
|
3694
|
-
embeddingsCachePath:
|
|
3695
|
-
policyViolationsPath:
|
|
3812
|
+
snapshotPath: import_node_path28.default.join(baseDir, "graph.json"),
|
|
3813
|
+
errorsPath: import_node_path28.default.join(baseDir, "errors.ndjson"),
|
|
3814
|
+
staleEventsPath: import_node_path28.default.join(baseDir, "stale-events.ndjson"),
|
|
3815
|
+
embeddingsCachePath: import_node_path28.default.join(baseDir, "embeddings.json"),
|
|
3816
|
+
policyViolationsPath: import_node_path28.default.join(baseDir, "policy-violations.ndjson")
|
|
3696
3817
|
};
|
|
3697
3818
|
}
|
|
3698
3819
|
return {
|
|
3699
|
-
snapshotPath:
|
|
3700
|
-
errorsPath:
|
|
3701
|
-
staleEventsPath:
|
|
3702
|
-
embeddingsCachePath:
|
|
3703
|
-
policyViolationsPath:
|
|
3820
|
+
snapshotPath: import_node_path28.default.join(baseDir, `${project}.json`),
|
|
3821
|
+
errorsPath: import_node_path28.default.join(baseDir, `errors.${project}.ndjson`),
|
|
3822
|
+
staleEventsPath: import_node_path28.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
3823
|
+
embeddingsCachePath: import_node_path28.default.join(baseDir, `embeddings.${project}.json`),
|
|
3824
|
+
policyViolationsPath: import_node_path28.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
3704
3825
|
};
|
|
3705
3826
|
}
|
|
3706
3827
|
var Projects = class {
|
|
@@ -3740,23 +3861,23 @@ function parseExtraProjects(raw) {
|
|
|
3740
3861
|
|
|
3741
3862
|
// src/registry.ts
|
|
3742
3863
|
init_cjs_shims();
|
|
3743
|
-
var
|
|
3864
|
+
var import_node_fs17 = require("fs");
|
|
3744
3865
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
3745
|
-
var
|
|
3866
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
3746
3867
|
var import_types18 = require("@neat.is/types");
|
|
3747
3868
|
function neatHome() {
|
|
3748
3869
|
const override = process.env.NEAT_HOME;
|
|
3749
|
-
if (override && override.length > 0) return
|
|
3750
|
-
return
|
|
3870
|
+
if (override && override.length > 0) return import_node_path29.default.resolve(override);
|
|
3871
|
+
return import_node_path29.default.join(import_node_os2.default.homedir(), ".neat");
|
|
3751
3872
|
}
|
|
3752
3873
|
function registryPath() {
|
|
3753
|
-
return
|
|
3874
|
+
return import_node_path29.default.join(neatHome(), "projects.json");
|
|
3754
3875
|
}
|
|
3755
3876
|
async function readRegistry() {
|
|
3756
3877
|
const file = registryPath();
|
|
3757
3878
|
let raw;
|
|
3758
3879
|
try {
|
|
3759
|
-
raw = await
|
|
3880
|
+
raw = await import_node_fs17.promises.readFile(file, "utf8");
|
|
3760
3881
|
} catch (err) {
|
|
3761
3882
|
if (err.code === "ENOENT") {
|
|
3762
3883
|
return { version: 1, projects: [] };
|
|
@@ -4192,8 +4313,8 @@ async function buildApi(opts) {
|
|
|
4192
4313
|
|
|
4193
4314
|
// src/persist.ts
|
|
4194
4315
|
init_cjs_shims();
|
|
4195
|
-
var
|
|
4196
|
-
var
|
|
4316
|
+
var import_node_fs18 = require("fs");
|
|
4317
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4197
4318
|
var SCHEMA_VERSION = 2;
|
|
4198
4319
|
function migrateV1ToV2(payload) {
|
|
4199
4320
|
const nodes = payload.graph.nodes;
|
|
@@ -4207,7 +4328,7 @@ function migrateV1ToV2(payload) {
|
|
|
4207
4328
|
return { ...payload, schemaVersion: 2 };
|
|
4208
4329
|
}
|
|
4209
4330
|
async function ensureDir(filePath) {
|
|
4210
|
-
await
|
|
4331
|
+
await import_node_fs18.promises.mkdir(import_node_path30.default.dirname(filePath), { recursive: true });
|
|
4211
4332
|
}
|
|
4212
4333
|
async function saveGraphToDisk(graph, outPath) {
|
|
4213
4334
|
await ensureDir(outPath);
|
|
@@ -4217,13 +4338,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
4217
4338
|
graph: graph.export()
|
|
4218
4339
|
};
|
|
4219
4340
|
const tmp = `${outPath}.tmp`;
|
|
4220
|
-
await
|
|
4221
|
-
await
|
|
4341
|
+
await import_node_fs18.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
4342
|
+
await import_node_fs18.promises.rename(tmp, outPath);
|
|
4222
4343
|
}
|
|
4223
4344
|
async function loadGraphFromDisk(graph, outPath) {
|
|
4224
4345
|
let raw;
|
|
4225
4346
|
try {
|
|
4226
|
-
raw = await
|
|
4347
|
+
raw = await import_node_fs18.promises.readFile(outPath, "utf8");
|
|
4227
4348
|
} catch (err) {
|
|
4228
4349
|
if (err.code === "ENOENT") return;
|
|
4229
4350
|
throw err;
|
|
@@ -4280,8 +4401,8 @@ init_otel_grpc();
|
|
|
4280
4401
|
|
|
4281
4402
|
// src/search.ts
|
|
4282
4403
|
init_cjs_shims();
|
|
4283
|
-
var
|
|
4284
|
-
var
|
|
4404
|
+
var import_node_fs19 = require("fs");
|
|
4405
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
4285
4406
|
var import_node_crypto = require("crypto");
|
|
4286
4407
|
var DEFAULT_LIMIT = 10;
|
|
4287
4408
|
var NOMIC_DIM = 768;
|
|
@@ -4411,7 +4532,7 @@ async function pickEmbedder() {
|
|
|
4411
4532
|
}
|
|
4412
4533
|
async function readCache(cachePath) {
|
|
4413
4534
|
try {
|
|
4414
|
-
const raw = await
|
|
4535
|
+
const raw = await import_node_fs19.promises.readFile(cachePath, "utf8");
|
|
4415
4536
|
const parsed = JSON.parse(raw);
|
|
4416
4537
|
if (parsed.version !== 1) return null;
|
|
4417
4538
|
return parsed;
|
|
@@ -4420,8 +4541,8 @@ async function readCache(cachePath) {
|
|
|
4420
4541
|
}
|
|
4421
4542
|
}
|
|
4422
4543
|
async function writeCache(cachePath, cache) {
|
|
4423
|
-
await
|
|
4424
|
-
await
|
|
4544
|
+
await import_node_fs19.promises.mkdir(import_node_path33.default.dirname(cachePath), { recursive: true });
|
|
4545
|
+
await import_node_fs19.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
4425
4546
|
}
|
|
4426
4547
|
var VectorIndex = class {
|
|
4427
4548
|
constructor(embedder, cachePath) {
|
|
@@ -4600,14 +4721,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
4600
4721
|
async function main() {
|
|
4601
4722
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
4602
4723
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
4603
|
-
const baseDir = baseDirEnv ?
|
|
4604
|
-
const defaultScanPath =
|
|
4724
|
+
const baseDir = baseDirEnv ? import_node_path34.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path34.default.resolve(import_node_path34.default.dirname(legacyOutPath)) : import_node_path34.default.resolve("./neat-out");
|
|
4725
|
+
const defaultScanPath = import_node_path34.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
4605
4726
|
const registry = new Projects();
|
|
4606
4727
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
4607
4728
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
4608
4729
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
4609
4730
|
const projectScan = process.env[envKey];
|
|
4610
|
-
await bootProject(registry, name, projectScan ?
|
|
4731
|
+
await bootProject(registry, name, projectScan ? import_node_path34.default.resolve(projectScan) : void 0, baseDir);
|
|
4611
4732
|
}
|
|
4612
4733
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
4613
4734
|
const port = Number(process.env.PORT ?? 8080);
|