@neat.is/core 0.8.1 → 0.8.2-dev.20260818
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-ZGZYZ33B.js → chunk-T4UGEMWN.js} +1072 -471
- package/dist/chunk-T4UGEMWN.js.map +1 -0
- package/dist/{chunk-RQTW7IV3.js → chunk-WLCHTXO3.js} +2 -2
- package/dist/cli.cjs +1648 -1044
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1336 -732
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +1349 -745
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +1243 -639
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +4 -2
- package/dist/chunk-ZGZYZ33B.js.map +0 -1
- /package/dist/{chunk-RQTW7IV3.js.map → chunk-WLCHTXO3.js.map} +0 -0
|
@@ -961,7 +961,7 @@ import { promises as fs3 } from "fs";
|
|
|
961
961
|
import path3 from "path";
|
|
962
962
|
import { parse as parseYaml } from "yaml";
|
|
963
963
|
import { extractedEdgeId } from "@neat.is/types";
|
|
964
|
-
var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php"]);
|
|
964
|
+
var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php", ".cs", ".java"]);
|
|
965
965
|
var CONFIG_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
|
|
966
966
|
var JSON_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["app.json", "app.config.json", "eas.json"]);
|
|
967
967
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -1301,6 +1301,10 @@ function languageForPath(relPath) {
|
|
|
1301
1301
|
return "ruby";
|
|
1302
1302
|
case ".php":
|
|
1303
1303
|
return "php";
|
|
1304
|
+
case ".cs":
|
|
1305
|
+
return "csharp";
|
|
1306
|
+
case ".java":
|
|
1307
|
+
return "java";
|
|
1304
1308
|
case ".ts":
|
|
1305
1309
|
case ".tsx":
|
|
1306
1310
|
return "typescript";
|
|
@@ -1842,8 +1846,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
1842
1846
|
chiWalk(tree.rootNode, "", out);
|
|
1843
1847
|
return out;
|
|
1844
1848
|
}
|
|
1845
|
-
function stripChiRegex(
|
|
1846
|
-
return
|
|
1849
|
+
function stripChiRegex(path67) {
|
|
1850
|
+
return path67.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
1847
1851
|
}
|
|
1848
1852
|
function chiWalk(node, prefix, out) {
|
|
1849
1853
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -2515,9 +2519,9 @@ function rubyRocketRoute(args) {
|
|
|
2515
2519
|
if (!pair || pair.type !== "pair") continue;
|
|
2516
2520
|
const k = pair.childForFieldName("key");
|
|
2517
2521
|
if (k?.type !== "string") continue;
|
|
2518
|
-
const
|
|
2519
|
-
if (
|
|
2520
|
-
return { path:
|
|
2522
|
+
const path67 = rubyLiteral(k);
|
|
2523
|
+
if (path67 === null) continue;
|
|
2524
|
+
return { path: path67, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
2521
2525
|
}
|
|
2522
2526
|
return null;
|
|
2523
2527
|
}
|
|
@@ -4634,29 +4638,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
4634
4638
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
4635
4639
|
});
|
|
4636
4640
|
let promoted = 0;
|
|
4637
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
4641
|
+
for (const { frontierId: frontierId2, serviceId: serviceId12 } of toPromote) {
|
|
4638
4642
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
4639
4643
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
4640
4644
|
if (!gate.allowed) {
|
|
4641
4645
|
continue;
|
|
4642
4646
|
}
|
|
4643
4647
|
}
|
|
4644
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
4648
|
+
rewireFrontierEdges(graph, frontierId2, serviceId12);
|
|
4645
4649
|
graph.dropNode(frontierId2);
|
|
4646
4650
|
promoted++;
|
|
4647
4651
|
}
|
|
4648
4652
|
return promoted;
|
|
4649
4653
|
}
|
|
4650
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
4654
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId12) {
|
|
4651
4655
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
4652
4656
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
4653
4657
|
for (const edgeId of inbound) {
|
|
4654
4658
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4655
|
-
rebuildEdge(graph, edge, edge.source,
|
|
4659
|
+
rebuildEdge(graph, edge, edge.source, serviceId12, edgeId);
|
|
4656
4660
|
}
|
|
4657
4661
|
for (const edgeId of outbound) {
|
|
4658
4662
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4659
|
-
rebuildEdge(graph, edge,
|
|
4663
|
+
rebuildEdge(graph, edge, serviceId12, edge.target, edgeId);
|
|
4660
4664
|
}
|
|
4661
4665
|
}
|
|
4662
4666
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -4988,19 +4992,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
4988
4992
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
4989
4993
|
let best = { path: [start], edges: [] };
|
|
4990
4994
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
4991
|
-
function step(node,
|
|
4992
|
-
if (
|
|
4993
|
-
best = { path: [...
|
|
4995
|
+
function step(node, path67, edges) {
|
|
4996
|
+
if (path67.length > best.path.length) {
|
|
4997
|
+
best = { path: [...path67], edges: [...edges] };
|
|
4994
4998
|
}
|
|
4995
|
-
if (
|
|
4999
|
+
if (path67.length - 1 >= maxDepth) return;
|
|
4996
5000
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
4997
5001
|
for (const [srcId, edge] of incoming) {
|
|
4998
5002
|
if (visited.has(srcId)) continue;
|
|
4999
5003
|
visited.add(srcId);
|
|
5000
|
-
|
|
5004
|
+
path67.push(srcId);
|
|
5001
5005
|
edges.push(edge);
|
|
5002
|
-
step(srcId,
|
|
5003
|
-
|
|
5006
|
+
step(srcId, path67, edges);
|
|
5007
|
+
path67.pop();
|
|
5004
5008
|
edges.pop();
|
|
5005
5009
|
visited.delete(srcId);
|
|
5006
5010
|
}
|
|
@@ -5015,7 +5019,7 @@ function databaseRootCauseShape(graph, origin, walk9) {
|
|
|
5015
5019
|
for (const id of walk9.path) {
|
|
5016
5020
|
const owner = resolveOwningService(graph, id);
|
|
5017
5021
|
if (!owner) continue;
|
|
5018
|
-
const { id:
|
|
5022
|
+
const { id: serviceId12, svc } = owner;
|
|
5019
5023
|
const deps = svc.dependencies ?? {};
|
|
5020
5024
|
for (const pair of candidatePairs) {
|
|
5021
5025
|
const declared = deps[pair.driver];
|
|
@@ -5028,7 +5032,7 @@ function databaseRootCauseShape(graph, origin, walk9) {
|
|
|
5028
5032
|
);
|
|
5029
5033
|
if (!result.compatible) {
|
|
5030
5034
|
return {
|
|
5031
|
-
rootCauseNode:
|
|
5035
|
+
rootCauseNode: serviceId12,
|
|
5032
5036
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
5033
5037
|
...result.minDriverVersion ? {
|
|
5034
5038
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -5043,7 +5047,7 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
5043
5047
|
for (const id of walk9.path) {
|
|
5044
5048
|
const owner = resolveOwningService(graph, id);
|
|
5045
5049
|
if (!owner) continue;
|
|
5046
|
-
const { id:
|
|
5050
|
+
const { id: serviceId12, svc } = owner;
|
|
5047
5051
|
const deps = svc.dependencies ?? {};
|
|
5048
5052
|
const serviceNodeEngine = svc.nodeEngine;
|
|
5049
5053
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -5052,7 +5056,7 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
5052
5056
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
5053
5057
|
if (!result.compatible && result.reason) {
|
|
5054
5058
|
return {
|
|
5055
|
-
rootCauseNode:
|
|
5059
|
+
rootCauseNode: serviceId12,
|
|
5056
5060
|
rootCauseReason: result.reason,
|
|
5057
5061
|
...result.requiredNodeVersion ? {
|
|
5058
5062
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -5067,7 +5071,7 @@ function serviceRootCauseShape(graph, _origin, walk9) {
|
|
|
5067
5071
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
5068
5072
|
if (!result.compatible && result.reason) {
|
|
5069
5073
|
return {
|
|
5070
|
-
rootCauseNode:
|
|
5074
|
+
rootCauseNode: serviceId12,
|
|
5071
5075
|
rootCauseReason: result.reason,
|
|
5072
5076
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
5073
5077
|
};
|
|
@@ -5168,9 +5172,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
5168
5172
|
function isFailingCallEdge(e) {
|
|
5169
5173
|
return e.type === EdgeType6.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
5170
5174
|
}
|
|
5171
|
-
function callSourcesForService(graph,
|
|
5172
|
-
const ids = [
|
|
5173
|
-
for (const edgeId of graph.outboundEdges(
|
|
5175
|
+
function callSourcesForService(graph, serviceId12) {
|
|
5176
|
+
const ids = [serviceId12];
|
|
5177
|
+
for (const edgeId of graph.outboundEdges(serviceId12)) {
|
|
5174
5178
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5175
5179
|
if (e.type !== EdgeType6.CONTAINS) continue;
|
|
5176
5180
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -5194,9 +5198,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
5194
5198
|
}
|
|
5195
5199
|
return id < curId;
|
|
5196
5200
|
}
|
|
5197
|
-
function dominantFailingCall(graph,
|
|
5201
|
+
function dominantFailingCall(graph, serviceId12, visited) {
|
|
5198
5202
|
let best = null;
|
|
5199
|
-
for (const src of callSourcesForService(graph,
|
|
5203
|
+
for (const src of callSourcesForService(graph, serviceId12)) {
|
|
5200
5204
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
5201
5205
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5202
5206
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -5211,26 +5215,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
|
|
|
5211
5215
|
return best;
|
|
5212
5216
|
}
|
|
5213
5217
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
5214
|
-
const
|
|
5218
|
+
const path67 = [originServiceId];
|
|
5215
5219
|
const edges = [];
|
|
5216
5220
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
5217
5221
|
let current = originServiceId;
|
|
5218
5222
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
5219
5223
|
const hop = dominantFailingCall(graph, current, visited);
|
|
5220
5224
|
if (!hop) break;
|
|
5221
|
-
|
|
5225
|
+
path67.push(hop.nextService);
|
|
5222
5226
|
edges.push(hop.edge);
|
|
5223
5227
|
visited.add(hop.nextService);
|
|
5224
5228
|
current = hop.nextService;
|
|
5225
5229
|
}
|
|
5226
5230
|
if (edges.length === 0) return null;
|
|
5227
|
-
return { path:
|
|
5231
|
+
return { path: path67, edges, culprit: current };
|
|
5228
5232
|
}
|
|
5229
5233
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
5230
5234
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
5231
5235
|
if (!chain) return null;
|
|
5232
5236
|
const culprit = chain.culprit;
|
|
5233
|
-
const
|
|
5237
|
+
const path67 = [...chain.path];
|
|
5234
5238
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
5235
5239
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
5236
5240
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -5238,14 +5242,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
5238
5242
|
if (loc) {
|
|
5239
5243
|
let rootCauseNode = culprit;
|
|
5240
5244
|
if (loc.fileNode) {
|
|
5241
|
-
|
|
5245
|
+
path67.push(loc.fileNode);
|
|
5242
5246
|
edgeProvenances.push(Provenance6.OBSERVED);
|
|
5243
5247
|
rootCauseNode = loc.fileNode;
|
|
5244
5248
|
}
|
|
5245
5249
|
return RootCauseResultSchema.parse({
|
|
5246
5250
|
rootCauseNode,
|
|
5247
5251
|
rootCauseReason: loc.rootCauseReason,
|
|
5248
|
-
traversalPath:
|
|
5252
|
+
traversalPath: path67,
|
|
5249
5253
|
edgeProvenances,
|
|
5250
5254
|
confidence,
|
|
5251
5255
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -5257,7 +5261,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
5257
5261
|
return RootCauseResultSchema.parse({
|
|
5258
5262
|
rootCauseNode: culprit,
|
|
5259
5263
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
5260
|
-
traversalPath:
|
|
5264
|
+
traversalPath: path67,
|
|
5261
5265
|
edgeProvenances,
|
|
5262
5266
|
confidence,
|
|
5263
5267
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -5693,10 +5697,10 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
|
|
|
5693
5697
|
let traversalPath = legacy.traversalPath;
|
|
5694
5698
|
let edgeProvenances = legacy.edgeProvenances;
|
|
5695
5699
|
if (top.node !== seedNode) {
|
|
5696
|
-
const
|
|
5697
|
-
if (
|
|
5698
|
-
traversalPath =
|
|
5699
|
-
edgeProvenances =
|
|
5700
|
+
const path67 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
5701
|
+
if (path67) {
|
|
5702
|
+
traversalPath = path67.nodes;
|
|
5703
|
+
edgeProvenances = path67.edges.map((e) => e.provenance);
|
|
5700
5704
|
} else {
|
|
5701
5705
|
traversalPath = [errorNodeId, top.node];
|
|
5702
5706
|
edgeProvenances = [top.provenance ?? Provenance6.OBSERVED];
|
|
@@ -5725,11 +5729,11 @@ function fixRecommendationForTop(top, seedNode, legacy) {
|
|
|
5725
5729
|
}
|
|
5726
5730
|
|
|
5727
5731
|
// src/extract/services.ts
|
|
5728
|
-
import { promises as
|
|
5729
|
-
import
|
|
5732
|
+
import { promises as fs15 } from "fs";
|
|
5733
|
+
import path17 from "path";
|
|
5730
5734
|
import ignore from "ignore";
|
|
5731
5735
|
import { minimatch as minimatch2 } from "minimatch";
|
|
5732
|
-
import { NodeType as
|
|
5736
|
+
import { NodeType as NodeType12, serviceId as serviceId8 } from "@neat.is/types";
|
|
5733
5737
|
|
|
5734
5738
|
// src/extract/python.ts
|
|
5735
5739
|
import { promises as fs8 } from "fs";
|
|
@@ -5918,18 +5922,230 @@ async function discoverPhpService(scanPath, dir) {
|
|
|
5918
5922
|
return { pkg, dir, node };
|
|
5919
5923
|
}
|
|
5920
5924
|
|
|
5921
|
-
// src/extract/
|
|
5925
|
+
// src/extract/csharp.ts
|
|
5922
5926
|
import { promises as fs11 } from "fs";
|
|
5923
5927
|
import path13 from "path";
|
|
5928
|
+
import { NodeType as NodeType9, serviceId as serviceId5 } from "@neat.is/types";
|
|
5929
|
+
var CSHARP_PROJECT_EXTS = /* @__PURE__ */ new Set([".csproj", ".sln"]);
|
|
5930
|
+
async function projectFiles(dir) {
|
|
5931
|
+
const entries = await fs11.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
5932
|
+
const out = [];
|
|
5933
|
+
for (const entry of entries) {
|
|
5934
|
+
if (!entry.isFile()) continue;
|
|
5935
|
+
if (CSHARP_PROJECT_EXTS.has(path13.extname(entry.name).toLowerCase())) out.push(entry.name);
|
|
5936
|
+
}
|
|
5937
|
+
return out.sort();
|
|
5938
|
+
}
|
|
5939
|
+
var NESTED_PROJECT_DIR = "src";
|
|
5940
|
+
async function resolveProjectDir(dir) {
|
|
5941
|
+
if ((await projectFiles(dir)).length > 0) return dir;
|
|
5942
|
+
const nested = path13.join(dir, NESTED_PROJECT_DIR);
|
|
5943
|
+
if ((await projectFiles(nested)).length > 0) return nested;
|
|
5944
|
+
return null;
|
|
5945
|
+
}
|
|
5946
|
+
async function hasCsharpProject(dir) {
|
|
5947
|
+
return await resolveProjectDir(dir) !== null;
|
|
5948
|
+
}
|
|
5949
|
+
function parseCsproj(source) {
|
|
5950
|
+
const dependencies = {};
|
|
5951
|
+
const re = /<PackageReference\b([^>]*?)\/?>/g;
|
|
5952
|
+
let m;
|
|
5953
|
+
while ((m = re.exec(source)) !== null) {
|
|
5954
|
+
const attrs = m[1] ?? "";
|
|
5955
|
+
const include = attrs.match(/\bInclude\s*=\s*"([^"]+)"/)?.[1];
|
|
5956
|
+
if (!include) continue;
|
|
5957
|
+
const version = attrs.match(/\bVersion\s*=\s*"([^"]+)"/)?.[1];
|
|
5958
|
+
dependencies[include] = version ?? "";
|
|
5959
|
+
}
|
|
5960
|
+
return { dependencies };
|
|
5961
|
+
}
|
|
5962
|
+
async function discoverCsharpService(scanPath, dir) {
|
|
5963
|
+
const projectDir = await resolveProjectDir(dir);
|
|
5964
|
+
if (projectDir === null) return null;
|
|
5965
|
+
const projects = await projectFiles(projectDir);
|
|
5966
|
+
const csproj = projects.find((p) => p.toLowerCase().endsWith(".csproj"));
|
|
5967
|
+
const name = csproj ? path13.basename(csproj, path13.extname(csproj)) : path13.basename(dir);
|
|
5968
|
+
let dependencies = {};
|
|
5969
|
+
if (csproj) {
|
|
5970
|
+
const raw = await fs11.readFile(path13.join(projectDir, csproj), "utf8").catch(() => "");
|
|
5971
|
+
dependencies = parseCsproj(raw).dependencies;
|
|
5972
|
+
}
|
|
5973
|
+
const pkg = { name, dependencies };
|
|
5974
|
+
const node = {
|
|
5975
|
+
id: serviceId5(name),
|
|
5976
|
+
type: NodeType9.ServiceNode,
|
|
5977
|
+
name,
|
|
5978
|
+
language: "csharp",
|
|
5979
|
+
dependencies,
|
|
5980
|
+
// Anchor on the directory that holds the project and its `.cs` files, so file
|
|
5981
|
+
// attribution reaches the source whether it sits at the root or under `src/`.
|
|
5982
|
+
repoPath: path13.relative(scanPath, projectDir)
|
|
5983
|
+
};
|
|
5984
|
+
return { pkg, dir: projectDir, node };
|
|
5985
|
+
}
|
|
5986
|
+
|
|
5987
|
+
// src/extract/java.ts
|
|
5988
|
+
import { promises as fs12 } from "fs";
|
|
5989
|
+
import path14 from "path";
|
|
5990
|
+
import { NodeType as NodeType10, serviceId as serviceId6 } from "@neat.is/types";
|
|
5991
|
+
var JAVA_MANIFESTS = ["pom.xml", "build.gradle", "build.gradle.kts"];
|
|
5992
|
+
async function hasJavaManifest(dir) {
|
|
5993
|
+
for (const manifest of JAVA_MANIFESTS) {
|
|
5994
|
+
if (await exists(path14.join(dir, manifest))) return true;
|
|
5995
|
+
}
|
|
5996
|
+
return false;
|
|
5997
|
+
}
|
|
5998
|
+
function parsePomXml(source) {
|
|
5999
|
+
const dependencies = {};
|
|
6000
|
+
const depBlock = /<dependency\b[^>]*>([\s\S]*?)<\/dependency>/g;
|
|
6001
|
+
let d;
|
|
6002
|
+
while ((d = depBlock.exec(source)) !== null) {
|
|
6003
|
+
const body = d[1] ?? "";
|
|
6004
|
+
const groupId = body.match(/<groupId>\s*([^<]+?)\s*<\/groupId>/)?.[1];
|
|
6005
|
+
const artifactId = body.match(/<artifactId>\s*([^<]+?)\s*<\/artifactId>/)?.[1];
|
|
6006
|
+
if (!groupId || !artifactId) continue;
|
|
6007
|
+
const version = body.match(/<version>\s*([^<]+?)\s*<\/version>/)?.[1];
|
|
6008
|
+
dependencies[`${groupId}:${artifactId}`] = version ?? "";
|
|
6009
|
+
}
|
|
6010
|
+
const withoutBlocks = source.replace(/<parent\b[^>]*>[\s\S]*?<\/parent>/g, "").replace(/<dependencyManagement\b[^>]*>[\s\S]*?<\/dependencyManagement>/g, "").replace(/<dependencies\b[^>]*>[\s\S]*?<\/dependencies>/g, "").replace(/<build\b[^>]*>[\s\S]*?<\/build>/g, "");
|
|
6011
|
+
const name = withoutBlocks.match(/<artifactId>\s*([^<]+?)\s*<\/artifactId>/)?.[1];
|
|
6012
|
+
return { name, dependencies };
|
|
6013
|
+
}
|
|
6014
|
+
var GRADLE_CONFIGS = "implementation|api|compileOnly|runtimeOnly|compileOnlyApi|testImplementation|testRuntimeOnly|testCompileOnly|annotationProcessor|developmentOnly|runtimeClasspath";
|
|
6015
|
+
function parseGradle(source) {
|
|
6016
|
+
const dependencies = {};
|
|
6017
|
+
const re = new RegExp(`\\b(?:${GRADLE_CONFIGS})\\s*\\(?\\s*(['"])([^'"]+)\\1`, "g");
|
|
6018
|
+
let m;
|
|
6019
|
+
while ((m = re.exec(source)) !== null) {
|
|
6020
|
+
const coord = m[2] ?? "";
|
|
6021
|
+
const parts = coord.split(":");
|
|
6022
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) continue;
|
|
6023
|
+
const key = `${parts[0]}:${parts[1]}`;
|
|
6024
|
+
dependencies[key] = parts.slice(2).join(":");
|
|
6025
|
+
}
|
|
6026
|
+
return { dependencies };
|
|
6027
|
+
}
|
|
6028
|
+
async function discoverJavaService(scanPath, dir) {
|
|
6029
|
+
const pomPath = path14.join(dir, "pom.xml");
|
|
6030
|
+
const gradlePath = path14.join(dir, "build.gradle");
|
|
6031
|
+
const gradleKtsPath = path14.join(dir, "build.gradle.kts");
|
|
6032
|
+
let manifest = null;
|
|
6033
|
+
if (await exists(pomPath)) {
|
|
6034
|
+
manifest = parsePomXml(await fs12.readFile(pomPath, "utf8").catch(() => ""));
|
|
6035
|
+
} else if (await exists(gradlePath)) {
|
|
6036
|
+
manifest = parseGradle(await fs12.readFile(gradlePath, "utf8").catch(() => ""));
|
|
6037
|
+
} else if (await exists(gradleKtsPath)) {
|
|
6038
|
+
manifest = parseGradle(await fs12.readFile(gradleKtsPath, "utf8").catch(() => ""));
|
|
6039
|
+
}
|
|
6040
|
+
if (!manifest) return null;
|
|
6041
|
+
const name = manifest.name?.trim() || path14.basename(dir);
|
|
6042
|
+
const dependencies = manifest.dependencies;
|
|
6043
|
+
const pkg = { name, dependencies };
|
|
6044
|
+
const node = {
|
|
6045
|
+
id: serviceId6(name),
|
|
6046
|
+
type: NodeType10.ServiceNode,
|
|
6047
|
+
name,
|
|
6048
|
+
language: "java",
|
|
6049
|
+
dependencies,
|
|
6050
|
+
repoPath: path14.relative(scanPath, dir)
|
|
6051
|
+
};
|
|
6052
|
+
return { pkg, dir, node };
|
|
6053
|
+
}
|
|
6054
|
+
|
|
6055
|
+
// src/extract/dockerfile-service.ts
|
|
6056
|
+
import { promises as fs13 } from "fs";
|
|
6057
|
+
import path15 from "path";
|
|
6058
|
+
import { NodeType as NodeType11, serviceId as serviceId7 } from "@neat.is/types";
|
|
6059
|
+
function languageForSourceExt(ext) {
|
|
6060
|
+
switch (ext) {
|
|
6061
|
+
case ".py":
|
|
6062
|
+
return "python";
|
|
6063
|
+
case ".go":
|
|
6064
|
+
return "go";
|
|
6065
|
+
case ".rb":
|
|
6066
|
+
return "ruby";
|
|
6067
|
+
case ".php":
|
|
6068
|
+
return "php";
|
|
6069
|
+
case ".cs":
|
|
6070
|
+
return "csharp";
|
|
6071
|
+
case ".java":
|
|
6072
|
+
return "java";
|
|
6073
|
+
case ".ts":
|
|
6074
|
+
case ".tsx":
|
|
6075
|
+
return "typescript";
|
|
6076
|
+
case ".js":
|
|
6077
|
+
case ".mjs":
|
|
6078
|
+
case ".cjs":
|
|
6079
|
+
return "javascript";
|
|
6080
|
+
default:
|
|
6081
|
+
return null;
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
var LANGUAGE_PRECEDENCE = ["typescript", "javascript", "python", "go", "ruby", "php", "csharp", "java"];
|
|
6085
|
+
async function inferServiceLanguage(dir) {
|
|
6086
|
+
const entries = await fs13.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
6087
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6088
|
+
for (const entry of entries) {
|
|
6089
|
+
if (!entry.isFile()) continue;
|
|
6090
|
+
if (isNeatAuthoredSourceFile(entry.name)) continue;
|
|
6091
|
+
const ext = path15.extname(entry.name).toLowerCase();
|
|
6092
|
+
if (!SERVICE_FILE_EXTENSIONS.has(ext)) continue;
|
|
6093
|
+
const lang = languageForSourceExt(ext);
|
|
6094
|
+
if (!lang) continue;
|
|
6095
|
+
counts.set(lang, (counts.get(lang) ?? 0) + 1);
|
|
6096
|
+
}
|
|
6097
|
+
if (counts.size === 0) return null;
|
|
6098
|
+
let best = null;
|
|
6099
|
+
let bestCount = -1;
|
|
6100
|
+
for (const lang of LANGUAGE_PRECEDENCE) {
|
|
6101
|
+
const c = counts.get(lang) ?? 0;
|
|
6102
|
+
if (c > bestCount) {
|
|
6103
|
+
best = lang;
|
|
6104
|
+
bestCount = c;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
return best;
|
|
6108
|
+
}
|
|
6109
|
+
async function hasLanguageManifest(dir) {
|
|
6110
|
+
return await exists(path15.join(dir, "package.json")) || await exists(path15.join(dir, "pyproject.toml")) || await exists(path15.join(dir, "requirements.txt")) || await exists(path15.join(dir, "setup.py")) || await exists(path15.join(dir, "go.mod")) || await exists(path15.join(dir, "Gemfile")) || await exists(path15.join(dir, "composer.json")) || // C#'s marker is a glob (`*.csproj` / `*.sln`), so it reads the directory
|
|
6111
|
+
// rather than probing a fixed filename (ADR-196).
|
|
6112
|
+
await hasCsharpProject(dir) || // Java keys on a Maven / Gradle build manifest (ADR-197).
|
|
6113
|
+
await hasJavaManifest(dir);
|
|
6114
|
+
}
|
|
6115
|
+
async function hasDockerfileDeclaredService(dir) {
|
|
6116
|
+
if (!await exists(path15.join(dir, "Dockerfile"))) return false;
|
|
6117
|
+
return await inferServiceLanguage(dir) !== null;
|
|
6118
|
+
}
|
|
6119
|
+
async function discoverDockerfileService(scanPath, dir) {
|
|
6120
|
+
if (!await exists(path15.join(dir, "Dockerfile"))) return null;
|
|
6121
|
+
if (await hasLanguageManifest(dir)) return null;
|
|
6122
|
+
const language = await inferServiceLanguage(dir);
|
|
6123
|
+
if (!language) return null;
|
|
6124
|
+
const name = path15.basename(dir);
|
|
6125
|
+
const pkg = { name, dependencies: {} };
|
|
6126
|
+
const node = {
|
|
6127
|
+
id: serviceId7(name),
|
|
6128
|
+
type: NodeType11.ServiceNode,
|
|
6129
|
+
name,
|
|
6130
|
+
language,
|
|
6131
|
+
dependencies: {},
|
|
6132
|
+
repoPath: path15.relative(scanPath, dir)
|
|
6133
|
+
};
|
|
6134
|
+
return { pkg, dir, node };
|
|
6135
|
+
}
|
|
6136
|
+
|
|
6137
|
+
// src/extract/owners.ts
|
|
6138
|
+
import { promises as fs14 } from "fs";
|
|
6139
|
+
import path16 from "path";
|
|
5924
6140
|
import { minimatch } from "minimatch";
|
|
5925
6141
|
async function loadCodeowners(scanPath) {
|
|
5926
6142
|
const candidates = [
|
|
5927
|
-
|
|
5928
|
-
|
|
6143
|
+
path16.join(scanPath, "CODEOWNERS"),
|
|
6144
|
+
path16.join(scanPath, ".github", "CODEOWNERS")
|
|
5929
6145
|
];
|
|
5930
6146
|
for (const file of candidates) {
|
|
5931
6147
|
if (await exists(file)) {
|
|
5932
|
-
const raw = await
|
|
6148
|
+
const raw = await fs14.readFile(file, "utf8");
|
|
5933
6149
|
return parseCodeowners(raw);
|
|
5934
6150
|
}
|
|
5935
6151
|
}
|
|
@@ -5947,7 +6163,7 @@ function parseCodeowners(raw) {
|
|
|
5947
6163
|
return { rules };
|
|
5948
6164
|
}
|
|
5949
6165
|
function matchOwner(file, repoPath) {
|
|
5950
|
-
const normalized = repoPath.split(
|
|
6166
|
+
const normalized = repoPath.split(path16.sep).join("/");
|
|
5951
6167
|
for (const rule of file.rules) {
|
|
5952
6168
|
if (matchesPattern(rule.pattern, normalized)) return rule.owners;
|
|
5953
6169
|
}
|
|
@@ -5963,7 +6179,7 @@ function matchesPattern(rawPattern, repoPath) {
|
|
|
5963
6179
|
return false;
|
|
5964
6180
|
}
|
|
5965
6181
|
async function readPackageJsonAuthor(serviceDir) {
|
|
5966
|
-
const pkgPath =
|
|
6182
|
+
const pkgPath = path16.join(serviceDir, "package.json");
|
|
5967
6183
|
if (!await exists(pkgPath)) return null;
|
|
5968
6184
|
try {
|
|
5969
6185
|
const pkg = await readJson(pkgPath);
|
|
@@ -6000,33 +6216,36 @@ function workspaceGlobs(pkg) {
|
|
|
6000
6216
|
return null;
|
|
6001
6217
|
}
|
|
6002
6218
|
async function hasPythonManifest(dir) {
|
|
6003
|
-
return await exists(
|
|
6219
|
+
return await exists(path17.join(dir, "pyproject.toml")) || await exists(path17.join(dir, "requirements.txt")) || await exists(path17.join(dir, "setup.py"));
|
|
6004
6220
|
}
|
|
6005
6221
|
async function hasGoManifest(dir) {
|
|
6006
|
-
return exists(
|
|
6222
|
+
return exists(path17.join(dir, "go.mod"));
|
|
6007
6223
|
}
|
|
6008
6224
|
async function hasRubyManifest(dir) {
|
|
6009
|
-
return exists(
|
|
6225
|
+
return exists(path17.join(dir, "Gemfile"));
|
|
6010
6226
|
}
|
|
6011
6227
|
async function hasPhpManifest(dir) {
|
|
6012
|
-
return exists(
|
|
6228
|
+
return exists(path17.join(dir, "composer.json"));
|
|
6229
|
+
}
|
|
6230
|
+
async function hasCsharpManifest(dir) {
|
|
6231
|
+
return hasCsharpProject(dir);
|
|
6013
6232
|
}
|
|
6014
6233
|
async function loadGitignore(scanPath) {
|
|
6015
|
-
const gitignorePath =
|
|
6234
|
+
const gitignorePath = path17.join(scanPath, ".gitignore");
|
|
6016
6235
|
if (!await exists(gitignorePath)) return null;
|
|
6017
|
-
const raw = await
|
|
6236
|
+
const raw = await fs15.readFile(gitignorePath, "utf8");
|
|
6018
6237
|
return ignore().add(raw);
|
|
6019
6238
|
}
|
|
6020
6239
|
async function walkDirs(start, scanPath, options, visit) {
|
|
6021
6240
|
async function recurse(current, depth) {
|
|
6022
6241
|
if (depth > options.maxDepth) return;
|
|
6023
|
-
const entries = await
|
|
6242
|
+
const entries = await fs15.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
6024
6243
|
for (const entry of entries) {
|
|
6025
6244
|
if (!entry.isDirectory()) continue;
|
|
6026
6245
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6027
|
-
const child =
|
|
6246
|
+
const child = path17.join(current, entry.name);
|
|
6028
6247
|
if (options.ig) {
|
|
6029
|
-
const rel =
|
|
6248
|
+
const rel = path17.relative(scanPath, child).split(path17.sep).join("/");
|
|
6030
6249
|
if (rel && options.ig.ignores(rel + "/")) continue;
|
|
6031
6250
|
}
|
|
6032
6251
|
if (await isPythonVenvDir(child)) continue;
|
|
@@ -6042,8 +6261,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
6042
6261
|
for (const raw of globs) {
|
|
6043
6262
|
const pattern = raw.replace(/^\.\//, "");
|
|
6044
6263
|
if (!pattern.includes("*")) {
|
|
6045
|
-
const candidate =
|
|
6046
|
-
if (await exists(
|
|
6264
|
+
const candidate = path17.join(scanPath, pattern);
|
|
6265
|
+
if (await exists(path17.join(candidate, "package.json"))) found.add(candidate);
|
|
6047
6266
|
continue;
|
|
6048
6267
|
}
|
|
6049
6268
|
const segments = pattern.split("/");
|
|
@@ -6052,13 +6271,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
6052
6271
|
if (seg.includes("*")) break;
|
|
6053
6272
|
staticSegments.push(seg);
|
|
6054
6273
|
}
|
|
6055
|
-
const start =
|
|
6274
|
+
const start = path17.join(scanPath, ...staticSegments);
|
|
6056
6275
|
if (!await exists(start)) continue;
|
|
6057
6276
|
const hasDoubleStar = pattern.includes("**");
|
|
6058
6277
|
const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
|
|
6059
6278
|
await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
|
|
6060
|
-
const rel =
|
|
6061
|
-
if (minimatch2(rel, pattern) && await exists(
|
|
6279
|
+
const rel = path17.relative(scanPath, dir).split(path17.sep).join("/");
|
|
6280
|
+
if (minimatch2(rel, pattern) && await exists(path17.join(dir, "package.json"))) {
|
|
6062
6281
|
found.add(dir);
|
|
6063
6282
|
}
|
|
6064
6283
|
});
|
|
@@ -6081,31 +6300,31 @@ function detectJsFramework(pkg) {
|
|
|
6081
6300
|
async function detectJsServiceLanguage(dir, pkg) {
|
|
6082
6301
|
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
6083
6302
|
if (deps["typescript"] !== void 0) return "typescript";
|
|
6084
|
-
const entries = await
|
|
6303
|
+
const entries = await fs15.readdir(dir).catch(() => []);
|
|
6085
6304
|
if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
|
|
6086
6305
|
return "javascript";
|
|
6087
6306
|
}
|
|
6088
6307
|
async function discoverNodeService(scanPath, dir) {
|
|
6089
|
-
const pkgPath =
|
|
6308
|
+
const pkgPath = path17.join(dir, "package.json");
|
|
6090
6309
|
if (!await exists(pkgPath)) return null;
|
|
6091
6310
|
let pkg;
|
|
6092
6311
|
try {
|
|
6093
6312
|
pkg = await readJson(pkgPath);
|
|
6094
6313
|
} catch (err) {
|
|
6095
|
-
recordExtractionError("services",
|
|
6314
|
+
recordExtractionError("services", path17.relative(scanPath, pkgPath), err);
|
|
6096
6315
|
return null;
|
|
6097
6316
|
}
|
|
6098
6317
|
if (!pkg.name) return null;
|
|
6099
6318
|
const framework = detectJsFramework(pkg);
|
|
6100
6319
|
const language = await detectJsServiceLanguage(dir, pkg);
|
|
6101
6320
|
const node = {
|
|
6102
|
-
id:
|
|
6103
|
-
type:
|
|
6321
|
+
id: serviceId8(pkg.name),
|
|
6322
|
+
type: NodeType12.ServiceNode,
|
|
6104
6323
|
name: pkg.name,
|
|
6105
6324
|
language,
|
|
6106
6325
|
version: pkg.version,
|
|
6107
6326
|
dependencies: pkg.dependencies ?? {},
|
|
6108
|
-
repoPath:
|
|
6327
|
+
repoPath: path17.relative(scanPath, dir),
|
|
6109
6328
|
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
|
|
6110
6329
|
...framework ? { framework } : {}
|
|
6111
6330
|
};
|
|
@@ -6116,18 +6335,18 @@ async function discoverPyService(scanPath, dir) {
|
|
|
6116
6335
|
if (!py) return null;
|
|
6117
6336
|
const pkg = pythonToPackage(py);
|
|
6118
6337
|
const node = {
|
|
6119
|
-
id:
|
|
6120
|
-
type:
|
|
6338
|
+
id: serviceId8(py.name),
|
|
6339
|
+
type: NodeType12.ServiceNode,
|
|
6121
6340
|
name: py.name,
|
|
6122
6341
|
language: "python",
|
|
6123
6342
|
version: py.version,
|
|
6124
6343
|
dependencies: py.dependencies,
|
|
6125
|
-
repoPath:
|
|
6344
|
+
repoPath: path17.relative(scanPath, dir)
|
|
6126
6345
|
};
|
|
6127
6346
|
return { pkg, dir, node };
|
|
6128
6347
|
}
|
|
6129
6348
|
async function discoverServices(scanPath) {
|
|
6130
|
-
const rootPkgPath =
|
|
6349
|
+
const rootPkgPath = path17.join(scanPath, "package.json");
|
|
6131
6350
|
let rootPkg = null;
|
|
6132
6351
|
if (await exists(rootPkgPath)) {
|
|
6133
6352
|
try {
|
|
@@ -6135,7 +6354,7 @@ async function discoverServices(scanPath) {
|
|
|
6135
6354
|
} catch (err) {
|
|
6136
6355
|
recordExtractionError(
|
|
6137
6356
|
"services workspaces",
|
|
6138
|
-
|
|
6357
|
+
path17.relative(scanPath, rootPkgPath),
|
|
6139
6358
|
err
|
|
6140
6359
|
);
|
|
6141
6360
|
}
|
|
@@ -6147,7 +6366,7 @@ async function discoverServices(scanPath) {
|
|
|
6147
6366
|
} else {
|
|
6148
6367
|
if (rootPkg && rootPkg.name) {
|
|
6149
6368
|
candidateDirs.push(scanPath);
|
|
6150
|
-
} else if (await hasPythonManifest(scanPath) || await hasGoManifest(scanPath) || await hasRubyManifest(scanPath) || await hasPhpManifest(scanPath)) {
|
|
6369
|
+
} else if (await hasPythonManifest(scanPath) || await hasGoManifest(scanPath) || await hasRubyManifest(scanPath) || await hasPhpManifest(scanPath) || await hasCsharpManifest(scanPath) || await hasJavaManifest(scanPath)) {
|
|
6151
6370
|
candidateDirs.push(scanPath);
|
|
6152
6371
|
}
|
|
6153
6372
|
const ig = await loadGitignore(scanPath);
|
|
@@ -6156,9 +6375,11 @@ async function discoverServices(scanPath) {
|
|
|
6156
6375
|
scanPath,
|
|
6157
6376
|
{ maxDepth: parseScanDepth(), ig },
|
|
6158
6377
|
async (dir) => {
|
|
6159
|
-
if (await exists(
|
|
6378
|
+
if (await exists(path17.join(dir, "package.json"))) {
|
|
6160
6379
|
candidateDirs.push(dir);
|
|
6161
|
-
} else if (await hasPythonManifest(dir) || await hasGoManifest(dir) || await hasRubyManifest(dir) || await hasPhpManifest(dir)) {
|
|
6380
|
+
} else if (await hasPythonManifest(dir) || await hasGoManifest(dir) || await hasRubyManifest(dir) || await hasPhpManifest(dir) || await hasCsharpManifest(dir) || await hasJavaManifest(dir)) {
|
|
6381
|
+
candidateDirs.push(dir);
|
|
6382
|
+
} else if (await hasDockerfileDeclaredService(dir)) {
|
|
6162
6383
|
candidateDirs.push(dir);
|
|
6163
6384
|
}
|
|
6164
6385
|
}
|
|
@@ -6166,14 +6387,17 @@ async function discoverServices(scanPath) {
|
|
|
6166
6387
|
}
|
|
6167
6388
|
candidateDirs.sort();
|
|
6168
6389
|
const seen = /* @__PURE__ */ new Map();
|
|
6390
|
+
const seenDirs = /* @__PURE__ */ new Set();
|
|
6169
6391
|
const out = [];
|
|
6170
6392
|
for (const dir of candidateDirs) {
|
|
6171
|
-
const service = await discoverNodeService(scanPath, dir) ?? await discoverPyService(scanPath, dir) ?? await discoverGoService(scanPath, dir) ?? await discoverRubyService(scanPath, dir) ?? await discoverPhpService(scanPath, dir);
|
|
6393
|
+
const service = await discoverNodeService(scanPath, dir) ?? await discoverPyService(scanPath, dir) ?? await discoverGoService(scanPath, dir) ?? await discoverRubyService(scanPath, dir) ?? await discoverPhpService(scanPath, dir) ?? await discoverCsharpService(scanPath, dir) ?? await discoverJavaService(scanPath, dir) ?? await discoverDockerfileService(scanPath, dir);
|
|
6172
6394
|
if (!service) continue;
|
|
6395
|
+
if (seenDirs.has(service.dir)) continue;
|
|
6396
|
+
seenDirs.add(service.dir);
|
|
6173
6397
|
const existingDir = seen.get(service.node.name);
|
|
6174
6398
|
if (existingDir !== void 0) {
|
|
6175
|
-
const a =
|
|
6176
|
-
const b =
|
|
6399
|
+
const a = path17.relative(scanPath, existingDir) || ".";
|
|
6400
|
+
const b = path17.relative(scanPath, dir) || ".";
|
|
6177
6401
|
console.warn(
|
|
6178
6402
|
`[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
|
|
6179
6403
|
);
|
|
@@ -6209,20 +6433,20 @@ function addServiceNodes(graph, services) {
|
|
|
6209
6433
|
}
|
|
6210
6434
|
|
|
6211
6435
|
// src/extract/aliases.ts
|
|
6212
|
-
import
|
|
6213
|
-
import { promises as
|
|
6436
|
+
import path18 from "path";
|
|
6437
|
+
import { promises as fs16 } from "fs";
|
|
6214
6438
|
import { parseAllDocuments } from "yaml";
|
|
6215
|
-
import { NodeType as
|
|
6439
|
+
import { NodeType as NodeType13 } from "@neat.is/types";
|
|
6216
6440
|
var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
6217
6441
|
"Service",
|
|
6218
6442
|
"Deployment",
|
|
6219
6443
|
"StatefulSet",
|
|
6220
6444
|
"DaemonSet"
|
|
6221
6445
|
]);
|
|
6222
|
-
function addAliases(graph,
|
|
6223
|
-
if (!graph.hasNode(
|
|
6224
|
-
const node = graph.getNodeAttributes(
|
|
6225
|
-
if (node.type !==
|
|
6446
|
+
function addAliases(graph, serviceId12, candidates) {
|
|
6447
|
+
if (!graph.hasNode(serviceId12)) return;
|
|
6448
|
+
const node = graph.getNodeAttributes(serviceId12);
|
|
6449
|
+
if (node.type !== NodeType13.ServiceNode) return;
|
|
6226
6450
|
const set = new Set(node.aliases ?? []);
|
|
6227
6451
|
for (const c of candidates) {
|
|
6228
6452
|
if (!c) continue;
|
|
@@ -6231,20 +6455,20 @@ function addAliases(graph, serviceId9, candidates) {
|
|
|
6231
6455
|
}
|
|
6232
6456
|
if (set.size === 0) return;
|
|
6233
6457
|
const updated = { ...node, aliases: [...set].sort() };
|
|
6234
|
-
graph.replaceNodeAttributes(
|
|
6458
|
+
graph.replaceNodeAttributes(serviceId12, updated);
|
|
6235
6459
|
}
|
|
6236
6460
|
function indexServicesByName(services) {
|
|
6237
6461
|
const map = /* @__PURE__ */ new Map();
|
|
6238
6462
|
for (const s of services) {
|
|
6239
6463
|
map.set(s.node.name, s.node.id);
|
|
6240
|
-
map.set(
|
|
6464
|
+
map.set(path18.basename(s.dir), s.node.id);
|
|
6241
6465
|
}
|
|
6242
6466
|
return map;
|
|
6243
6467
|
}
|
|
6244
6468
|
async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
6245
6469
|
let composePath = null;
|
|
6246
6470
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6247
|
-
const abs =
|
|
6471
|
+
const abs = path18.join(scanPath, name);
|
|
6248
6472
|
if (await exists(abs)) {
|
|
6249
6473
|
composePath = abs;
|
|
6250
6474
|
break;
|
|
@@ -6257,19 +6481,19 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
6257
6481
|
} catch (err) {
|
|
6258
6482
|
recordExtractionError(
|
|
6259
6483
|
"aliases compose",
|
|
6260
|
-
|
|
6484
|
+
path18.relative(scanPath, composePath),
|
|
6261
6485
|
err
|
|
6262
6486
|
);
|
|
6263
6487
|
return;
|
|
6264
6488
|
}
|
|
6265
6489
|
if (!compose?.services) return;
|
|
6266
6490
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6267
|
-
const
|
|
6268
|
-
if (!
|
|
6491
|
+
const serviceId12 = serviceIndex.get(composeName);
|
|
6492
|
+
if (!serviceId12) continue;
|
|
6269
6493
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
6270
6494
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
6271
6495
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
6272
|
-
addAliases(graph,
|
|
6496
|
+
addAliases(graph, serviceId12, aliases);
|
|
6273
6497
|
}
|
|
6274
6498
|
}
|
|
6275
6499
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -6300,11 +6524,11 @@ function parseDockerfileLabels(content) {
|
|
|
6300
6524
|
}
|
|
6301
6525
|
async function collectDockerfileAliases(graph, services) {
|
|
6302
6526
|
for (const service of services) {
|
|
6303
|
-
const dockerfilePath =
|
|
6527
|
+
const dockerfilePath = path18.join(service.dir, "Dockerfile");
|
|
6304
6528
|
if (!await exists(dockerfilePath)) continue;
|
|
6305
6529
|
let content;
|
|
6306
6530
|
try {
|
|
6307
|
-
content = await
|
|
6531
|
+
content = await fs16.readFile(dockerfilePath, "utf8");
|
|
6308
6532
|
} catch (err) {
|
|
6309
6533
|
recordExtractionError("aliases dockerfile", dockerfilePath, err);
|
|
6310
6534
|
continue;
|
|
@@ -6316,15 +6540,15 @@ async function collectDockerfileAliases(graph, services) {
|
|
|
6316
6540
|
async function walkYamlFiles(start, depth = 0, max = 5) {
|
|
6317
6541
|
if (depth > max) return [];
|
|
6318
6542
|
const out = [];
|
|
6319
|
-
const entries = await
|
|
6543
|
+
const entries = await fs16.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6320
6544
|
for (const entry of entries) {
|
|
6321
6545
|
if (entry.isDirectory()) {
|
|
6322
6546
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6323
|
-
const child =
|
|
6547
|
+
const child = path18.join(start, entry.name);
|
|
6324
6548
|
if (await isPythonVenvDir(child)) continue;
|
|
6325
6549
|
out.push(...await walkYamlFiles(child, depth + 1, max));
|
|
6326
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6327
|
-
out.push(
|
|
6550
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path18.extname(entry.name))) {
|
|
6551
|
+
out.push(path18.join(start, entry.name));
|
|
6328
6552
|
}
|
|
6329
6553
|
}
|
|
6330
6554
|
return out;
|
|
@@ -6351,7 +6575,7 @@ function k8sServiceTarget(doc, byName) {
|
|
|
6351
6575
|
async function collectK8sAliases(graph, scanPath, serviceIndex) {
|
|
6352
6576
|
const files = await walkYamlFiles(scanPath);
|
|
6353
6577
|
for (const file of files) {
|
|
6354
|
-
const content = await
|
|
6578
|
+
const content = await fs16.readFile(file, "utf8");
|
|
6355
6579
|
let docs;
|
|
6356
6580
|
try {
|
|
6357
6581
|
docs = parseAllDocuments(content).map((d) => d.toJSON());
|
|
@@ -6375,14 +6599,14 @@ async function addServiceAliases(graph, scanPath, services) {
|
|
|
6375
6599
|
}
|
|
6376
6600
|
|
|
6377
6601
|
// src/extract/files.ts
|
|
6378
|
-
import
|
|
6602
|
+
import path19 from "path";
|
|
6379
6603
|
async function addFiles(graph, services) {
|
|
6380
6604
|
let nodesAdded = 0;
|
|
6381
6605
|
let edgesAdded = 0;
|
|
6382
6606
|
for (const service of services) {
|
|
6383
6607
|
const filePaths = await walkSourceFiles(service.dir);
|
|
6384
6608
|
for (const filePath of filePaths) {
|
|
6385
|
-
const relPath = toPosix(
|
|
6609
|
+
const relPath = toPosix(path19.relative(service.dir, filePath));
|
|
6386
6610
|
const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6387
6611
|
graph,
|
|
6388
6612
|
service.pkg.name,
|
|
@@ -6397,13 +6621,19 @@ async function addFiles(graph, services) {
|
|
|
6397
6621
|
}
|
|
6398
6622
|
|
|
6399
6623
|
// src/extract/symbols.ts
|
|
6400
|
-
import
|
|
6624
|
+
import path20 from "path";
|
|
6401
6625
|
import Parser3 from "tree-sitter";
|
|
6402
6626
|
import JavaScript3 from "tree-sitter-javascript";
|
|
6403
6627
|
import TypeScript from "tree-sitter-typescript";
|
|
6628
|
+
import Python3 from "tree-sitter-python";
|
|
6629
|
+
import Go3 from "tree-sitter-go";
|
|
6630
|
+
import Ruby2 from "tree-sitter-ruby";
|
|
6631
|
+
import Php2 from "tree-sitter-php";
|
|
6632
|
+
import CSharp from "tree-sitter-c-sharp";
|
|
6633
|
+
import Java from "tree-sitter-java";
|
|
6404
6634
|
import {
|
|
6405
6635
|
EdgeType as EdgeType7,
|
|
6406
|
-
NodeType as
|
|
6636
|
+
NodeType as NodeType14,
|
|
6407
6637
|
Provenance as Provenance7,
|
|
6408
6638
|
confidenceForExtracted as confidenceForExtracted4,
|
|
6409
6639
|
extractedEdgeId as extractedEdgeId6,
|
|
@@ -6418,6 +6648,15 @@ var GRAMMAR_BY_EXT = {
|
|
|
6418
6648
|
".mjs": JavaScript3,
|
|
6419
6649
|
".cjs": JavaScript3
|
|
6420
6650
|
};
|
|
6651
|
+
var SYMBOL_GRAMMAR_BY_EXT = {
|
|
6652
|
+
...GRAMMAR_BY_EXT,
|
|
6653
|
+
".py": Python3,
|
|
6654
|
+
".go": Go3,
|
|
6655
|
+
".rb": Ruby2,
|
|
6656
|
+
".php": Php2.php_only,
|
|
6657
|
+
".cs": CSharp,
|
|
6658
|
+
".java": Java
|
|
6659
|
+
};
|
|
6421
6660
|
function parseSource3(parser, source) {
|
|
6422
6661
|
return parser.parse(
|
|
6423
6662
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK3)
|
|
@@ -6486,6 +6725,367 @@ function collectSymbolDefs(root) {
|
|
|
6486
6725
|
visit(root, void 0);
|
|
6487
6726
|
return out;
|
|
6488
6727
|
}
|
|
6728
|
+
function collectPythonSymbolDefs(root) {
|
|
6729
|
+
const out = [];
|
|
6730
|
+
const push = (kind, qualname, node) => {
|
|
6731
|
+
out.push({
|
|
6732
|
+
kind,
|
|
6733
|
+
qualname,
|
|
6734
|
+
startLine: node.startPosition.row + 1,
|
|
6735
|
+
endLine: node.endPosition.row + 1
|
|
6736
|
+
});
|
|
6737
|
+
};
|
|
6738
|
+
const visit = (node, classCtx) => {
|
|
6739
|
+
switch (node.type) {
|
|
6740
|
+
case "function_definition": {
|
|
6741
|
+
const name = node.childForFieldName("name")?.text;
|
|
6742
|
+
if (name) {
|
|
6743
|
+
if (classCtx) {
|
|
6744
|
+
const kind = name === "__init__" ? "constructor" : "method";
|
|
6745
|
+
push(kind, `${classCtx}.${name}`, node);
|
|
6746
|
+
} else {
|
|
6747
|
+
push("function", name, node);
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
const body = node.childForFieldName("body");
|
|
6751
|
+
if (body) {
|
|
6752
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
6753
|
+
const child = body.namedChild(i);
|
|
6754
|
+
if (child) visit(child, void 0);
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
return;
|
|
6758
|
+
}
|
|
6759
|
+
case "class_definition": {
|
|
6760
|
+
const name = node.childForFieldName("name")?.text;
|
|
6761
|
+
if (name) push("class", name, node);
|
|
6762
|
+
const body = node.childForFieldName("body");
|
|
6763
|
+
if (body) {
|
|
6764
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
6765
|
+
const child = body.namedChild(i);
|
|
6766
|
+
if (child) visit(child, name ?? classCtx);
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6769
|
+
return;
|
|
6770
|
+
}
|
|
6771
|
+
case "decorated_definition": {
|
|
6772
|
+
const def = node.childForFieldName("definition");
|
|
6773
|
+
if (def) visit(def, classCtx);
|
|
6774
|
+
return;
|
|
6775
|
+
}
|
|
6776
|
+
}
|
|
6777
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6778
|
+
const child = node.namedChild(i);
|
|
6779
|
+
if (child) visit(child, classCtx);
|
|
6780
|
+
}
|
|
6781
|
+
};
|
|
6782
|
+
visit(root, void 0);
|
|
6783
|
+
return out;
|
|
6784
|
+
}
|
|
6785
|
+
function goReceiverType(node) {
|
|
6786
|
+
const receiver = node.childForFieldName("receiver");
|
|
6787
|
+
if (!receiver) return void 0;
|
|
6788
|
+
for (let i = 0; i < receiver.namedChildCount; i++) {
|
|
6789
|
+
const param = receiver.namedChild(i);
|
|
6790
|
+
if (param?.type !== "parameter_declaration") continue;
|
|
6791
|
+
let typeNode = param.childForFieldName("type");
|
|
6792
|
+
if (typeNode?.type === "pointer_type") typeNode = typeNode.namedChild(0) ?? typeNode;
|
|
6793
|
+
if (typeNode?.type === "generic_type") {
|
|
6794
|
+
typeNode = typeNode.childForFieldName("type") ?? typeNode.namedChild(0) ?? typeNode;
|
|
6795
|
+
}
|
|
6796
|
+
if (typeNode?.type === "type_identifier") return typeNode.text;
|
|
6797
|
+
return typeNode?.text;
|
|
6798
|
+
}
|
|
6799
|
+
return void 0;
|
|
6800
|
+
}
|
|
6801
|
+
function collectGoSymbolDefs(root) {
|
|
6802
|
+
const out = [];
|
|
6803
|
+
const push = (kind, qualname, node) => {
|
|
6804
|
+
out.push({
|
|
6805
|
+
kind,
|
|
6806
|
+
qualname,
|
|
6807
|
+
startLine: node.startPosition.row + 1,
|
|
6808
|
+
endLine: node.endPosition.row + 1
|
|
6809
|
+
});
|
|
6810
|
+
};
|
|
6811
|
+
const visit = (node) => {
|
|
6812
|
+
switch (node.type) {
|
|
6813
|
+
case "function_declaration": {
|
|
6814
|
+
const name = node.childForFieldName("name")?.text;
|
|
6815
|
+
if (name) push("function", name, node);
|
|
6816
|
+
break;
|
|
6817
|
+
}
|
|
6818
|
+
case "method_declaration": {
|
|
6819
|
+
const name = node.childForFieldName("name")?.text;
|
|
6820
|
+
if (name) {
|
|
6821
|
+
const recv = goReceiverType(node);
|
|
6822
|
+
push("method", recv ? `${recv}.${name}` : name, node);
|
|
6823
|
+
}
|
|
6824
|
+
break;
|
|
6825
|
+
}
|
|
6826
|
+
}
|
|
6827
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6828
|
+
const child = node.namedChild(i);
|
|
6829
|
+
if (child) visit(child);
|
|
6830
|
+
}
|
|
6831
|
+
};
|
|
6832
|
+
visit(root);
|
|
6833
|
+
return out;
|
|
6834
|
+
}
|
|
6835
|
+
function collectRubySymbolDefs(root) {
|
|
6836
|
+
const out = [];
|
|
6837
|
+
const push = (kind, qualname, node) => {
|
|
6838
|
+
out.push({
|
|
6839
|
+
kind,
|
|
6840
|
+
qualname,
|
|
6841
|
+
startLine: node.startPosition.row + 1,
|
|
6842
|
+
endLine: node.endPosition.row + 1
|
|
6843
|
+
});
|
|
6844
|
+
};
|
|
6845
|
+
const nameText = (node) => node.childForFieldName("name")?.text.replace(/::/g, ".");
|
|
6846
|
+
const walkBody = (node, ctx) => {
|
|
6847
|
+
const body = node.childForFieldName("body");
|
|
6848
|
+
if (!body) return;
|
|
6849
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
6850
|
+
const child = body.namedChild(i);
|
|
6851
|
+
if (child) visit(child, ctx);
|
|
6852
|
+
}
|
|
6853
|
+
};
|
|
6854
|
+
const visit = (node, classCtx) => {
|
|
6855
|
+
switch (node.type) {
|
|
6856
|
+
case "method":
|
|
6857
|
+
case "singleton_method": {
|
|
6858
|
+
const name = node.childForFieldName("name")?.text;
|
|
6859
|
+
if (name) {
|
|
6860
|
+
if (classCtx) {
|
|
6861
|
+
const kind = node.type === "method" && name === "initialize" ? "constructor" : "method";
|
|
6862
|
+
push(kind, `${classCtx}.${name}`, node);
|
|
6863
|
+
} else {
|
|
6864
|
+
push("function", name, node);
|
|
6865
|
+
}
|
|
6866
|
+
}
|
|
6867
|
+
walkBody(node, void 0);
|
|
6868
|
+
return;
|
|
6869
|
+
}
|
|
6870
|
+
case "class":
|
|
6871
|
+
case "module": {
|
|
6872
|
+
const name = nameText(node);
|
|
6873
|
+
if (name) push("class", classCtx ? `${classCtx}.${name}` : name, node);
|
|
6874
|
+
walkBody(node, name ? classCtx ? `${classCtx}.${name}` : name : classCtx);
|
|
6875
|
+
return;
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6878
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6879
|
+
const child = node.namedChild(i);
|
|
6880
|
+
if (child) visit(child, classCtx);
|
|
6881
|
+
}
|
|
6882
|
+
};
|
|
6883
|
+
visit(root, void 0);
|
|
6884
|
+
return out;
|
|
6885
|
+
}
|
|
6886
|
+
function collectPhpSymbolDefs(root) {
|
|
6887
|
+
const out = [];
|
|
6888
|
+
const push = (kind, qualname, node) => {
|
|
6889
|
+
out.push({
|
|
6890
|
+
kind,
|
|
6891
|
+
qualname,
|
|
6892
|
+
startLine: node.startPosition.row + 1,
|
|
6893
|
+
endLine: node.endPosition.row + 1
|
|
6894
|
+
});
|
|
6895
|
+
};
|
|
6896
|
+
const dot = (s) => s.replace(/\\/g, ".").replace(/::/g, ".").replace(/^\.+/, "");
|
|
6897
|
+
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
6898
|
+
const walkChildren = (container, nsCtx, classCtx) => {
|
|
6899
|
+
let ns = nsCtx;
|
|
6900
|
+
for (let i = 0; i < container.namedChildCount; i++) {
|
|
6901
|
+
const child = container.namedChild(i);
|
|
6902
|
+
if (!child) continue;
|
|
6903
|
+
if (child.type === "namespace_definition" && !child.childForFieldName("body")) {
|
|
6904
|
+
const nameNode = child.childForFieldName("name");
|
|
6905
|
+
if (nameNode) ns = dot(nameNode.text);
|
|
6906
|
+
continue;
|
|
6907
|
+
}
|
|
6908
|
+
visit(child, ns, classCtx);
|
|
6909
|
+
}
|
|
6910
|
+
};
|
|
6911
|
+
const walkBody = (node, nsCtx, classCtx) => {
|
|
6912
|
+
const body = node.childForFieldName("body");
|
|
6913
|
+
if (body) walkChildren(body, nsCtx, classCtx);
|
|
6914
|
+
};
|
|
6915
|
+
const visit = (node, nsCtx, classCtx) => {
|
|
6916
|
+
switch (node.type) {
|
|
6917
|
+
case "function_definition": {
|
|
6918
|
+
const name = node.childForFieldName("name")?.text;
|
|
6919
|
+
if (name) push("function", join(nsCtx, name), node);
|
|
6920
|
+
walkBody(node, nsCtx, void 0);
|
|
6921
|
+
return;
|
|
6922
|
+
}
|
|
6923
|
+
case "class_declaration":
|
|
6924
|
+
case "trait_declaration":
|
|
6925
|
+
case "interface_declaration": {
|
|
6926
|
+
const name = node.childForFieldName("name")?.text;
|
|
6927
|
+
const full = name ? join(nsCtx, name) : classCtx;
|
|
6928
|
+
if (name) push("class", full, node);
|
|
6929
|
+
walkBody(node, nsCtx, full);
|
|
6930
|
+
return;
|
|
6931
|
+
}
|
|
6932
|
+
case "method_declaration": {
|
|
6933
|
+
const name = node.childForFieldName("name")?.text;
|
|
6934
|
+
if (name) {
|
|
6935
|
+
const kind = name === "__construct" ? "constructor" : "method";
|
|
6936
|
+
push(kind, join(classCtx ?? nsCtx, name), node);
|
|
6937
|
+
}
|
|
6938
|
+
return;
|
|
6939
|
+
}
|
|
6940
|
+
case "namespace_definition": {
|
|
6941
|
+
const nameNode = node.childForFieldName("name");
|
|
6942
|
+
const ns = nameNode ? dot(nameNode.text) : nsCtx;
|
|
6943
|
+
walkBody(node, ns, classCtx);
|
|
6944
|
+
return;
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6947
|
+
walkChildren(node, nsCtx, classCtx);
|
|
6948
|
+
};
|
|
6949
|
+
walkChildren(root, void 0, void 0);
|
|
6950
|
+
return out;
|
|
6951
|
+
}
|
|
6952
|
+
function collectCsharpSymbolDefs(root) {
|
|
6953
|
+
const out = [];
|
|
6954
|
+
const push = (kind, qualname, node) => {
|
|
6955
|
+
out.push({
|
|
6956
|
+
kind,
|
|
6957
|
+
qualname,
|
|
6958
|
+
startLine: node.startPosition.row + 1,
|
|
6959
|
+
endLine: node.endPosition.row + 1
|
|
6960
|
+
});
|
|
6961
|
+
};
|
|
6962
|
+
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
6963
|
+
const walkChildren = (container, nsCtx, classCtx) => {
|
|
6964
|
+
let ns = nsCtx;
|
|
6965
|
+
for (let i = 0; i < container.namedChildCount; i++) {
|
|
6966
|
+
const child = container.namedChild(i);
|
|
6967
|
+
if (!child) continue;
|
|
6968
|
+
if (child.type === "file_scoped_namespace_declaration") {
|
|
6969
|
+
const nameNode = child.childForFieldName("name");
|
|
6970
|
+
if (nameNode) ns = join(nsCtx, nameNode.text);
|
|
6971
|
+
continue;
|
|
6972
|
+
}
|
|
6973
|
+
visit(child, ns, classCtx);
|
|
6974
|
+
}
|
|
6975
|
+
};
|
|
6976
|
+
const walkBody = (node, nsCtx, classCtx) => {
|
|
6977
|
+
const body = node.childForFieldName("body");
|
|
6978
|
+
if (body) walkChildren(body, nsCtx, classCtx);
|
|
6979
|
+
};
|
|
6980
|
+
const visit = (node, nsCtx, classCtx) => {
|
|
6981
|
+
switch (node.type) {
|
|
6982
|
+
case "namespace_declaration": {
|
|
6983
|
+
const nameNode = node.childForFieldName("name");
|
|
6984
|
+
const ns = nameNode ? join(nsCtx, nameNode.text) : nsCtx;
|
|
6985
|
+
walkBody(node, ns, classCtx);
|
|
6986
|
+
return;
|
|
6987
|
+
}
|
|
6988
|
+
case "class_declaration":
|
|
6989
|
+
case "interface_declaration":
|
|
6990
|
+
case "struct_declaration":
|
|
6991
|
+
case "record_declaration": {
|
|
6992
|
+
const name = node.childForFieldName("name")?.text;
|
|
6993
|
+
const full = name ? join(classCtx ?? nsCtx, name) : classCtx;
|
|
6994
|
+
if (name) push("class", full, node);
|
|
6995
|
+
walkBody(node, nsCtx, full);
|
|
6996
|
+
return;
|
|
6997
|
+
}
|
|
6998
|
+
case "method_declaration": {
|
|
6999
|
+
const name = node.childForFieldName("name")?.text;
|
|
7000
|
+
if (name) push("method", join(classCtx ?? nsCtx, name), node);
|
|
7001
|
+
return;
|
|
7002
|
+
}
|
|
7003
|
+
case "constructor_declaration": {
|
|
7004
|
+
const name = node.childForFieldName("name")?.text;
|
|
7005
|
+
if (name) push("constructor", join(classCtx ?? nsCtx, name), node);
|
|
7006
|
+
return;
|
|
7007
|
+
}
|
|
7008
|
+
}
|
|
7009
|
+
walkChildren(node, nsCtx, classCtx);
|
|
7010
|
+
};
|
|
7011
|
+
walkChildren(root, void 0, void 0);
|
|
7012
|
+
return out;
|
|
7013
|
+
}
|
|
7014
|
+
function collectJavaSymbolDefs(root) {
|
|
7015
|
+
const out = [];
|
|
7016
|
+
const push = (kind, qualname, node) => {
|
|
7017
|
+
out.push({
|
|
7018
|
+
kind,
|
|
7019
|
+
qualname,
|
|
7020
|
+
startLine: node.startPosition.row + 1,
|
|
7021
|
+
endLine: node.endPosition.row + 1
|
|
7022
|
+
});
|
|
7023
|
+
};
|
|
7024
|
+
const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
7025
|
+
let pkg;
|
|
7026
|
+
for (let i = 0; i < root.namedChildCount; i++) {
|
|
7027
|
+
const child = root.namedChild(i);
|
|
7028
|
+
if (child?.type === "package_declaration") {
|
|
7029
|
+
pkg = child.namedChild(0)?.text;
|
|
7030
|
+
break;
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
const walkChildren = (container, classCtx) => {
|
|
7034
|
+
for (let i = 0; i < container.namedChildCount; i++) {
|
|
7035
|
+
const child = container.namedChild(i);
|
|
7036
|
+
if (child) visit(child, classCtx);
|
|
7037
|
+
}
|
|
7038
|
+
};
|
|
7039
|
+
const walkBody = (node, classCtx) => {
|
|
7040
|
+
const body = node.childForFieldName("body");
|
|
7041
|
+
if (body) walkChildren(body, classCtx);
|
|
7042
|
+
};
|
|
7043
|
+
const visit = (node, classCtx) => {
|
|
7044
|
+
switch (node.type) {
|
|
7045
|
+
case "class_declaration":
|
|
7046
|
+
case "interface_declaration":
|
|
7047
|
+
case "enum_declaration":
|
|
7048
|
+
case "record_declaration": {
|
|
7049
|
+
const name = node.childForFieldName("name")?.text;
|
|
7050
|
+
const full = name ? join(classCtx ?? pkg, name) : classCtx;
|
|
7051
|
+
if (name) push("class", full, node);
|
|
7052
|
+
walkBody(node, full);
|
|
7053
|
+
return;
|
|
7054
|
+
}
|
|
7055
|
+
case "method_declaration": {
|
|
7056
|
+
const name = node.childForFieldName("name")?.text;
|
|
7057
|
+
if (name) push("method", join(classCtx ?? pkg, name), node);
|
|
7058
|
+
return;
|
|
7059
|
+
}
|
|
7060
|
+
case "constructor_declaration": {
|
|
7061
|
+
const name = node.childForFieldName("name")?.text;
|
|
7062
|
+
if (name) push("constructor", join(classCtx ?? pkg, name), node);
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
}
|
|
7066
|
+
walkChildren(node, classCtx);
|
|
7067
|
+
};
|
|
7068
|
+
walkChildren(root, void 0);
|
|
7069
|
+
return out;
|
|
7070
|
+
}
|
|
7071
|
+
function collectSymbolDefsForExt(ext, root) {
|
|
7072
|
+
switch (ext) {
|
|
7073
|
+
case ".py":
|
|
7074
|
+
return collectPythonSymbolDefs(root);
|
|
7075
|
+
case ".go":
|
|
7076
|
+
return collectGoSymbolDefs(root);
|
|
7077
|
+
case ".rb":
|
|
7078
|
+
return collectRubySymbolDefs(root);
|
|
7079
|
+
case ".php":
|
|
7080
|
+
return collectPhpSymbolDefs(root);
|
|
7081
|
+
case ".cs":
|
|
7082
|
+
return collectCsharpSymbolDefs(root);
|
|
7083
|
+
case ".java":
|
|
7084
|
+
return collectJavaSymbolDefs(root);
|
|
7085
|
+
default:
|
|
7086
|
+
return collectSymbolDefs(root);
|
|
7087
|
+
}
|
|
7088
|
+
}
|
|
6489
7089
|
function disambiguate(defs) {
|
|
6490
7090
|
const counts = /* @__PURE__ */ new Map();
|
|
6491
7091
|
for (const def of defs) counts.set(def.qualname, (counts.get(def.qualname) ?? 0) + 1);
|
|
@@ -6500,7 +7100,7 @@ function disambiguate(defs) {
|
|
|
6500
7100
|
async function addSymbols(graph, services) {
|
|
6501
7101
|
const parsers = /* @__PURE__ */ new Map();
|
|
6502
7102
|
const parserForExt5 = (ext) => {
|
|
6503
|
-
const grammar =
|
|
7103
|
+
const grammar = SYMBOL_GRAMMAR_BY_EXT[ext];
|
|
6504
7104
|
if (!grammar) return null;
|
|
6505
7105
|
let parser = parsers.get(ext);
|
|
6506
7106
|
if (!parser) {
|
|
@@ -6515,13 +7115,14 @@ async function addSymbols(graph, services) {
|
|
|
6515
7115
|
for (const service of services) {
|
|
6516
7116
|
const files = await loadSourceFiles(service.dir);
|
|
6517
7117
|
for (const file of files) {
|
|
6518
|
-
const
|
|
7118
|
+
const ext = path20.extname(file.path);
|
|
7119
|
+
const parser = parserForExt5(ext);
|
|
6519
7120
|
if (!parser) continue;
|
|
6520
|
-
const relPath = toPosix(
|
|
7121
|
+
const relPath = toPosix(path20.relative(service.dir, file.path));
|
|
6521
7122
|
let defs;
|
|
6522
7123
|
try {
|
|
6523
7124
|
const tree = parseSource3(parser, file.content);
|
|
6524
|
-
defs =
|
|
7125
|
+
defs = collectSymbolDefsForExt(ext, tree.rootNode);
|
|
6525
7126
|
} catch (err) {
|
|
6526
7127
|
recordExtractionError("symbol extraction", file.path, err);
|
|
6527
7128
|
continue;
|
|
@@ -6540,7 +7141,7 @@ async function addSymbols(graph, services) {
|
|
|
6540
7141
|
if (!graph.hasNode(sid)) {
|
|
6541
7142
|
const node = {
|
|
6542
7143
|
id: sid,
|
|
6543
|
-
type:
|
|
7144
|
+
type: NodeType14.SymbolNode,
|
|
6544
7145
|
kind: def.kind,
|
|
6545
7146
|
qualname: def.qualname,
|
|
6546
7147
|
span: { startLine: def.startLine, endLine: def.endLine },
|
|
@@ -6576,11 +7177,11 @@ async function addSymbols(graph, services) {
|
|
|
6576
7177
|
}
|
|
6577
7178
|
|
|
6578
7179
|
// src/extract/symbol-edges.ts
|
|
6579
|
-
import
|
|
7180
|
+
import path21 from "path";
|
|
6580
7181
|
import Parser4 from "tree-sitter";
|
|
6581
7182
|
import {
|
|
6582
7183
|
EdgeType as EdgeType8,
|
|
6583
|
-
NodeType as
|
|
7184
|
+
NodeType as NodeType15,
|
|
6584
7185
|
Provenance as Provenance8,
|
|
6585
7186
|
confidenceForExtracted as confidenceForExtracted5,
|
|
6586
7187
|
extractedEdgeId as extractedEdgeId7,
|
|
@@ -6690,10 +7291,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
6690
7291
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
6691
7292
|
const files = await loadSourceFiles(service.dir);
|
|
6692
7293
|
for (const file of files) {
|
|
6693
|
-
const parser = parserForExt5(
|
|
7294
|
+
const parser = parserForExt5(path21.extname(file.path));
|
|
6694
7295
|
if (!parser) continue;
|
|
6695
|
-
const relPath = toPosix(
|
|
6696
|
-
const fileDir =
|
|
7296
|
+
const relPath = toPosix(path21.relative(service.dir, file.path));
|
|
7297
|
+
const fileDir = path21.dirname(file.path);
|
|
6697
7298
|
let root;
|
|
6698
7299
|
try {
|
|
6699
7300
|
root = parseSource3(parser, file.content).rootNode;
|
|
@@ -6735,7 +7336,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6735
7336
|
const candidate = symbolId3(serviceName, imp.targetRelPath, imp.importedName);
|
|
6736
7337
|
if (graph.hasNode(candidate)) {
|
|
6737
7338
|
const node = graph.getNodeAttributes(candidate);
|
|
6738
|
-
if (node.type ===
|
|
7339
|
+
if (node.type === NodeType15.SymbolNode && node.kind === wantKind) return candidate;
|
|
6739
7340
|
}
|
|
6740
7341
|
}
|
|
6741
7342
|
return null;
|
|
@@ -6827,11 +7428,11 @@ async function addSymbolEdges(graph, services) {
|
|
|
6827
7428
|
}
|
|
6828
7429
|
|
|
6829
7430
|
// src/extract/actions.ts
|
|
6830
|
-
import
|
|
7431
|
+
import path22 from "path";
|
|
6831
7432
|
import Parser5 from "tree-sitter";
|
|
6832
7433
|
import {
|
|
6833
7434
|
EdgeType as EdgeType9,
|
|
6834
|
-
NodeType as
|
|
7435
|
+
NodeType as NodeType16,
|
|
6835
7436
|
Provenance as Provenance9,
|
|
6836
7437
|
confidenceForExtracted as confidenceForExtracted6,
|
|
6837
7438
|
extractedEdgeId as extractedEdgeId8,
|
|
@@ -6981,9 +7582,9 @@ async function addServerActions(graph, services) {
|
|
|
6981
7582
|
const files = await loadSourceFiles(service.dir);
|
|
6982
7583
|
for (const file of files) {
|
|
6983
7584
|
if (isTestPath(file.path)) continue;
|
|
6984
|
-
const parser = parserForExt5(
|
|
7585
|
+
const parser = parserForExt5(path22.extname(file.path));
|
|
6985
7586
|
if (!parser) continue;
|
|
6986
|
-
const relPath = toPosix(
|
|
7587
|
+
const relPath = toPosix(path22.relative(service.dir, file.path));
|
|
6987
7588
|
let root;
|
|
6988
7589
|
try {
|
|
6989
7590
|
root = parseSource3(parser, file.content).rootNode;
|
|
@@ -7010,7 +7611,7 @@ async function addServerActions(graph, services) {
|
|
|
7010
7611
|
if (!graph.hasNode(aid)) {
|
|
7011
7612
|
const node = {
|
|
7012
7613
|
id: aid,
|
|
7013
|
-
type:
|
|
7614
|
+
type: NodeType16.ServerActionNode,
|
|
7014
7615
|
name: action.exportName,
|
|
7015
7616
|
service: service.pkg.name,
|
|
7016
7617
|
module: relPath,
|
|
@@ -7044,10 +7645,10 @@ async function addServerActions(graph, services) {
|
|
|
7044
7645
|
}
|
|
7045
7646
|
for (const file of files) {
|
|
7046
7647
|
if (isTestPath(file.path)) continue;
|
|
7047
|
-
const parser = parserForExt5(
|
|
7648
|
+
const parser = parserForExt5(path22.extname(file.path));
|
|
7048
7649
|
if (!parser) continue;
|
|
7049
|
-
const relPath = toPosix(
|
|
7050
|
-
const fileDir =
|
|
7650
|
+
const relPath = toPosix(path22.relative(service.dir, file.path));
|
|
7651
|
+
const fileDir = path22.dirname(file.path);
|
|
7051
7652
|
let root;
|
|
7052
7653
|
try {
|
|
7053
7654
|
root = parseSource3(parser, file.content).rootNode;
|
|
@@ -7099,10 +7700,10 @@ async function addServerActions(graph, services) {
|
|
|
7099
7700
|
}
|
|
7100
7701
|
|
|
7101
7702
|
// src/extract/databases/index.ts
|
|
7102
|
-
import
|
|
7703
|
+
import path30 from "path";
|
|
7103
7704
|
import {
|
|
7104
7705
|
EdgeType as EdgeType10,
|
|
7105
|
-
NodeType as
|
|
7706
|
+
NodeType as NodeType17,
|
|
7106
7707
|
Provenance as Provenance10,
|
|
7107
7708
|
configId,
|
|
7108
7709
|
databaseId as databaseId2,
|
|
@@ -7110,9 +7711,9 @@ import {
|
|
|
7110
7711
|
} from "@neat.is/types";
|
|
7111
7712
|
|
|
7112
7713
|
// src/extract/databases/db-config-yaml.ts
|
|
7113
|
-
import
|
|
7714
|
+
import path23 from "path";
|
|
7114
7715
|
async function parse(serviceDir) {
|
|
7115
|
-
const yamlPath =
|
|
7716
|
+
const yamlPath = path23.join(serviceDir, "db-config.yaml");
|
|
7116
7717
|
if (!await exists(yamlPath)) return [];
|
|
7117
7718
|
const raw = await readYaml(yamlPath);
|
|
7118
7719
|
return [
|
|
@@ -7129,12 +7730,12 @@ async function parse(serviceDir) {
|
|
|
7129
7730
|
var dbConfigYamlParser = { name: "db-config.yaml", parse };
|
|
7130
7731
|
|
|
7131
7732
|
// src/extract/databases/dotenv.ts
|
|
7132
|
-
import { promises as
|
|
7133
|
-
import
|
|
7733
|
+
import { promises as fs18 } from "fs";
|
|
7734
|
+
import path25 from "path";
|
|
7134
7735
|
|
|
7135
7736
|
// src/extract/databases/shared.ts
|
|
7136
|
-
import { promises as
|
|
7137
|
-
import
|
|
7737
|
+
import { promises as fs17 } from "fs";
|
|
7738
|
+
import path24 from "path";
|
|
7138
7739
|
function schemeToEngine(scheme) {
|
|
7139
7740
|
const s = scheme.toLowerCase().split("+")[0];
|
|
7140
7741
|
switch (s) {
|
|
@@ -7173,18 +7774,18 @@ function parseConnectionString(url) {
|
|
|
7173
7774
|
}
|
|
7174
7775
|
async function readIfExists(filePath) {
|
|
7175
7776
|
try {
|
|
7176
|
-
return await
|
|
7777
|
+
return await fs17.readFile(filePath, "utf8");
|
|
7177
7778
|
} catch {
|
|
7178
7779
|
return null;
|
|
7179
7780
|
}
|
|
7180
7781
|
}
|
|
7181
7782
|
async function resolveEnvVar(serviceDir, name) {
|
|
7182
|
-
const entries = await
|
|
7783
|
+
const entries = await fs17.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
7183
7784
|
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
7184
7785
|
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
7185
7786
|
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
7186
7787
|
for (const fileName of envNames) {
|
|
7187
|
-
const content = await readIfExists(
|
|
7788
|
+
const content = await readIfExists(path24.join(serviceDir, fileName));
|
|
7188
7789
|
if (!content) continue;
|
|
7189
7790
|
for (const line of content.split("\n")) {
|
|
7190
7791
|
const trimmed = line.trim();
|
|
@@ -7203,7 +7804,7 @@ async function resolveEnvVar(serviceDir, name) {
|
|
|
7203
7804
|
}
|
|
7204
7805
|
async function findFirst(serviceDir, candidates) {
|
|
7205
7806
|
for (const rel of candidates) {
|
|
7206
|
-
const abs =
|
|
7807
|
+
const abs = path24.join(serviceDir, rel);
|
|
7207
7808
|
const content = await readIfExists(abs);
|
|
7208
7809
|
if (content !== null) return abs;
|
|
7209
7810
|
}
|
|
@@ -7261,15 +7862,15 @@ function parseDotenvLine(line) {
|
|
|
7261
7862
|
return { key, value };
|
|
7262
7863
|
}
|
|
7263
7864
|
async function parse2(serviceDir) {
|
|
7264
|
-
const entries = await
|
|
7865
|
+
const entries = await fs18.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
7265
7866
|
const configs = [];
|
|
7266
7867
|
const seen = /* @__PURE__ */ new Set();
|
|
7267
7868
|
for (const entry of entries) {
|
|
7268
7869
|
if (!entry.isFile()) continue;
|
|
7269
7870
|
const match = isConfigFile(entry.name);
|
|
7270
7871
|
if (!match.match || match.fileType !== "env") continue;
|
|
7271
|
-
const filePath =
|
|
7272
|
-
const content = await
|
|
7872
|
+
const filePath = path25.join(serviceDir, entry.name);
|
|
7873
|
+
const content = await fs18.readFile(filePath, "utf8");
|
|
7273
7874
|
for (const line of content.split("\n")) {
|
|
7274
7875
|
const parsed = parseDotenvLine(line);
|
|
7275
7876
|
if (!parsed) continue;
|
|
@@ -7287,9 +7888,9 @@ async function parse2(serviceDir) {
|
|
|
7287
7888
|
var dotenvParser = { name: ".env", parse: parse2 };
|
|
7288
7889
|
|
|
7289
7890
|
// src/extract/databases/prisma.ts
|
|
7290
|
-
import
|
|
7891
|
+
import path26 from "path";
|
|
7291
7892
|
async function parse3(serviceDir) {
|
|
7292
|
-
const schemaPath =
|
|
7893
|
+
const schemaPath = path26.join(serviceDir, "prisma", "schema.prisma");
|
|
7293
7894
|
const content = await readIfExists(schemaPath);
|
|
7294
7895
|
if (!content) return [];
|
|
7295
7896
|
const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
|
|
@@ -7444,10 +8045,10 @@ async function parse5(serviceDir) {
|
|
|
7444
8045
|
var knexParser = { name: "knex", parse: parse5 };
|
|
7445
8046
|
|
|
7446
8047
|
// src/extract/databases/ormconfig.ts
|
|
7447
|
-
import
|
|
8048
|
+
import path27 from "path";
|
|
7448
8049
|
async function parse6(serviceDir) {
|
|
7449
8050
|
for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
|
|
7450
|
-
const abs =
|
|
8051
|
+
const abs = path27.join(serviceDir, candidate);
|
|
7451
8052
|
if (!await exists(abs)) continue;
|
|
7452
8053
|
const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
|
|
7453
8054
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
@@ -7505,9 +8106,9 @@ async function parse7(serviceDir) {
|
|
|
7505
8106
|
var typeormParser = { name: "typeorm", parse: parse7 };
|
|
7506
8107
|
|
|
7507
8108
|
// src/extract/databases/sequelize.ts
|
|
7508
|
-
import
|
|
8109
|
+
import path28 from "path";
|
|
7509
8110
|
async function parse8(serviceDir) {
|
|
7510
|
-
const configPath =
|
|
8111
|
+
const configPath = path28.join(serviceDir, "config", "config.json");
|
|
7511
8112
|
if (!await exists(configPath)) return [];
|
|
7512
8113
|
const raw = await readJson(configPath);
|
|
7513
8114
|
const out = [];
|
|
@@ -7533,7 +8134,7 @@ async function parse8(serviceDir) {
|
|
|
7533
8134
|
var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
7534
8135
|
|
|
7535
8136
|
// src/extract/databases/docker-compose.ts
|
|
7536
|
-
import
|
|
8137
|
+
import path29 from "path";
|
|
7537
8138
|
function portFromService(svc) {
|
|
7538
8139
|
for (const raw of svc.ports ?? []) {
|
|
7539
8140
|
const str = String(raw);
|
|
@@ -7560,7 +8161,7 @@ function databaseFromEnv(svc) {
|
|
|
7560
8161
|
}
|
|
7561
8162
|
async function parse9(serviceDir) {
|
|
7562
8163
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
7563
|
-
const abs =
|
|
8164
|
+
const abs = path29.join(serviceDir, name);
|
|
7564
8165
|
if (!await exists(abs)) continue;
|
|
7565
8166
|
const raw = await readYaml(abs);
|
|
7566
8167
|
if (!raw?.services) return [];
|
|
@@ -7602,7 +8203,7 @@ function compatibleDriversFor(engine) {
|
|
|
7602
8203
|
function toDatabaseNode(config) {
|
|
7603
8204
|
return {
|
|
7604
8205
|
id: databaseId2(config.host),
|
|
7605
|
-
type:
|
|
8206
|
+
type: NodeType17.DatabaseNode,
|
|
7606
8207
|
name: config.database || config.host,
|
|
7607
8208
|
engine: config.engine,
|
|
7608
8209
|
engineVersion: config.engineVersion,
|
|
@@ -7732,7 +8333,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7732
8333
|
discoveredVia: mergedDiscoveredVia
|
|
7733
8334
|
});
|
|
7734
8335
|
}
|
|
7735
|
-
const relConfigFile = toPosix(
|
|
8336
|
+
const relConfigFile = toPosix(path30.relative(service.dir, config.sourceFile));
|
|
7736
8337
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7737
8338
|
graph,
|
|
7738
8339
|
service.pkg.name,
|
|
@@ -7741,7 +8342,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7741
8342
|
);
|
|
7742
8343
|
nodesAdded += fn;
|
|
7743
8344
|
edgesAdded += fe;
|
|
7744
|
-
const evidenceFile = toPosix(
|
|
8345
|
+
const evidenceFile = toPosix(path30.relative(scanPath, config.sourceFile));
|
|
7745
8346
|
const edge = {
|
|
7746
8347
|
id: extractedEdgeId(fileNodeId, dbNode.id, EdgeType10.CONNECTS_TO),
|
|
7747
8348
|
source: fileNodeId,
|
|
@@ -7759,15 +8360,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7759
8360
|
if (allConfigs.length === 1) {
|
|
7760
8361
|
const primary = allConfigs[0];
|
|
7761
8362
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
7762
|
-
const relPath =
|
|
8363
|
+
const relPath = path30.relative(scanPath, primary.sourceFile);
|
|
7763
8364
|
const cfgId = configId(relPath);
|
|
7764
8365
|
if (!graph.hasNode(cfgId)) {
|
|
7765
8366
|
const cfgNode = {
|
|
7766
8367
|
id: cfgId,
|
|
7767
|
-
type:
|
|
7768
|
-
name:
|
|
8368
|
+
type: NodeType17.ConfigNode,
|
|
8369
|
+
name: path30.basename(primary.sourceFile),
|
|
7769
8370
|
path: relPath,
|
|
7770
|
-
fileType: isConfigFile(
|
|
8371
|
+
fileType: isConfigFile(path30.basename(primary.sourceFile)).fileType || "config"
|
|
7771
8372
|
};
|
|
7772
8373
|
graph.addNode(cfgId, cfgNode);
|
|
7773
8374
|
nodesAdded++;
|
|
@@ -7807,11 +8408,11 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7807
8408
|
}
|
|
7808
8409
|
|
|
7809
8410
|
// src/extract/configs.ts
|
|
7810
|
-
import { promises as
|
|
7811
|
-
import
|
|
8411
|
+
import { promises as fs19 } from "fs";
|
|
8412
|
+
import path31 from "path";
|
|
7812
8413
|
import {
|
|
7813
8414
|
EdgeType as EdgeType11,
|
|
7814
|
-
NodeType as
|
|
8415
|
+
NodeType as NodeType18,
|
|
7815
8416
|
Provenance as Provenance11,
|
|
7816
8417
|
configId as configId2,
|
|
7817
8418
|
confidenceForExtracted as confidenceForExtracted8
|
|
@@ -7819,9 +8420,9 @@ import {
|
|
|
7819
8420
|
async function walkConfigFiles(dir) {
|
|
7820
8421
|
const out = [];
|
|
7821
8422
|
async function walk9(current) {
|
|
7822
|
-
const entries = await
|
|
8423
|
+
const entries = await fs19.readdir(current, { withFileTypes: true });
|
|
7823
8424
|
for (const entry of entries) {
|
|
7824
|
-
const full =
|
|
8425
|
+
const full = path31.join(current, entry.name);
|
|
7825
8426
|
if (entry.isDirectory()) {
|
|
7826
8427
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7827
8428
|
if (await isPythonVenvDir(full)) continue;
|
|
@@ -7840,19 +8441,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
7840
8441
|
for (const service of services) {
|
|
7841
8442
|
const configFiles = await walkConfigFiles(service.dir);
|
|
7842
8443
|
for (const file of configFiles) {
|
|
7843
|
-
const relPath =
|
|
8444
|
+
const relPath = path31.relative(scanPath, file);
|
|
7844
8445
|
const node = {
|
|
7845
8446
|
id: configId2(relPath),
|
|
7846
|
-
type:
|
|
7847
|
-
name:
|
|
8447
|
+
type: NodeType18.ConfigNode,
|
|
8448
|
+
name: path31.basename(file),
|
|
7848
8449
|
path: relPath,
|
|
7849
|
-
fileType: isConfigFile(
|
|
8450
|
+
fileType: isConfigFile(path31.basename(file)).fileType
|
|
7850
8451
|
};
|
|
7851
8452
|
if (!graph.hasNode(node.id)) {
|
|
7852
8453
|
graph.addNode(node.id, node);
|
|
7853
8454
|
nodesAdded++;
|
|
7854
8455
|
}
|
|
7855
|
-
const relToService = toPosix(
|
|
8456
|
+
const relToService = toPosix(path31.relative(service.dir, file));
|
|
7856
8457
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7857
8458
|
graph,
|
|
7858
8459
|
service.pkg.name,
|
|
@@ -7868,7 +8469,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
7868
8469
|
type: EdgeType11.CONFIGURED_BY,
|
|
7869
8470
|
provenance: Provenance11.EXTRACTED,
|
|
7870
8471
|
confidence: confidenceForExtracted8("structural"),
|
|
7871
|
-
evidence: { file: relPath.split(
|
|
8472
|
+
evidence: { file: relPath.split(path31.sep).join("/") }
|
|
7872
8473
|
};
|
|
7873
8474
|
if (!graph.hasEdge(edge.id)) {
|
|
7874
8475
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -7880,11 +8481,11 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
7880
8481
|
}
|
|
7881
8482
|
|
|
7882
8483
|
// src/extract/proto.ts
|
|
7883
|
-
import { promises as
|
|
7884
|
-
import
|
|
8484
|
+
import { promises as fs20 } from "fs";
|
|
8485
|
+
import path32 from "path";
|
|
7885
8486
|
import {
|
|
7886
8487
|
EdgeType as EdgeType12,
|
|
7887
|
-
NodeType as
|
|
8488
|
+
NodeType as NodeType19,
|
|
7888
8489
|
Provenance as Provenance12,
|
|
7889
8490
|
confidenceForExtracted as confidenceForExtracted9,
|
|
7890
8491
|
extractedEdgeId as extractedEdgeId9,
|
|
@@ -7928,14 +8529,14 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
7928
8529
|
async function walkProtoFiles(dir) {
|
|
7929
8530
|
const out = [];
|
|
7930
8531
|
async function walk9(current) {
|
|
7931
|
-
const entries = await
|
|
8532
|
+
const entries = await fs20.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
7932
8533
|
for (const entry of entries) {
|
|
7933
|
-
const full =
|
|
8534
|
+
const full = path32.join(current, entry.name);
|
|
7934
8535
|
if (entry.isDirectory()) {
|
|
7935
8536
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7936
8537
|
if (await isPythonVenvDir(full)) continue;
|
|
7937
8538
|
await walk9(full);
|
|
7938
|
-
} else if (entry.isFile() &&
|
|
8539
|
+
} else if (entry.isFile() && path32.extname(entry.name) === PROTO_EXTENSION) {
|
|
7939
8540
|
out.push(full);
|
|
7940
8541
|
}
|
|
7941
8542
|
}
|
|
@@ -7950,10 +8551,10 @@ async function addGrpcMethods(graph, services) {
|
|
|
7950
8551
|
const protoPaths = await walkProtoFiles(service.dir);
|
|
7951
8552
|
for (const protoPath of protoPaths) {
|
|
7952
8553
|
if (isTestPath(protoPath)) continue;
|
|
7953
|
-
const relFile = toPosix(
|
|
8554
|
+
const relFile = toPosix(path32.relative(service.dir, protoPath));
|
|
7954
8555
|
let content;
|
|
7955
8556
|
try {
|
|
7956
|
-
content = await
|
|
8557
|
+
content = await fs20.readFile(protoPath, "utf8");
|
|
7957
8558
|
} catch (err) {
|
|
7958
8559
|
recordExtractionError("proto extraction", protoPath, err);
|
|
7959
8560
|
continue;
|
|
@@ -7971,7 +8572,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
7971
8572
|
if (!graph.hasNode(mid)) {
|
|
7972
8573
|
const node = {
|
|
7973
8574
|
id: mid,
|
|
7974
|
-
type:
|
|
8575
|
+
type: NodeType19.GrpcMethodNode,
|
|
7975
8576
|
name: `${method.rpcService}/${method.rpcMethod}`,
|
|
7976
8577
|
rpcService: method.rpcService,
|
|
7977
8578
|
rpcMethod: method.rpcMethod,
|
|
@@ -8009,18 +8610,18 @@ async function addGrpcMethods(graph, services) {
|
|
|
8009
8610
|
// src/extract/calls/index.ts
|
|
8010
8611
|
import {
|
|
8011
8612
|
EdgeType as EdgeType15,
|
|
8012
|
-
NodeType as
|
|
8613
|
+
NodeType as NodeType21,
|
|
8013
8614
|
Provenance as Provenance15,
|
|
8014
8615
|
confidenceForExtracted as confidenceForExtracted12,
|
|
8015
8616
|
passesExtractedFloor as passesExtractedFloor3
|
|
8016
8617
|
} from "@neat.is/types";
|
|
8017
8618
|
|
|
8018
8619
|
// src/extract/calls/http.ts
|
|
8019
|
-
import
|
|
8620
|
+
import path33 from "path";
|
|
8020
8621
|
import Parser6 from "tree-sitter";
|
|
8021
8622
|
import JavaScript4 from "tree-sitter-javascript";
|
|
8022
8623
|
import TypeScript2 from "tree-sitter-typescript";
|
|
8023
|
-
import
|
|
8624
|
+
import Python4 from "tree-sitter-python";
|
|
8024
8625
|
import {
|
|
8025
8626
|
EdgeType as EdgeType13,
|
|
8026
8627
|
Provenance as Provenance13,
|
|
@@ -8082,7 +8683,7 @@ var GRAMMAR_BY_EXT2 = {
|
|
|
8082
8683
|
".jsx": JavaScript4,
|
|
8083
8684
|
".mjs": JavaScript4,
|
|
8084
8685
|
".cjs": JavaScript4,
|
|
8085
|
-
".py":
|
|
8686
|
+
".py": Python4
|
|
8086
8687
|
};
|
|
8087
8688
|
function parserForExt(ext, cache) {
|
|
8088
8689
|
const grammar = GRAMMAR_BY_EXT2[ext] ?? JavaScript4;
|
|
@@ -8104,7 +8705,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
8104
8705
|
const seen = /* @__PURE__ */ new Set();
|
|
8105
8706
|
for (const file of files) {
|
|
8106
8707
|
if (isTestPath(file.path)) continue;
|
|
8107
|
-
const parser = parserForExt(
|
|
8708
|
+
const parser = parserForExt(path33.extname(file.path), parserCache);
|
|
8108
8709
|
let sites;
|
|
8109
8710
|
try {
|
|
8110
8711
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -8113,7 +8714,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
8113
8714
|
continue;
|
|
8114
8715
|
}
|
|
8115
8716
|
if (sites.length === 0) continue;
|
|
8116
|
-
const relFile = toPosix(
|
|
8717
|
+
const relFile = toPosix(path33.relative(service.dir, file.path));
|
|
8117
8718
|
for (const site of sites) {
|
|
8118
8719
|
const targetId = hostToNodeId.get(site.host);
|
|
8119
8720
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -8166,16 +8767,16 @@ async function addHttpCallEdges(graph, services) {
|
|
|
8166
8767
|
}
|
|
8167
8768
|
|
|
8168
8769
|
// src/extract/calls/route-match.ts
|
|
8169
|
-
import
|
|
8770
|
+
import path34 from "path";
|
|
8170
8771
|
import Parser7 from "tree-sitter";
|
|
8171
8772
|
import JavaScript5 from "tree-sitter-javascript";
|
|
8172
8773
|
import {
|
|
8173
8774
|
EdgeType as EdgeType14,
|
|
8174
|
-
NodeType as
|
|
8775
|
+
NodeType as NodeType20,
|
|
8175
8776
|
Provenance as Provenance14,
|
|
8176
8777
|
confidenceForExtracted as confidenceForExtracted11,
|
|
8177
8778
|
passesExtractedFloor as passesExtractedFloor2,
|
|
8178
|
-
serviceId as
|
|
8779
|
+
serviceId as serviceId9
|
|
8179
8780
|
} from "@neat.is/types";
|
|
8180
8781
|
var PARSE_CHUNK5 = 16384;
|
|
8181
8782
|
function parseSource5(parser, source) {
|
|
@@ -8341,9 +8942,9 @@ function buildRouteIndex(graph) {
|
|
|
8341
8942
|
const index = /* @__PURE__ */ new Map();
|
|
8342
8943
|
graph.forEachNode((_id, attrs) => {
|
|
8343
8944
|
const node = attrs;
|
|
8344
|
-
if (node.type !==
|
|
8945
|
+
if (node.type !== NodeType20.RouteNode) return;
|
|
8345
8946
|
const route = attrs;
|
|
8346
|
-
const owner =
|
|
8947
|
+
const owner = serviceId9(route.service);
|
|
8347
8948
|
const entry = {
|
|
8348
8949
|
method: route.method.toUpperCase(),
|
|
8349
8950
|
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
@@ -8372,7 +8973,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
8372
8973
|
const seen = /* @__PURE__ */ new Set();
|
|
8373
8974
|
for (const file of files) {
|
|
8374
8975
|
if (isTestPath(file.path)) continue;
|
|
8375
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
8976
|
+
if (!JS_CLIENT_EXTENSIONS.has(path34.extname(file.path))) continue;
|
|
8376
8977
|
let sites;
|
|
8377
8978
|
try {
|
|
8378
8979
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -8381,7 +8982,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
8381
8982
|
continue;
|
|
8382
8983
|
}
|
|
8383
8984
|
if (sites.length === 0) continue;
|
|
8384
|
-
const relFile = toPosix(
|
|
8985
|
+
const relFile = toPosix(path34.relative(service.dir, file.path));
|
|
8385
8986
|
for (const site of sites) {
|
|
8386
8987
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
8387
8988
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -8441,7 +9042,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
8441
9042
|
}
|
|
8442
9043
|
|
|
8443
9044
|
// src/extract/calls/kafka.ts
|
|
8444
|
-
import
|
|
9045
|
+
import path35 from "path";
|
|
8445
9046
|
import { infraId as infraId2 } from "@neat.is/types";
|
|
8446
9047
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
8447
9048
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -8472,7 +9073,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
8472
9073
|
// tier (ADR-066).
|
|
8473
9074
|
confidenceKind: "verified-call-site",
|
|
8474
9075
|
evidence: {
|
|
8475
|
-
file:
|
|
9076
|
+
file: path35.relative(serviceDir, file.path),
|
|
8476
9077
|
line,
|
|
8477
9078
|
snippet: snippet(file.content, line)
|
|
8478
9079
|
}
|
|
@@ -8484,7 +9085,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
8484
9085
|
}
|
|
8485
9086
|
|
|
8486
9087
|
// src/extract/calls/redis.ts
|
|
8487
|
-
import
|
|
9088
|
+
import path36 from "path";
|
|
8488
9089
|
import { infraId as infraId3 } from "@neat.is/types";
|
|
8489
9090
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
8490
9091
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -8507,7 +9108,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
8507
9108
|
// support tier (ADR-066).
|
|
8508
9109
|
confidenceKind: "url-with-structural-support",
|
|
8509
9110
|
evidence: {
|
|
8510
|
-
file:
|
|
9111
|
+
file: path36.relative(serviceDir, file.path),
|
|
8511
9112
|
line,
|
|
8512
9113
|
snippet: snippet(file.content, line)
|
|
8513
9114
|
}
|
|
@@ -8517,7 +9118,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
8517
9118
|
}
|
|
8518
9119
|
|
|
8519
9120
|
// src/extract/calls/aws.ts
|
|
8520
|
-
import
|
|
9121
|
+
import path37 from "path";
|
|
8521
9122
|
import { infraId as infraId4 } from "@neat.is/types";
|
|
8522
9123
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
8523
9124
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -8551,7 +9152,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
8551
9152
|
// (ADR-066).
|
|
8552
9153
|
confidenceKind: "verified-call-site",
|
|
8553
9154
|
evidence: {
|
|
8554
|
-
file:
|
|
9155
|
+
file: path37.relative(serviceDir, file.path),
|
|
8555
9156
|
line,
|
|
8556
9157
|
snippet: snippet(file.content, line)
|
|
8557
9158
|
}
|
|
@@ -8575,7 +9176,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
8575
9176
|
}
|
|
8576
9177
|
|
|
8577
9178
|
// src/extract/calls/grpc.ts
|
|
8578
|
-
import
|
|
9179
|
+
import path38 from "path";
|
|
8579
9180
|
import { infraId as infraId5 } from "@neat.is/types";
|
|
8580
9181
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
8581
9182
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -8634,7 +9235,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
8634
9235
|
// tier (ADR-066).
|
|
8635
9236
|
confidenceKind: "verified-call-site",
|
|
8636
9237
|
evidence: {
|
|
8637
|
-
file:
|
|
9238
|
+
file: path38.relative(serviceDir, file.path),
|
|
8638
9239
|
line,
|
|
8639
9240
|
snippet: snippet(file.content, line)
|
|
8640
9241
|
}
|
|
@@ -8644,7 +9245,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
8644
9245
|
}
|
|
8645
9246
|
|
|
8646
9247
|
// src/extract/calls/supabase.ts
|
|
8647
|
-
import
|
|
9248
|
+
import path39 from "path";
|
|
8648
9249
|
import { infraId as infraId6 } from "@neat.is/types";
|
|
8649
9250
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
8650
9251
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -8703,7 +9304,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
8703
9304
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
8704
9305
|
confidenceKind: "verified-call-site",
|
|
8705
9306
|
evidence: {
|
|
8706
|
-
file:
|
|
9307
|
+
file: path39.relative(serviceDir, file.path),
|
|
8707
9308
|
line,
|
|
8708
9309
|
snippet: snippet(file.content, line)
|
|
8709
9310
|
}
|
|
@@ -8730,7 +9331,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
8730
9331
|
edgeType: "CALLS",
|
|
8731
9332
|
confidenceKind: "verified-call-site",
|
|
8732
9333
|
evidence: {
|
|
8733
|
-
file:
|
|
9334
|
+
file: path39.relative(serviceDir, file.path),
|
|
8734
9335
|
line,
|
|
8735
9336
|
snippet: snippet(file.content, line)
|
|
8736
9337
|
}
|
|
@@ -8741,7 +9342,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
8741
9342
|
}
|
|
8742
9343
|
|
|
8743
9344
|
// src/extract/calls/firestore.ts
|
|
8744
|
-
import
|
|
9345
|
+
import path40 from "path";
|
|
8745
9346
|
import Parser8 from "tree-sitter";
|
|
8746
9347
|
import JavaScript6 from "tree-sitter-javascript";
|
|
8747
9348
|
import { infraId as infraId7 } from "@neat.is/types";
|
|
@@ -8912,7 +9513,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
8912
9513
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
8913
9514
|
if (!hasClient && !hasAdmin) return [];
|
|
8914
9515
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
8915
|
-
const tree = parseSource3(parserForExt2(
|
|
9516
|
+
const tree = parseSource3(parserForExt2(path40.extname(file.path)), file.content);
|
|
8916
9517
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
8917
9518
|
const collLine = /* @__PURE__ */ new Map();
|
|
8918
9519
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -9016,7 +9617,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9016
9617
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
9017
9618
|
...sdkWrites ? { sdkWrites } : {},
|
|
9018
9619
|
evidence: {
|
|
9019
|
-
file:
|
|
9620
|
+
file: path40.relative(serviceDir, file.path),
|
|
9020
9621
|
line,
|
|
9021
9622
|
snippet: snippet(file.content, line)
|
|
9022
9623
|
}
|
|
@@ -9026,7 +9627,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9026
9627
|
}
|
|
9027
9628
|
|
|
9028
9629
|
// src/extract/calls/mongoose.ts
|
|
9029
|
-
import
|
|
9630
|
+
import path41 from "path";
|
|
9030
9631
|
import { infraId as infraId8 } from "@neat.is/types";
|
|
9031
9632
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
9032
9633
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -9177,7 +9778,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
9177
9778
|
kind: r.kind,
|
|
9178
9779
|
edgeType: "CALLS",
|
|
9179
9780
|
confidenceKind: "verified-call-site",
|
|
9180
|
-
evidence: { file:
|
|
9781
|
+
evidence: { file: path41.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
9181
9782
|
};
|
|
9182
9783
|
}
|
|
9183
9784
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -9267,7 +9868,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
9267
9868
|
const registry = /* @__PURE__ */ new Map();
|
|
9268
9869
|
for (const f of mongooseFiles) {
|
|
9269
9870
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
9270
|
-
if (fx) registry.set(toPosix(
|
|
9871
|
+
if (fx) registry.set(toPosix(path41.relative(serviceDir, f.path)), fx);
|
|
9271
9872
|
}
|
|
9272
9873
|
if (registry.size === 0) return [];
|
|
9273
9874
|
const out = [];
|
|
@@ -9278,7 +9879,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
9278
9879
|
const directColl = /* @__PURE__ */ new Map();
|
|
9279
9880
|
const nsExports = /* @__PURE__ */ new Map();
|
|
9280
9881
|
for (const b of bindings) {
|
|
9281
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
9882
|
+
const resolvedRel = await resolveJsImport(b.specifier, path41.dirname(f.path), serviceDir, null);
|
|
9282
9883
|
if (!resolvedRel) continue;
|
|
9283
9884
|
const fx = registry.get(resolvedRel);
|
|
9284
9885
|
if (!fx) continue;
|
|
@@ -9321,15 +9922,15 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
9321
9922
|
}
|
|
9322
9923
|
|
|
9323
9924
|
// src/extract/calls/sqlalchemy.ts
|
|
9324
|
-
import
|
|
9925
|
+
import path42 from "path";
|
|
9325
9926
|
import Parser9 from "tree-sitter";
|
|
9326
|
-
import
|
|
9927
|
+
import Python5 from "tree-sitter-python";
|
|
9327
9928
|
import { infraId as infraId9 } from "@neat.is/types";
|
|
9328
9929
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
9329
9930
|
var PARSE_CHUNK6 = 16384;
|
|
9330
9931
|
function makePyParser3() {
|
|
9331
9932
|
const p = new Parser9();
|
|
9332
|
-
p.setLanguage(
|
|
9933
|
+
p.setLanguage(Python5);
|
|
9333
9934
|
return p;
|
|
9334
9935
|
}
|
|
9335
9936
|
function parseSource6(parser, source) {
|
|
@@ -9460,7 +10061,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
9460
10061
|
childTable,
|
|
9461
10062
|
parentTable,
|
|
9462
10063
|
evidence: {
|
|
9463
|
-
file:
|
|
10064
|
+
file: path42.relative(serviceDir, file.path),
|
|
9464
10065
|
line,
|
|
9465
10066
|
snippet: snippet(file.content, line)
|
|
9466
10067
|
}
|
|
@@ -9485,7 +10086,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
9485
10086
|
confidenceKind: "verified-call-site",
|
|
9486
10087
|
...columns && columns.length > 0 ? { columns } : {},
|
|
9487
10088
|
evidence: {
|
|
9488
|
-
file:
|
|
10089
|
+
file: path42.relative(serviceDir, file.path),
|
|
9489
10090
|
line,
|
|
9490
10091
|
snippet: snippet(file.content, line)
|
|
9491
10092
|
}
|
|
@@ -9592,7 +10193,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
9592
10193
|
edgeType: "CALLS",
|
|
9593
10194
|
confidenceKind: "verified-call-site",
|
|
9594
10195
|
evidence: {
|
|
9595
|
-
file:
|
|
10196
|
+
file: path42.relative(serviceDir, file.path),
|
|
9596
10197
|
line,
|
|
9597
10198
|
snippet: snippet(file.content, line)
|
|
9598
10199
|
}
|
|
@@ -9603,15 +10204,15 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
9603
10204
|
}
|
|
9604
10205
|
|
|
9605
10206
|
// src/extract/calls/django-orm.ts
|
|
9606
|
-
import
|
|
10207
|
+
import path43 from "path";
|
|
9607
10208
|
import Parser10 from "tree-sitter";
|
|
9608
|
-
import
|
|
10209
|
+
import Python6 from "tree-sitter-python";
|
|
9609
10210
|
import { infraId as infraId10 } from "@neat.is/types";
|
|
9610
10211
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
9611
10212
|
var PARSE_CHUNK7 = 16384;
|
|
9612
10213
|
function makePyParser4() {
|
|
9613
10214
|
const p = new Parser10();
|
|
9614
|
-
p.setLanguage(
|
|
10215
|
+
p.setLanguage(Python6);
|
|
9615
10216
|
return p;
|
|
9616
10217
|
}
|
|
9617
10218
|
function parseSource7(parser, source) {
|
|
@@ -9673,7 +10274,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
9673
10274
|
const tree = parseSource7(makePyParser4(), file.content);
|
|
9674
10275
|
const out = [];
|
|
9675
10276
|
const seen = /* @__PURE__ */ new Set();
|
|
9676
|
-
const defaultAppLabel =
|
|
10277
|
+
const defaultAppLabel = path43.basename(path43.dirname(file.path));
|
|
9677
10278
|
walk4(tree.rootNode, (node) => {
|
|
9678
10279
|
if (node.type !== "class_definition") return;
|
|
9679
10280
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -9691,14 +10292,14 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
9691
10292
|
kind: "sql-table",
|
|
9692
10293
|
edgeType: "CALLS",
|
|
9693
10294
|
confidenceKind: "verified-call-site",
|
|
9694
|
-
evidence: { file:
|
|
10295
|
+
evidence: { file: path43.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
9695
10296
|
});
|
|
9696
10297
|
});
|
|
9697
10298
|
return out;
|
|
9698
10299
|
}
|
|
9699
10300
|
|
|
9700
10301
|
// src/extract/calls/drizzle.ts
|
|
9701
|
-
import
|
|
10302
|
+
import path44 from "path";
|
|
9702
10303
|
import Parser11 from "tree-sitter";
|
|
9703
10304
|
import JavaScript7 from "tree-sitter-javascript";
|
|
9704
10305
|
import { infraId as infraId11 } from "@neat.is/types";
|
|
@@ -9776,7 +10377,7 @@ function columnsFromObject(obj) {
|
|
|
9776
10377
|
}
|
|
9777
10378
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
9778
10379
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9779
|
-
const tree = parseSource3(parserForExt3(
|
|
10380
|
+
const tree = parseSource3(parserForExt3(path44.extname(file.path)), file.content);
|
|
9780
10381
|
const out = [];
|
|
9781
10382
|
const seen = /* @__PURE__ */ new Set();
|
|
9782
10383
|
const walk9 = (node) => {
|
|
@@ -9799,7 +10400,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
9799
10400
|
confidenceKind: "structural",
|
|
9800
10401
|
columns,
|
|
9801
10402
|
evidence: {
|
|
9802
|
-
file:
|
|
10403
|
+
file: path44.relative(serviceDir, file.path),
|
|
9803
10404
|
line,
|
|
9804
10405
|
snippet: snippet(file.content, line)
|
|
9805
10406
|
}
|
|
@@ -9865,7 +10466,7 @@ function referencesTargetVar(call) {
|
|
|
9865
10466
|
}
|
|
9866
10467
|
function drizzleForeignKeys(file, serviceDir) {
|
|
9867
10468
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9868
|
-
const tree = parseSource3(parserForExt3(
|
|
10469
|
+
const tree = parseSource3(parserForExt3(path44.extname(file.path)), file.content);
|
|
9869
10470
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
9870
10471
|
const out = [];
|
|
9871
10472
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9884,7 +10485,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
9884
10485
|
childTable: table.tableName,
|
|
9885
10486
|
parentTable,
|
|
9886
10487
|
evidence: {
|
|
9887
|
-
file:
|
|
10488
|
+
file: path44.relative(serviceDir, file.path),
|
|
9888
10489
|
line,
|
|
9889
10490
|
snippet: snippet(file.content, line)
|
|
9890
10491
|
}
|
|
@@ -9900,7 +10501,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
9900
10501
|
}
|
|
9901
10502
|
|
|
9902
10503
|
// src/extract/calls/prisma.ts
|
|
9903
|
-
import
|
|
10504
|
+
import path45 from "path";
|
|
9904
10505
|
import { infraId as infraId12 } from "@neat.is/types";
|
|
9905
10506
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
9906
10507
|
"Int",
|
|
@@ -9956,7 +10557,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
9956
10557
|
confidenceKind: "structural",
|
|
9957
10558
|
columns: b.columns,
|
|
9958
10559
|
evidence: {
|
|
9959
|
-
file:
|
|
10560
|
+
file: path45.relative(serviceDir, file.path),
|
|
9960
10561
|
line: b.startLine,
|
|
9961
10562
|
snippet: snippet(content, b.startLine)
|
|
9962
10563
|
}
|
|
@@ -10017,7 +10618,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
10017
10618
|
}
|
|
10018
10619
|
async function prismaColumnEndpoints(serviceDir) {
|
|
10019
10620
|
const schemaPath = await findFirst(serviceDir, [
|
|
10020
|
-
|
|
10621
|
+
path45.join("prisma", "schema.prisma"),
|
|
10021
10622
|
"schema.prisma"
|
|
10022
10623
|
]);
|
|
10023
10624
|
if (!schemaPath) return [];
|
|
@@ -10092,7 +10693,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
10092
10693
|
childTable: current.table,
|
|
10093
10694
|
parentTable,
|
|
10094
10695
|
evidence: {
|
|
10095
|
-
file:
|
|
10696
|
+
file: path45.relative(serviceDir, file.path),
|
|
10096
10697
|
line: lineNo,
|
|
10097
10698
|
snippet: snippet(content, lineNo)
|
|
10098
10699
|
}
|
|
@@ -10107,7 +10708,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
10107
10708
|
}
|
|
10108
10709
|
async function prismaForeignKeys(serviceDir) {
|
|
10109
10710
|
const schemaPath = await findFirst(serviceDir, [
|
|
10110
|
-
|
|
10711
|
+
path45.join("prisma", "schema.prisma"),
|
|
10111
10712
|
"schema.prisma"
|
|
10112
10713
|
]);
|
|
10113
10714
|
if (!schemaPath) return [];
|
|
@@ -10117,15 +10718,15 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
10117
10718
|
}
|
|
10118
10719
|
|
|
10119
10720
|
// src/extract/calls/activerecord.ts
|
|
10120
|
-
import
|
|
10721
|
+
import path46 from "path";
|
|
10121
10722
|
import Parser12 from "tree-sitter";
|
|
10122
|
-
import
|
|
10723
|
+
import Ruby3 from "tree-sitter-ruby";
|
|
10123
10724
|
import { infraId as infraId13 } from "@neat.is/types";
|
|
10124
10725
|
var AR_SCHEMA_RE = /ActiveRecord::Schema/;
|
|
10125
10726
|
var PARSE_CHUNK8 = 16384;
|
|
10126
10727
|
function makeRubyParser2() {
|
|
10127
10728
|
const p = new Parser12();
|
|
10128
|
-
p.setLanguage(
|
|
10729
|
+
p.setLanguage(Ruby3);
|
|
10129
10730
|
return p;
|
|
10130
10731
|
}
|
|
10131
10732
|
function parseSource8(parser, source) {
|
|
@@ -10325,7 +10926,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
10325
10926
|
confidenceKind: "structural",
|
|
10326
10927
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
10327
10928
|
evidence: {
|
|
10328
|
-
file:
|
|
10929
|
+
file: path46.relative(serviceDir, file.path),
|
|
10329
10930
|
line: table.line,
|
|
10330
10931
|
snippet: snippet(file.content, table.line)
|
|
10331
10932
|
}
|
|
@@ -10347,7 +10948,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
10347
10948
|
childTable,
|
|
10348
10949
|
parentTable,
|
|
10349
10950
|
evidence: {
|
|
10350
|
-
file:
|
|
10951
|
+
file: path46.relative(serviceDir, file.path),
|
|
10351
10952
|
line,
|
|
10352
10953
|
snippet: snippet(file.content, line)
|
|
10353
10954
|
}
|
|
@@ -10430,7 +11031,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
10430
11031
|
edgeType: "CALLS",
|
|
10431
11032
|
confidenceKind: "verified-call-site",
|
|
10432
11033
|
evidence: {
|
|
10433
|
-
file:
|
|
11034
|
+
file: path46.relative(serviceDir, file.path),
|
|
10434
11035
|
line,
|
|
10435
11036
|
snippet: snippet(file.content, line)
|
|
10436
11037
|
}
|
|
@@ -10467,7 +11068,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
10467
11068
|
childTable,
|
|
10468
11069
|
parentTable,
|
|
10469
11070
|
evidence: {
|
|
10470
|
-
file:
|
|
11071
|
+
file: path46.relative(serviceDir, file.path),
|
|
10471
11072
|
line,
|
|
10472
11073
|
snippet: snippet(file.content, line)
|
|
10473
11074
|
}
|
|
@@ -10478,15 +11079,15 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
10478
11079
|
}
|
|
10479
11080
|
|
|
10480
11081
|
// src/extract/calls/eloquent.ts
|
|
10481
|
-
import
|
|
11082
|
+
import path47 from "path";
|
|
10482
11083
|
import Parser13 from "tree-sitter";
|
|
10483
|
-
import
|
|
11084
|
+
import Php3 from "tree-sitter-php";
|
|
10484
11085
|
import { infraId as infraId14 } from "@neat.is/types";
|
|
10485
11086
|
var LARAVEL_SCHEMA_RE = /\bSchema::(create|createIfNotExists|table)\b/;
|
|
10486
11087
|
var PARSE_CHUNK9 = 16384;
|
|
10487
11088
|
function makePhpParser2() {
|
|
10488
11089
|
const p = new Parser13();
|
|
10489
|
-
p.setLanguage(
|
|
11090
|
+
p.setLanguage(Php3.php_only);
|
|
10490
11091
|
return p;
|
|
10491
11092
|
}
|
|
10492
11093
|
function parseSource9(parser, source) {
|
|
@@ -10735,7 +11336,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
10735
11336
|
confidenceKind: "structural",
|
|
10736
11337
|
...columns.length > 0 ? { columns } : {},
|
|
10737
11338
|
evidence: {
|
|
10738
|
-
file:
|
|
11339
|
+
file: path47.relative(serviceDir, file.path),
|
|
10739
11340
|
line: bp.line,
|
|
10740
11341
|
snippet: snippet(file.content, bp.line)
|
|
10741
11342
|
}
|
|
@@ -10757,7 +11358,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
10757
11358
|
childTable,
|
|
10758
11359
|
parentTable,
|
|
10759
11360
|
evidence: {
|
|
10760
|
-
file:
|
|
11361
|
+
file: path47.relative(serviceDir, file.path),
|
|
10761
11362
|
line,
|
|
10762
11363
|
snippet: snippet(file.content, line)
|
|
10763
11364
|
}
|
|
@@ -10877,7 +11478,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
10877
11478
|
edgeType: "CALLS",
|
|
10878
11479
|
confidenceKind: "verified-call-site",
|
|
10879
11480
|
evidence: {
|
|
10880
|
-
file:
|
|
11481
|
+
file: path47.relative(serviceDir, file.path),
|
|
10881
11482
|
line,
|
|
10882
11483
|
snippet: snippet(file.content, line)
|
|
10883
11484
|
}
|
|
@@ -10913,7 +11514,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
10913
11514
|
childTable,
|
|
10914
11515
|
parentTable,
|
|
10915
11516
|
evidence: {
|
|
10916
|
-
file:
|
|
11517
|
+
file: path47.relative(serviceDir, file.path),
|
|
10917
11518
|
line,
|
|
10918
11519
|
snippet: snippet(file.content, line)
|
|
10919
11520
|
}
|
|
@@ -10924,9 +11525,9 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
10924
11525
|
}
|
|
10925
11526
|
|
|
10926
11527
|
// src/extract/calls/go.ts
|
|
10927
|
-
import
|
|
11528
|
+
import path48 from "path";
|
|
10928
11529
|
import Parser14 from "tree-sitter";
|
|
10929
|
-
import
|
|
11530
|
+
import Go4 from "tree-sitter-go";
|
|
10930
11531
|
import { infraId as infraId15 } from "@neat.is/types";
|
|
10931
11532
|
var PARSE_CHUNK10 = 16384;
|
|
10932
11533
|
var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
|
|
@@ -10955,7 +11556,7 @@ var DATABASE_SQL_IMPORT = "database/sql";
|
|
|
10955
11556
|
var SQLX_IMPORT = "github.com/jmoiron/sqlx";
|
|
10956
11557
|
function makeGoParser3() {
|
|
10957
11558
|
const p = new Parser14();
|
|
10958
|
-
p.setLanguage(
|
|
11559
|
+
p.setLanguage(Go4);
|
|
10959
11560
|
return p;
|
|
10960
11561
|
}
|
|
10961
11562
|
function parseSource10(parser, source) {
|
|
@@ -10998,7 +11599,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
10998
11599
|
return null;
|
|
10999
11600
|
}
|
|
11000
11601
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
11001
|
-
if (
|
|
11602
|
+
if (path48.extname(file.path) !== ".go") return [];
|
|
11002
11603
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
11003
11604
|
const tree = parseSource10(makeGoParser3(), file.content);
|
|
11004
11605
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -11027,7 +11628,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
11027
11628
|
confidenceKind: "verified-call-site",
|
|
11028
11629
|
...columns.length > 0 ? { columns } : {},
|
|
11029
11630
|
evidence: {
|
|
11030
|
-
file: toPosix(
|
|
11631
|
+
file: toPosix(path48.relative(serviceDir, file.path)),
|
|
11031
11632
|
line,
|
|
11032
11633
|
snippet: snippet(file.content, line)
|
|
11033
11634
|
}
|
|
@@ -11037,15 +11638,15 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
11037
11638
|
}
|
|
11038
11639
|
|
|
11039
11640
|
// src/extract/calls/gorm.ts
|
|
11040
|
-
import
|
|
11641
|
+
import path49 from "path";
|
|
11041
11642
|
import Parser15 from "tree-sitter";
|
|
11042
|
-
import
|
|
11643
|
+
import Go5 from "tree-sitter-go";
|
|
11043
11644
|
import { infraId as infraId16 } from "@neat.is/types";
|
|
11044
11645
|
var GORM_IMPORT_RE = /gorm\.io\/gorm/;
|
|
11045
11646
|
var PARSE_CHUNK11 = 16384;
|
|
11046
11647
|
function makeGoParser4() {
|
|
11047
11648
|
const p = new Parser15();
|
|
11048
|
-
p.setLanguage(
|
|
11649
|
+
p.setLanguage(Go5);
|
|
11049
11650
|
return p;
|
|
11050
11651
|
}
|
|
11051
11652
|
function parseSource11(parser, source) {
|
|
@@ -11470,7 +12071,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
11470
12071
|
seen.delete(struct.name);
|
|
11471
12072
|
}
|
|
11472
12073
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
11473
|
-
if (
|
|
12074
|
+
if (path49.extname(file.path) !== ".go") return [];
|
|
11474
12075
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
11475
12076
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
11476
12077
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -11492,7 +12093,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
11492
12093
|
confidenceKind: "structural",
|
|
11493
12094
|
...columns.length > 0 ? { columns } : {},
|
|
11494
12095
|
evidence: {
|
|
11495
|
-
file: toPosix(
|
|
12096
|
+
file: toPosix(path49.relative(serviceDir, file.path)),
|
|
11496
12097
|
line: struct.line,
|
|
11497
12098
|
snippet: snippet(file.content, struct.line)
|
|
11498
12099
|
}
|
|
@@ -11501,7 +12102,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
11501
12102
|
return out;
|
|
11502
12103
|
}
|
|
11503
12104
|
function gormForeignKeys(file, serviceDir) {
|
|
11504
|
-
if (
|
|
12105
|
+
if (path49.extname(file.path) !== ".go") return [];
|
|
11505
12106
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
11506
12107
|
const tree = parseSource11(makeGoParser4(), file.content);
|
|
11507
12108
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -11516,7 +12117,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
11516
12117
|
childTable,
|
|
11517
12118
|
parentTable,
|
|
11518
12119
|
evidence: {
|
|
11519
|
-
file: toPosix(
|
|
12120
|
+
file: toPosix(path49.relative(serviceDir, file.path)),
|
|
11520
12121
|
line,
|
|
11521
12122
|
snippet: snippet(file.content, line)
|
|
11522
12123
|
}
|
|
@@ -11619,7 +12220,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11619
12220
|
if (!graph.hasNode(ep.infraId)) {
|
|
11620
12221
|
const node = {
|
|
11621
12222
|
id: ep.infraId,
|
|
11622
|
-
type:
|
|
12223
|
+
type: NodeType21.InfraNode,
|
|
11623
12224
|
name: ep.name,
|
|
11624
12225
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
11625
12226
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -11632,7 +12233,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11632
12233
|
}
|
|
11633
12234
|
if (ep.columns && ep.columns.length > 0) {
|
|
11634
12235
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
11635
|
-
if (node.type ===
|
|
12236
|
+
if (node.type === NodeType21.InfraNode) {
|
|
11636
12237
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
11637
12238
|
...node,
|
|
11638
12239
|
columns: foldColumns(
|
|
@@ -11646,7 +12247,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11646
12247
|
}
|
|
11647
12248
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
11648
12249
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
11649
|
-
if (node.type ===
|
|
12250
|
+
if (node.type === NodeType21.InfraNode) {
|
|
11650
12251
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
11651
12252
|
...node,
|
|
11652
12253
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -11708,7 +12309,7 @@ async function addCallEdges(graph, services) {
|
|
|
11708
12309
|
// src/extract/table-edges.ts
|
|
11709
12310
|
import {
|
|
11710
12311
|
EdgeType as EdgeType16,
|
|
11711
|
-
NodeType as
|
|
12312
|
+
NodeType as NodeType22,
|
|
11712
12313
|
Provenance as Provenance16,
|
|
11713
12314
|
confidenceForExtracted as confidenceForExtracted13,
|
|
11714
12315
|
extractedEdgeId as extractedEdgeId10,
|
|
@@ -11767,7 +12368,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
11767
12368
|
if (graph.hasNode(id)) return 0;
|
|
11768
12369
|
const node = {
|
|
11769
12370
|
id,
|
|
11770
|
-
type:
|
|
12371
|
+
type: NodeType22.InfraNode,
|
|
11771
12372
|
name,
|
|
11772
12373
|
provider: "self",
|
|
11773
12374
|
kind: "sql-table"
|
|
@@ -11777,15 +12378,15 @@ function ensureTableNode(graph, id, name) {
|
|
|
11777
12378
|
}
|
|
11778
12379
|
|
|
11779
12380
|
// src/extract/infra/docker-compose.ts
|
|
11780
|
-
import
|
|
12381
|
+
import path50 from "path";
|
|
11781
12382
|
import { EdgeType as EdgeType17, Provenance as Provenance18, confidenceForExtracted as confidenceForExtracted15 } from "@neat.is/types";
|
|
11782
12383
|
|
|
11783
12384
|
// src/extract/infra/shared.ts
|
|
11784
|
-
import { NodeType as
|
|
12385
|
+
import { NodeType as NodeType23, Provenance as Provenance17, confidenceForExtracted as confidenceForExtracted14, infraId as infraId18 } from "@neat.is/types";
|
|
11785
12386
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
11786
12387
|
return {
|
|
11787
12388
|
id: infraId18(kind, name),
|
|
11788
|
-
type:
|
|
12389
|
+
type: NodeType23.InfraNode,
|
|
11789
12390
|
name,
|
|
11790
12391
|
provider,
|
|
11791
12392
|
kind,
|
|
@@ -11847,7 +12448,7 @@ function dependsOnList(value) {
|
|
|
11847
12448
|
}
|
|
11848
12449
|
function serviceNameToServiceNode(name, services) {
|
|
11849
12450
|
for (const s of services) {
|
|
11850
|
-
if (s.node.name === name ||
|
|
12451
|
+
if (s.node.name === name || path50.basename(s.dir) === name) return s.node.id;
|
|
11851
12452
|
}
|
|
11852
12453
|
return null;
|
|
11853
12454
|
}
|
|
@@ -11856,7 +12457,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
11856
12457
|
let edgesAdded = 0;
|
|
11857
12458
|
let composePath = null;
|
|
11858
12459
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
11859
|
-
const abs =
|
|
12460
|
+
const abs = path50.join(scanPath, name);
|
|
11860
12461
|
if (await exists(abs)) {
|
|
11861
12462
|
composePath = abs;
|
|
11862
12463
|
break;
|
|
@@ -11869,13 +12470,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
11869
12470
|
} catch (err) {
|
|
11870
12471
|
recordExtractionError(
|
|
11871
12472
|
"infra docker-compose",
|
|
11872
|
-
|
|
12473
|
+
path50.relative(scanPath, composePath),
|
|
11873
12474
|
err
|
|
11874
12475
|
);
|
|
11875
12476
|
return { nodesAdded, edgesAdded };
|
|
11876
12477
|
}
|
|
11877
12478
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
11878
|
-
const evidenceFile =
|
|
12479
|
+
const evidenceFile = path50.relative(scanPath, composePath).split(path50.sep).join("/");
|
|
11879
12480
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
11880
12481
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
11881
12482
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -11916,8 +12517,8 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
11916
12517
|
}
|
|
11917
12518
|
|
|
11918
12519
|
// src/extract/infra/dockerfile.ts
|
|
11919
|
-
import
|
|
11920
|
-
import { promises as
|
|
12520
|
+
import path51 from "path";
|
|
12521
|
+
import { promises as fs21 } from "fs";
|
|
11921
12522
|
import { EdgeType as EdgeType18, Provenance as Provenance19, confidenceForExtracted as confidenceForExtracted16 } from "@neat.is/types";
|
|
11922
12523
|
function readDockerfile(content) {
|
|
11923
12524
|
let image = null;
|
|
@@ -11947,15 +12548,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
11947
12548
|
let nodesAdded = 0;
|
|
11948
12549
|
let edgesAdded = 0;
|
|
11949
12550
|
for (const service of services) {
|
|
11950
|
-
const dockerfilePath =
|
|
12551
|
+
const dockerfilePath = path51.join(service.dir, "Dockerfile");
|
|
11951
12552
|
if (!await exists(dockerfilePath)) continue;
|
|
11952
12553
|
let content;
|
|
11953
12554
|
try {
|
|
11954
|
-
content = await
|
|
12555
|
+
content = await fs21.readFile(dockerfilePath, "utf8");
|
|
11955
12556
|
} catch (err) {
|
|
11956
12557
|
recordExtractionError(
|
|
11957
12558
|
"infra dockerfile",
|
|
11958
|
-
|
|
12559
|
+
path51.relative(scanPath, dockerfilePath),
|
|
11959
12560
|
err
|
|
11960
12561
|
);
|
|
11961
12562
|
continue;
|
|
@@ -11967,8 +12568,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
11967
12568
|
graph.addNode(node.id, node);
|
|
11968
12569
|
nodesAdded++;
|
|
11969
12570
|
}
|
|
11970
|
-
const relDockerfile = toPosix(
|
|
11971
|
-
const evidenceFile = toPosix(
|
|
12571
|
+
const relDockerfile = toPosix(path51.relative(service.dir, dockerfilePath));
|
|
12572
|
+
const evidenceFile = toPosix(path51.relative(scanPath, dockerfilePath));
|
|
11972
12573
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
11973
12574
|
graph,
|
|
11974
12575
|
service.pkg.name,
|
|
@@ -12019,23 +12620,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12019
12620
|
}
|
|
12020
12621
|
|
|
12021
12622
|
// src/extract/infra/terraform.ts
|
|
12022
|
-
import { promises as
|
|
12023
|
-
import
|
|
12623
|
+
import { promises as fs22 } from "fs";
|
|
12624
|
+
import path52 from "path";
|
|
12024
12625
|
import { EdgeType as EdgeType19, Provenance as Provenance20, confidenceForExtracted as confidenceForExtracted17 } from "@neat.is/types";
|
|
12025
12626
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
12026
12627
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
12027
12628
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
12028
12629
|
if (depth > max) return [];
|
|
12029
12630
|
const out = [];
|
|
12030
|
-
const entries = await
|
|
12631
|
+
const entries = await fs22.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
12031
12632
|
for (const entry of entries) {
|
|
12032
12633
|
if (entry.isDirectory()) {
|
|
12033
12634
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
12034
|
-
const child =
|
|
12635
|
+
const child = path52.join(start, entry.name);
|
|
12035
12636
|
if (await isPythonVenvDir(child)) continue;
|
|
12036
12637
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
12037
12638
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
12038
|
-
out.push(
|
|
12639
|
+
out.push(path52.join(start, entry.name));
|
|
12039
12640
|
}
|
|
12040
12641
|
}
|
|
12041
12642
|
return out;
|
|
@@ -12066,8 +12667,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
12066
12667
|
let edgesAdded = 0;
|
|
12067
12668
|
const files = await walkTfFiles(scanPath);
|
|
12068
12669
|
for (const file of files) {
|
|
12069
|
-
const content = await
|
|
12070
|
-
const evidenceFile = toPosix(
|
|
12670
|
+
const content = await fs22.readFile(file, "utf8");
|
|
12671
|
+
const evidenceFile = toPosix(path52.relative(scanPath, file));
|
|
12071
12672
|
const resources = [];
|
|
12072
12673
|
const byKey = /* @__PURE__ */ new Map();
|
|
12073
12674
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -12123,8 +12724,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
12123
12724
|
}
|
|
12124
12725
|
|
|
12125
12726
|
// src/extract/infra/k8s.ts
|
|
12126
|
-
import { promises as
|
|
12127
|
-
import
|
|
12727
|
+
import { promises as fs23 } from "fs";
|
|
12728
|
+
import path53 from "path";
|
|
12128
12729
|
import { parseAllDocuments as parseAllDocuments2 } from "yaml";
|
|
12129
12730
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
12130
12731
|
Service: "k8s-service",
|
|
@@ -12138,15 +12739,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
12138
12739
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
12139
12740
|
if (depth > max) return [];
|
|
12140
12741
|
const out = [];
|
|
12141
|
-
const entries = await
|
|
12742
|
+
const entries = await fs23.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
12142
12743
|
for (const entry of entries) {
|
|
12143
12744
|
if (entry.isDirectory()) {
|
|
12144
12745
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
12145
|
-
const child =
|
|
12746
|
+
const child = path53.join(start, entry.name);
|
|
12146
12747
|
if (await isPythonVenvDir(child)) continue;
|
|
12147
12748
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
12148
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
12149
|
-
out.push(
|
|
12749
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path53.extname(entry.name))) {
|
|
12750
|
+
out.push(path53.join(start, entry.name));
|
|
12150
12751
|
}
|
|
12151
12752
|
}
|
|
12152
12753
|
return out;
|
|
@@ -12155,7 +12756,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
12155
12756
|
let nodesAdded = 0;
|
|
12156
12757
|
const files = await walkYamlFiles2(scanPath);
|
|
12157
12758
|
for (const file of files) {
|
|
12158
|
-
const content = await
|
|
12759
|
+
const content = await fs23.readFile(file, "utf8");
|
|
12159
12760
|
let docs;
|
|
12160
12761
|
try {
|
|
12161
12762
|
docs = parseAllDocuments2(content).map((d) => d.toJSON());
|
|
@@ -12178,16 +12779,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
12178
12779
|
}
|
|
12179
12780
|
|
|
12180
12781
|
// src/extract/infra/cloudflare.ts
|
|
12181
|
-
import { promises as
|
|
12182
|
-
import
|
|
12782
|
+
import { promises as fs24 } from "fs";
|
|
12783
|
+
import path54 from "path";
|
|
12183
12784
|
import { parse as parseToml2 } from "smol-toml";
|
|
12184
12785
|
import { EdgeType as EdgeType20, Provenance as Provenance21, confidenceForExtracted as confidenceForExtracted18 } from "@neat.is/types";
|
|
12185
12786
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
12186
12787
|
async function readWranglerConfig(dir) {
|
|
12187
12788
|
for (const filename of WRANGLER_FILENAMES) {
|
|
12188
|
-
const abs =
|
|
12789
|
+
const abs = path54.join(dir, filename);
|
|
12189
12790
|
if (!await exists(abs)) continue;
|
|
12190
|
-
const raw = await
|
|
12791
|
+
const raw = await fs24.readFile(abs, "utf8");
|
|
12191
12792
|
const config = filename === "wrangler.toml" ? parseToml2(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
12192
12793
|
return { config, relFile: filename, raw };
|
|
12193
12794
|
}
|
|
@@ -12248,11 +12849,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12248
12849
|
try {
|
|
12249
12850
|
read = await readWranglerConfig(service.dir);
|
|
12250
12851
|
} catch (err) {
|
|
12251
|
-
recordExtractionError("infra cloudflare",
|
|
12852
|
+
recordExtractionError("infra cloudflare", path54.relative(scanPath, service.dir), err);
|
|
12252
12853
|
continue;
|
|
12253
12854
|
}
|
|
12254
12855
|
if (!read || !read.config.name) continue;
|
|
12255
|
-
const evidenceFile = toPosix(
|
|
12856
|
+
const evidenceFile = toPosix(path54.relative(scanPath, path54.join(service.dir, read.relFile)));
|
|
12256
12857
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
12257
12858
|
}
|
|
12258
12859
|
for (const worker of discovered) {
|
|
@@ -12264,7 +12865,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12264
12865
|
}
|
|
12265
12866
|
let anchorId = service.node.id;
|
|
12266
12867
|
if (config.main) {
|
|
12267
|
-
const entryRelPath = toPosix(
|
|
12868
|
+
const entryRelPath = toPosix(path54.normalize(config.main));
|
|
12268
12869
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
12269
12870
|
graph,
|
|
12270
12871
|
service.pkg.name,
|
|
@@ -12410,24 +13011,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12410
13011
|
}
|
|
12411
13012
|
|
|
12412
13013
|
// src/extract/infra/vercel.ts
|
|
12413
|
-
import { promises as
|
|
12414
|
-
import
|
|
13014
|
+
import { promises as fs25 } from "fs";
|
|
13015
|
+
import path55 from "path";
|
|
12415
13016
|
import { EdgeType as EdgeType21 } from "@neat.is/types";
|
|
12416
13017
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
12417
13018
|
async function readVercelConfig(dir) {
|
|
12418
13019
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
12419
|
-
const abs =
|
|
13020
|
+
const abs = path55.join(dir, filename);
|
|
12420
13021
|
if (!await exists(abs)) continue;
|
|
12421
|
-
const raw = await
|
|
13022
|
+
const raw = await fs25.readFile(abs, "utf8");
|
|
12422
13023
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
12423
13024
|
return { config, relFile: filename, raw };
|
|
12424
13025
|
}
|
|
12425
13026
|
return null;
|
|
12426
13027
|
}
|
|
12427
13028
|
async function readLinkedProjectName(dir) {
|
|
12428
|
-
const abs =
|
|
13029
|
+
const abs = path55.join(dir, ".vercel", "project.json");
|
|
12429
13030
|
if (!await exists(abs)) return void 0;
|
|
12430
|
-
const parsed = JSON.parse(await
|
|
13031
|
+
const parsed = JSON.parse(await fs25.readFile(abs, "utf8"));
|
|
12431
13032
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
12432
13033
|
}
|
|
12433
13034
|
function routeSource(route) {
|
|
@@ -12443,7 +13044,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12443
13044
|
read = await readVercelConfig(service.dir);
|
|
12444
13045
|
projectName = await readLinkedProjectName(service.dir);
|
|
12445
13046
|
} catch (err) {
|
|
12446
|
-
recordExtractionError("infra vercel",
|
|
13047
|
+
recordExtractionError("infra vercel", path55.relative(scanPath, service.dir), err);
|
|
12447
13048
|
continue;
|
|
12448
13049
|
}
|
|
12449
13050
|
if (!read && !projectName) continue;
|
|
@@ -12459,7 +13060,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12459
13060
|
const anchorId = service.node.id;
|
|
12460
13061
|
if (!read) continue;
|
|
12461
13062
|
const { config, relFile, raw } = read;
|
|
12462
|
-
const evidenceFile = toPosix(
|
|
13063
|
+
const evidenceFile = toPosix(path55.relative(scanPath, path55.join(service.dir, relFile)));
|
|
12463
13064
|
const add = (edgeType, kind, name) => {
|
|
12464
13065
|
if (!name) return;
|
|
12465
13066
|
const result = emitPlatformResourceEdge(
|
|
@@ -12487,16 +13088,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12487
13088
|
}
|
|
12488
13089
|
|
|
12489
13090
|
// src/extract/infra/railway.ts
|
|
12490
|
-
import { promises as
|
|
12491
|
-
import
|
|
13091
|
+
import { promises as fs26 } from "fs";
|
|
13092
|
+
import path56 from "path";
|
|
12492
13093
|
import { parse as parseToml3 } from "smol-toml";
|
|
12493
13094
|
import { EdgeType as EdgeType22 } from "@neat.is/types";
|
|
12494
13095
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
12495
13096
|
async function readRailwayConfig(dir) {
|
|
12496
13097
|
for (const filename of RAILWAY_FILENAMES) {
|
|
12497
|
-
const abs =
|
|
13098
|
+
const abs = path56.join(dir, filename);
|
|
12498
13099
|
if (!await exists(abs)) continue;
|
|
12499
|
-
const raw = await
|
|
13100
|
+
const raw = await fs26.readFile(abs, "utf8");
|
|
12500
13101
|
const config = filename === "railway.toml" ? parseToml3(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
12501
13102
|
return { config, relFile: filename, raw };
|
|
12502
13103
|
}
|
|
@@ -12510,7 +13111,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12510
13111
|
try {
|
|
12511
13112
|
read = await readRailwayConfig(service.dir);
|
|
12512
13113
|
} catch (err) {
|
|
12513
|
-
recordExtractionError("infra railway",
|
|
13114
|
+
recordExtractionError("infra railway", path56.relative(scanPath, service.dir), err);
|
|
12514
13115
|
continue;
|
|
12515
13116
|
}
|
|
12516
13117
|
if (!read) continue;
|
|
@@ -12520,7 +13121,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12520
13121
|
}
|
|
12521
13122
|
const anchorId = service.node.id;
|
|
12522
13123
|
const { config, relFile, raw } = read;
|
|
12523
|
-
const evidenceFile = toPosix(
|
|
13124
|
+
const evidenceFile = toPosix(path56.relative(scanPath, path56.join(service.dir, relFile)));
|
|
12524
13125
|
const add = (edgeType, kind, name) => {
|
|
12525
13126
|
if (!name) return;
|
|
12526
13127
|
const result = emitPlatformResourceEdge(
|
|
@@ -12544,15 +13145,15 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12544
13145
|
}
|
|
12545
13146
|
|
|
12546
13147
|
// src/extract/infra/supabase.ts
|
|
12547
|
-
import { promises as
|
|
12548
|
-
import
|
|
13148
|
+
import { promises as fs27 } from "fs";
|
|
13149
|
+
import path57 from "path";
|
|
12549
13150
|
import { parse as parseToml4 } from "smol-toml";
|
|
12550
13151
|
import { EdgeType as EdgeType23 } from "@neat.is/types";
|
|
12551
13152
|
async function readSupabaseConfig(dir) {
|
|
12552
|
-
const relFile =
|
|
12553
|
-
const abs =
|
|
13153
|
+
const relFile = path57.join("supabase", "config.toml");
|
|
13154
|
+
const abs = path57.join(dir, relFile);
|
|
12554
13155
|
if (!await exists(abs)) return null;
|
|
12555
|
-
const raw = await
|
|
13156
|
+
const raw = await fs27.readFile(abs, "utf8");
|
|
12556
13157
|
const config = parseToml4(raw);
|
|
12557
13158
|
return { config, relFile, raw };
|
|
12558
13159
|
}
|
|
@@ -12564,7 +13165,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
12564
13165
|
try {
|
|
12565
13166
|
read = await readSupabaseConfig(service.dir);
|
|
12566
13167
|
} catch (err) {
|
|
12567
|
-
recordExtractionError("infra supabase",
|
|
13168
|
+
recordExtractionError("infra supabase", path57.relative(scanPath, service.dir), err);
|
|
12568
13169
|
continue;
|
|
12569
13170
|
}
|
|
12570
13171
|
if (!read) continue;
|
|
@@ -12579,7 +13180,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
12579
13180
|
});
|
|
12580
13181
|
}
|
|
12581
13182
|
const anchorId = service.node.id;
|
|
12582
|
-
const evidenceFile = toPosix(
|
|
13183
|
+
const evidenceFile = toPosix(path57.relative(scanPath, path57.join(service.dir, relFile)));
|
|
12583
13184
|
const add = (edgeType, kind, name) => {
|
|
12584
13185
|
if (!name) return;
|
|
12585
13186
|
const result = emitPlatformResourceEdge(
|
|
@@ -12620,12 +13221,12 @@ async function addInfra(graph, scanPath, services) {
|
|
|
12620
13221
|
}
|
|
12621
13222
|
|
|
12622
13223
|
// src/extract/zod-shapes.ts
|
|
12623
|
-
import
|
|
13224
|
+
import path58 from "path";
|
|
12624
13225
|
import Parser16 from "tree-sitter";
|
|
12625
13226
|
import JavaScript8 from "tree-sitter-javascript";
|
|
12626
13227
|
import {
|
|
12627
13228
|
EdgeType as EdgeType24,
|
|
12628
|
-
NodeType as
|
|
13229
|
+
NodeType as NodeType24,
|
|
12629
13230
|
Provenance as Provenance22,
|
|
12630
13231
|
confidenceForExtracted as confidenceForExtracted19,
|
|
12631
13232
|
extractedEdgeId as extractedEdgeId11,
|
|
@@ -12722,7 +13323,7 @@ function topLevelSchemas(root) {
|
|
|
12722
13323
|
}
|
|
12723
13324
|
function zodShapesFromFile(file, serviceDir) {
|
|
12724
13325
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
12725
|
-
const tree = parseSource3(parserForExt4(
|
|
13326
|
+
const tree = parseSource3(parserForExt4(path58.extname(file.path)), file.content);
|
|
12726
13327
|
const out = [];
|
|
12727
13328
|
const seen = /* @__PURE__ */ new Set();
|
|
12728
13329
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -12740,7 +13341,7 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
12740
13341
|
name,
|
|
12741
13342
|
fields,
|
|
12742
13343
|
evidence: {
|
|
12743
|
-
file:
|
|
13344
|
+
file: path58.relative(serviceDir, file.path),
|
|
12744
13345
|
line,
|
|
12745
13346
|
snippet: snippet(file.content, line)
|
|
12746
13347
|
}
|
|
@@ -12771,7 +13372,7 @@ async function addZodShapes(graph, services) {
|
|
|
12771
13372
|
if (!graph.hasNode(shape.infraId)) {
|
|
12772
13373
|
const node = {
|
|
12773
13374
|
id: shape.infraId,
|
|
12774
|
-
type:
|
|
13375
|
+
type: NodeType24.InfraNode,
|
|
12775
13376
|
name: shape.name,
|
|
12776
13377
|
provider: "self",
|
|
12777
13378
|
kind: "zod-schema"
|
|
@@ -12781,7 +13382,7 @@ async function addZodShapes(graph, services) {
|
|
|
12781
13382
|
}
|
|
12782
13383
|
if (shape.fields.length > 0) {
|
|
12783
13384
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
12784
|
-
if (node.type ===
|
|
13385
|
+
if (node.type === NodeType24.InfraNode) {
|
|
12785
13386
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
12786
13387
|
...node,
|
|
12787
13388
|
columns: foldColumns(
|
|
@@ -12823,7 +13424,7 @@ async function addZodShapes(graph, services) {
|
|
|
12823
13424
|
}
|
|
12824
13425
|
|
|
12825
13426
|
// src/extract/firestore-rules.ts
|
|
12826
|
-
import { NodeType as
|
|
13427
|
+
import { NodeType as NodeType25 } from "@neat.is/types";
|
|
12827
13428
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
12828
13429
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
12829
13430
|
function stripComments(src) {
|
|
@@ -12963,7 +13564,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
12963
13564
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
12964
13565
|
graph.forEachNode((id, attrs) => {
|
|
12965
13566
|
const node = attrs;
|
|
12966
|
-
if (node.type !==
|
|
13567
|
+
if (node.type !== NodeType25.InfraNode) return;
|
|
12967
13568
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
12968
13569
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
12969
13570
|
if (!fields || fields.size === 0) return;
|
|
@@ -12976,16 +13577,16 @@ async function addFirestoreRules(graph, services) {
|
|
|
12976
13577
|
}
|
|
12977
13578
|
|
|
12978
13579
|
// src/extract/index.ts
|
|
12979
|
-
import
|
|
13580
|
+
import path60 from "path";
|
|
12980
13581
|
|
|
12981
13582
|
// src/extract/retire.ts
|
|
12982
13583
|
import { existsSync as existsSync2 } from "fs";
|
|
12983
|
-
import
|
|
12984
|
-
import { NodeType as
|
|
13584
|
+
import path59 from "path";
|
|
13585
|
+
import { NodeType as NodeType26, Provenance as Provenance23 } from "@neat.is/types";
|
|
12985
13586
|
function dropOrphanedFileNodes(graph) {
|
|
12986
13587
|
const orphans = [];
|
|
12987
13588
|
graph.forEachNode((id, attrs) => {
|
|
12988
|
-
if (attrs.type !==
|
|
13589
|
+
if (attrs.type !== NodeType26.FileNode) return;
|
|
12989
13590
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
12990
13591
|
orphans.push(id);
|
|
12991
13592
|
}
|
|
@@ -13014,11 +13615,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
13014
13615
|
if (edge.provenance !== Provenance23.EXTRACTED) return;
|
|
13015
13616
|
const evidenceFile = edge.evidence?.file;
|
|
13016
13617
|
if (!evidenceFile) return;
|
|
13017
|
-
if (
|
|
13618
|
+
if (path59.isAbsolute(evidenceFile)) {
|
|
13018
13619
|
if (!existsSync2(evidenceFile)) toDrop.push(id);
|
|
13019
13620
|
return;
|
|
13020
13621
|
}
|
|
13021
|
-
const found = bases.some((base) => existsSync2(
|
|
13622
|
+
const found = bases.some((base) => existsSync2(path59.join(base, evidenceFile)));
|
|
13022
13623
|
if (!found) toDrop.push(id);
|
|
13023
13624
|
});
|
|
13024
13625
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -13075,7 +13676,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
13075
13676
|
}
|
|
13076
13677
|
const droppedEntries = drainDroppedExtracted();
|
|
13077
13678
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
13078
|
-
const rejectedPath =
|
|
13679
|
+
const rejectedPath = path60.join(path60.dirname(opts.errorsPath), "rejected.ndjson");
|
|
13079
13680
|
try {
|
|
13080
13681
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
13081
13682
|
} catch (err) {
|
|
@@ -13112,11 +13713,11 @@ import {
|
|
|
13112
13713
|
databaseId as databaseId3,
|
|
13113
13714
|
DivergenceResultSchema,
|
|
13114
13715
|
EdgeType as EdgeType25,
|
|
13115
|
-
NodeType as
|
|
13716
|
+
NodeType as NodeType27,
|
|
13116
13717
|
parseEdgeId,
|
|
13117
13718
|
parseFileId,
|
|
13118
13719
|
Provenance as Provenance24,
|
|
13119
|
-
serviceId as
|
|
13720
|
+
serviceId as serviceId10
|
|
13120
13721
|
} from "@neat.is/types";
|
|
13121
13722
|
function bucketKey(source, target, type) {
|
|
13122
13723
|
return `${type}|${source}|${target}`;
|
|
@@ -13126,8 +13727,8 @@ function bucketSourceFor(graph, edge) {
|
|
|
13126
13727
|
const parsed = parseFileId(edge.source);
|
|
13127
13728
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
13128
13729
|
const target = graph.getNodeAttributes(edge.target);
|
|
13129
|
-
if (target.type !==
|
|
13130
|
-
return
|
|
13730
|
+
if (target.type !== NodeType27.DatabaseNode) return edge.source;
|
|
13731
|
+
return serviceId10(parsed.service);
|
|
13131
13732
|
}
|
|
13132
13733
|
function bucketEdges(graph) {
|
|
13133
13734
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
@@ -13158,22 +13759,22 @@ function bucketEdges(graph) {
|
|
|
13158
13759
|
function nodeIsFrontier(graph, nodeId) {
|
|
13159
13760
|
if (!graph.hasNode(nodeId)) return false;
|
|
13160
13761
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
13161
|
-
return attrs.type ===
|
|
13762
|
+
return attrs.type === NodeType27.FrontierNode;
|
|
13162
13763
|
}
|
|
13163
13764
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
13164
13765
|
if (!graph.hasNode(nodeId)) return false;
|
|
13165
13766
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
13166
|
-
return attrs.type ===
|
|
13767
|
+
return attrs.type === NodeType27.WebSocketChannelNode;
|
|
13167
13768
|
}
|
|
13168
13769
|
function nodeIsServerAction(graph, nodeId) {
|
|
13169
13770
|
if (!graph.hasNode(nodeId)) return false;
|
|
13170
13771
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
13171
|
-
return attrs.type ===
|
|
13772
|
+
return attrs.type === NodeType27.ServerActionNode;
|
|
13172
13773
|
}
|
|
13173
13774
|
function nodeIsSymbol(graph, nodeId) {
|
|
13174
13775
|
if (!graph.hasNode(nodeId)) return false;
|
|
13175
13776
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
13176
|
-
return attrs.type ===
|
|
13777
|
+
return attrs.type === NodeType27.SymbolNode;
|
|
13177
13778
|
}
|
|
13178
13779
|
function clampConfidence(n) {
|
|
13179
13780
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -13258,7 +13859,7 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
13258
13859
|
if (edge.type !== EdgeType25.CONNECTS_TO) continue;
|
|
13259
13860
|
if (edge.provenance !== Provenance24.OBSERVED) continue;
|
|
13260
13861
|
const target = graph.getNodeAttributes(edge.target);
|
|
13261
|
-
if (target.type !==
|
|
13862
|
+
if (target.type !== NodeType27.DatabaseNode) continue;
|
|
13262
13863
|
const observedHost = target.host?.trim();
|
|
13263
13864
|
if (!observedHost) continue;
|
|
13264
13865
|
if (observedHost === declaredHost) continue;
|
|
@@ -13283,7 +13884,7 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
13283
13884
|
if (edge.type !== EdgeType25.CONNECTS_TO) continue;
|
|
13284
13885
|
if (edge.provenance !== Provenance24.OBSERVED) continue;
|
|
13285
13886
|
const target = graph.getNodeAttributes(edge.target);
|
|
13286
|
-
if (target.type !==
|
|
13887
|
+
if (target.type !== NodeType27.DatabaseNode) continue;
|
|
13287
13888
|
for (const pair of compatPairs()) {
|
|
13288
13889
|
if (pair.engine !== target.engine) continue;
|
|
13289
13890
|
const declared = deps[pair.driver];
|
|
@@ -13399,13 +14000,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
13399
14000
|
}
|
|
13400
14001
|
graph.forEachNode((nodeId, attrs) => {
|
|
13401
14002
|
const n = attrs;
|
|
13402
|
-
if (n.type ===
|
|
14003
|
+
if (n.type === NodeType27.ServiceNode) {
|
|
13403
14004
|
const svc = n;
|
|
13404
14005
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
13405
14006
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
13406
14007
|
return;
|
|
13407
14008
|
}
|
|
13408
|
-
if (n.type ===
|
|
14009
|
+
if (n.type === NodeType27.InfraNode && n.kind === "sql-table") {
|
|
13409
14010
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
13410
14011
|
}
|
|
13411
14012
|
});
|
|
@@ -13449,9 +14050,9 @@ function computeDivergences(graph, opts = {}) {
|
|
|
13449
14050
|
}
|
|
13450
14051
|
|
|
13451
14052
|
// src/persist.ts
|
|
13452
|
-
import { promises as
|
|
13453
|
-
import
|
|
13454
|
-
import { NodeType as
|
|
14053
|
+
import { promises as fs28 } from "fs";
|
|
14054
|
+
import path61 from "path";
|
|
14055
|
+
import { NodeType as NodeType28, Provenance as Provenance25, observedEdgeId as observedEdgeId2 } from "@neat.is/types";
|
|
13455
14056
|
var SCHEMA_VERSION = 7;
|
|
13456
14057
|
function migrateV1ToV2(payload) {
|
|
13457
14058
|
const nodes = payload.graph.nodes;
|
|
@@ -13475,7 +14076,7 @@ function migrateV5ToV6(payload) {
|
|
|
13475
14076
|
if (Array.isArray(nodes)) {
|
|
13476
14077
|
for (const node of nodes) {
|
|
13477
14078
|
const attrs = node.attributes;
|
|
13478
|
-
if (!attrs || attrs.type !==
|
|
14079
|
+
if (!attrs || attrs.type !== NodeType28.InfraNode) continue;
|
|
13479
14080
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
13480
14081
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
13481
14082
|
}
|
|
@@ -13505,7 +14106,7 @@ function migrateV2ToV3(payload) {
|
|
|
13505
14106
|
return { ...payload, schemaVersion: 3 };
|
|
13506
14107
|
}
|
|
13507
14108
|
async function ensureDir(filePath) {
|
|
13508
|
-
await
|
|
14109
|
+
await fs28.mkdir(path61.dirname(filePath), { recursive: true });
|
|
13509
14110
|
}
|
|
13510
14111
|
async function saveGraphToDisk(graph, outPath) {
|
|
13511
14112
|
await ensureDir(outPath);
|
|
@@ -13515,13 +14116,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
13515
14116
|
graph: graph.export()
|
|
13516
14117
|
};
|
|
13517
14118
|
const tmp = `${outPath}.tmp`;
|
|
13518
|
-
await
|
|
13519
|
-
await
|
|
14119
|
+
await fs28.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
14120
|
+
await fs28.rename(tmp, outPath);
|
|
13520
14121
|
}
|
|
13521
14122
|
async function loadGraphFromDisk(graph, outPath) {
|
|
13522
14123
|
let raw;
|
|
13523
14124
|
try {
|
|
13524
|
-
raw = await
|
|
14125
|
+
raw = await fs28.readFile(outPath, "utf8");
|
|
13525
14126
|
} catch (err) {
|
|
13526
14127
|
if (err.code === "ENOENT") return;
|
|
13527
14128
|
throw err;
|
|
@@ -13594,7 +14195,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
13594
14195
|
}
|
|
13595
14196
|
|
|
13596
14197
|
// src/diff.ts
|
|
13597
|
-
import { promises as
|
|
14198
|
+
import { promises as fs29 } from "fs";
|
|
13598
14199
|
async function loadSnapshotForDiff(target) {
|
|
13599
14200
|
if (/^https?:\/\//i.test(target)) {
|
|
13600
14201
|
const res = await fetch(target);
|
|
@@ -13603,7 +14204,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
13603
14204
|
}
|
|
13604
14205
|
return await res.json();
|
|
13605
14206
|
}
|
|
13606
|
-
const raw = await
|
|
14207
|
+
const raw = await fs29.readFile(target, "utf8");
|
|
13607
14208
|
return JSON.parse(raw);
|
|
13608
14209
|
}
|
|
13609
14210
|
function indexEntries(entries) {
|
|
@@ -13670,23 +14271,23 @@ function canonicalJson(value) {
|
|
|
13670
14271
|
}
|
|
13671
14272
|
|
|
13672
14273
|
// src/projects.ts
|
|
13673
|
-
import
|
|
14274
|
+
import path62 from "path";
|
|
13674
14275
|
function pathsForProject(project, baseDir) {
|
|
13675
14276
|
if (project === DEFAULT_PROJECT) {
|
|
13676
14277
|
return {
|
|
13677
|
-
snapshotPath:
|
|
13678
|
-
errorsPath:
|
|
13679
|
-
staleEventsPath:
|
|
13680
|
-
embeddingsCachePath:
|
|
13681
|
-
policyViolationsPath:
|
|
14278
|
+
snapshotPath: path62.join(baseDir, "graph.json"),
|
|
14279
|
+
errorsPath: path62.join(baseDir, "errors.ndjson"),
|
|
14280
|
+
staleEventsPath: path62.join(baseDir, "stale-events.ndjson"),
|
|
14281
|
+
embeddingsCachePath: path62.join(baseDir, "embeddings.json"),
|
|
14282
|
+
policyViolationsPath: path62.join(baseDir, "policy-violations.ndjson")
|
|
13682
14283
|
};
|
|
13683
14284
|
}
|
|
13684
14285
|
return {
|
|
13685
|
-
snapshotPath:
|
|
13686
|
-
errorsPath:
|
|
13687
|
-
staleEventsPath:
|
|
13688
|
-
embeddingsCachePath:
|
|
13689
|
-
policyViolationsPath:
|
|
14286
|
+
snapshotPath: path62.join(baseDir, `${project}.json`),
|
|
14287
|
+
errorsPath: path62.join(baseDir, `errors.${project}.ndjson`),
|
|
14288
|
+
staleEventsPath: path62.join(baseDir, `stale-events.${project}.ndjson`),
|
|
14289
|
+
embeddingsCachePath: path62.join(baseDir, `embeddings.${project}.json`),
|
|
14290
|
+
policyViolationsPath: path62.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
13690
14291
|
};
|
|
13691
14292
|
}
|
|
13692
14293
|
var Projects = class {
|
|
@@ -13725,9 +14326,9 @@ function parseExtraProjects(raw) {
|
|
|
13725
14326
|
}
|
|
13726
14327
|
|
|
13727
14328
|
// src/registry.ts
|
|
13728
|
-
import { promises as
|
|
14329
|
+
import { promises as fs30 } from "fs";
|
|
13729
14330
|
import os2 from "os";
|
|
13730
|
-
import
|
|
14331
|
+
import path63 from "path";
|
|
13731
14332
|
import {
|
|
13732
14333
|
RegistryFileSchema
|
|
13733
14334
|
} from "@neat.is/types";
|
|
@@ -13735,20 +14336,20 @@ var LOCK_TIMEOUT_MS = 5e3;
|
|
|
13735
14336
|
var LOCK_RETRY_MS = 50;
|
|
13736
14337
|
function neatHome() {
|
|
13737
14338
|
const override = process.env.NEAT_HOME;
|
|
13738
|
-
if (override && override.length > 0) return
|
|
13739
|
-
return
|
|
14339
|
+
if (override && override.length > 0) return path63.resolve(override);
|
|
14340
|
+
return path63.join(os2.homedir(), ".neat");
|
|
13740
14341
|
}
|
|
13741
14342
|
function registryPath() {
|
|
13742
|
-
return
|
|
14343
|
+
return path63.join(neatHome(), "projects.json");
|
|
13743
14344
|
}
|
|
13744
14345
|
function registryLockPath() {
|
|
13745
|
-
return
|
|
14346
|
+
return path63.join(neatHome(), "projects.json.lock");
|
|
13746
14347
|
}
|
|
13747
14348
|
function daemonPidPath() {
|
|
13748
|
-
return
|
|
14349
|
+
return path63.join(neatHome(), "neatd.pid");
|
|
13749
14350
|
}
|
|
13750
14351
|
function daemonsDir() {
|
|
13751
|
-
return
|
|
14352
|
+
return path63.join(neatHome(), "daemons");
|
|
13752
14353
|
}
|
|
13753
14354
|
function isFiniteInt(v) {
|
|
13754
14355
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -13781,7 +14382,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
13781
14382
|
const dir = daemonsDir();
|
|
13782
14383
|
let names;
|
|
13783
14384
|
try {
|
|
13784
|
-
names = await
|
|
14385
|
+
names = await fs30.readdir(dir);
|
|
13785
14386
|
} catch (err) {
|
|
13786
14387
|
if (err.code === "ENOENT") return [];
|
|
13787
14388
|
throw err;
|
|
@@ -13789,10 +14390,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
13789
14390
|
const out = [];
|
|
13790
14391
|
for (const name of names) {
|
|
13791
14392
|
if (!name.endsWith(".json")) continue;
|
|
13792
|
-
const file =
|
|
14393
|
+
const file = path63.join(dir, name);
|
|
13793
14394
|
let raw;
|
|
13794
14395
|
try {
|
|
13795
|
-
raw = await
|
|
14396
|
+
raw = await fs30.readFile(file, "utf8");
|
|
13796
14397
|
} catch {
|
|
13797
14398
|
continue;
|
|
13798
14399
|
}
|
|
@@ -13805,7 +14406,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
13805
14406
|
return out;
|
|
13806
14407
|
}
|
|
13807
14408
|
async function removeDaemonRecord(source) {
|
|
13808
|
-
await
|
|
14409
|
+
await fs30.unlink(source).catch(() => {
|
|
13809
14410
|
});
|
|
13810
14411
|
}
|
|
13811
14412
|
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
@@ -13862,7 +14463,7 @@ function isPidAliveDefault(pid) {
|
|
|
13862
14463
|
}
|
|
13863
14464
|
async function readPidFile(file) {
|
|
13864
14465
|
try {
|
|
13865
|
-
const raw = await
|
|
14466
|
+
const raw = await fs30.readFile(file, "utf8");
|
|
13866
14467
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
13867
14468
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
13868
14469
|
} catch {
|
|
@@ -13910,32 +14511,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
13910
14511
|
}
|
|
13911
14512
|
}
|
|
13912
14513
|
async function normalizeProjectPath(input) {
|
|
13913
|
-
const resolved =
|
|
14514
|
+
const resolved = path63.resolve(input);
|
|
13914
14515
|
try {
|
|
13915
|
-
return await
|
|
14516
|
+
return await fs30.realpath(resolved);
|
|
13916
14517
|
} catch {
|
|
13917
14518
|
return resolved;
|
|
13918
14519
|
}
|
|
13919
14520
|
}
|
|
13920
14521
|
async function writeAtomically(target, contents) {
|
|
13921
|
-
await
|
|
14522
|
+
await fs30.mkdir(path63.dirname(target), { recursive: true });
|
|
13922
14523
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
13923
|
-
const fd = await
|
|
14524
|
+
const fd = await fs30.open(tmp, "w");
|
|
13924
14525
|
try {
|
|
13925
14526
|
await fd.writeFile(contents, "utf8");
|
|
13926
14527
|
await fd.sync();
|
|
13927
14528
|
} finally {
|
|
13928
14529
|
await fd.close();
|
|
13929
14530
|
}
|
|
13930
|
-
await
|
|
14531
|
+
await fs30.rename(tmp, target);
|
|
13931
14532
|
}
|
|
13932
14533
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
13933
14534
|
const deadline = Date.now() + timeoutMs;
|
|
13934
|
-
await
|
|
14535
|
+
await fs30.mkdir(path63.dirname(lockPath), { recursive: true });
|
|
13935
14536
|
let probedHolder = false;
|
|
13936
14537
|
while (true) {
|
|
13937
14538
|
try {
|
|
13938
|
-
const fd = await
|
|
14539
|
+
const fd = await fs30.open(lockPath, "wx");
|
|
13939
14540
|
try {
|
|
13940
14541
|
await fd.writeFile(`${process.pid}
|
|
13941
14542
|
`, "utf8");
|
|
@@ -13960,7 +14561,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
13960
14561
|
}
|
|
13961
14562
|
}
|
|
13962
14563
|
async function releaseLock(lockPath) {
|
|
13963
|
-
await
|
|
14564
|
+
await fs30.unlink(lockPath).catch(() => {
|
|
13964
14565
|
});
|
|
13965
14566
|
}
|
|
13966
14567
|
async function withLock(fn) {
|
|
@@ -13976,7 +14577,7 @@ async function readRegistry() {
|
|
|
13976
14577
|
const file = registryPath();
|
|
13977
14578
|
let raw;
|
|
13978
14579
|
try {
|
|
13979
|
-
raw = await
|
|
14580
|
+
raw = await fs30.readFile(file, "utf8");
|
|
13980
14581
|
} catch (err) {
|
|
13981
14582
|
if (err.code === "ENOENT") {
|
|
13982
14583
|
return { version: 1, projects: [] };
|
|
@@ -14081,7 +14682,7 @@ function pruneTtlMs() {
|
|
|
14081
14682
|
}
|
|
14082
14683
|
async function statPathStatus(p) {
|
|
14083
14684
|
try {
|
|
14084
|
-
const stat = await
|
|
14685
|
+
const stat = await fs30.stat(p);
|
|
14085
14686
|
return stat.isDirectory() ? "present" : "unknown";
|
|
14086
14687
|
} catch (err) {
|
|
14087
14688
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -14126,14 +14727,14 @@ import cors from "@fastify/cors";
|
|
|
14126
14727
|
import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } from "@neat.is/types";
|
|
14127
14728
|
|
|
14128
14729
|
// src/extend/index.ts
|
|
14129
|
-
import { promises as
|
|
14130
|
-
import
|
|
14730
|
+
import { promises as fs32 } from "fs";
|
|
14731
|
+
import path65 from "path";
|
|
14131
14732
|
import os3 from "os";
|
|
14132
14733
|
import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
|
|
14133
14734
|
|
|
14134
14735
|
// src/installers/package-manager.ts
|
|
14135
|
-
import { promises as
|
|
14136
|
-
import
|
|
14736
|
+
import { promises as fs31 } from "fs";
|
|
14737
|
+
import path64 from "path";
|
|
14137
14738
|
import { spawn } from "child_process";
|
|
14138
14739
|
var LOCKFILE_PRIORITY = [
|
|
14139
14740
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -14148,29 +14749,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
14148
14749
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
14149
14750
|
async function exists2(p) {
|
|
14150
14751
|
try {
|
|
14151
|
-
await
|
|
14752
|
+
await fs31.access(p);
|
|
14152
14753
|
return true;
|
|
14153
14754
|
} catch {
|
|
14154
14755
|
return false;
|
|
14155
14756
|
}
|
|
14156
14757
|
}
|
|
14157
14758
|
async function detectPackageManager(serviceDir) {
|
|
14158
|
-
let dir =
|
|
14759
|
+
let dir = path64.resolve(serviceDir);
|
|
14159
14760
|
const stops = /* @__PURE__ */ new Set();
|
|
14160
14761
|
for (let i = 0; i < 64; i++) {
|
|
14161
14762
|
if (stops.has(dir)) break;
|
|
14162
14763
|
stops.add(dir);
|
|
14163
14764
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
14164
|
-
const lockPath =
|
|
14765
|
+
const lockPath = path64.join(dir, candidate.lockfile);
|
|
14165
14766
|
if (await exists2(lockPath)) {
|
|
14166
14767
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
14167
14768
|
}
|
|
14168
14769
|
}
|
|
14169
|
-
const parent =
|
|
14770
|
+
const parent = path64.dirname(dir);
|
|
14170
14771
|
if (parent === dir) break;
|
|
14171
14772
|
dir = parent;
|
|
14172
14773
|
}
|
|
14173
|
-
return { pm: "npm", cwd:
|
|
14774
|
+
return { pm: "npm", cwd: path64.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
14174
14775
|
}
|
|
14175
14776
|
async function runPackageManagerInstall(cmd) {
|
|
14176
14777
|
return new Promise((resolve) => {
|
|
@@ -14212,15 +14813,15 @@ ${err.message}`
|
|
|
14212
14813
|
// src/extend/index.ts
|
|
14213
14814
|
async function fileExists2(p) {
|
|
14214
14815
|
try {
|
|
14215
|
-
await
|
|
14816
|
+
await fs32.access(p);
|
|
14216
14817
|
return true;
|
|
14217
14818
|
} catch {
|
|
14218
14819
|
return false;
|
|
14219
14820
|
}
|
|
14220
14821
|
}
|
|
14221
14822
|
async function readPackageJson(scanPath) {
|
|
14222
|
-
const pkgPath =
|
|
14223
|
-
const raw = await
|
|
14823
|
+
const pkgPath = path65.join(scanPath, "package.json");
|
|
14824
|
+
const raw = await fs32.readFile(pkgPath, "utf8");
|
|
14224
14825
|
return JSON.parse(raw);
|
|
14225
14826
|
}
|
|
14226
14827
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -14234,15 +14835,15 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
14234
14835
|
async function findHookFiles(scanPath) {
|
|
14235
14836
|
const found = [];
|
|
14236
14837
|
const walk9 = async (dir) => {
|
|
14237
|
-
const entries = await
|
|
14838
|
+
const entries = await fs32.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
14238
14839
|
for (const entry of entries) {
|
|
14239
14840
|
if (entry.isDirectory()) {
|
|
14240
14841
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
14241
|
-
await walk9(
|
|
14842
|
+
await walk9(path65.join(dir, entry.name));
|
|
14242
14843
|
} else if (entry.isFile()) {
|
|
14243
14844
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
14244
|
-
const rel =
|
|
14245
|
-
found.push(rel.split(
|
|
14845
|
+
const rel = path65.relative(scanPath, path65.join(dir, entry.name));
|
|
14846
|
+
found.push(rel.split(path65.sep).join("/"));
|
|
14246
14847
|
}
|
|
14247
14848
|
}
|
|
14248
14849
|
}
|
|
@@ -14253,7 +14854,7 @@ async function findHookFiles(scanPath) {
|
|
|
14253
14854
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
14254
14855
|
let fallback = null;
|
|
14255
14856
|
for (const file of hookFiles) {
|
|
14256
|
-
const content = await
|
|
14857
|
+
const content = await fs32.readFile(path65.join(scanPath, file), "utf8");
|
|
14257
14858
|
const patched = splicedContent(content, snippet2);
|
|
14258
14859
|
if (patched !== null) return { file, content, patched };
|
|
14259
14860
|
if (fallback === null) fallback = { file, content };
|
|
@@ -14261,12 +14862,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
14261
14862
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
14262
14863
|
}
|
|
14263
14864
|
function extendLogPath() {
|
|
14264
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
14865
|
+
return process.env.NEAT_EXTEND_LOG ?? path65.join(os3.homedir(), ".neat", "extend-log.ndjson");
|
|
14265
14866
|
}
|
|
14266
14867
|
async function appendExtendLog(entry) {
|
|
14267
14868
|
const logPath = extendLogPath();
|
|
14268
|
-
await
|
|
14269
|
-
await
|
|
14869
|
+
await fs32.mkdir(path65.dirname(logPath), { recursive: true });
|
|
14870
|
+
await fs32.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
14270
14871
|
}
|
|
14271
14872
|
function splicedContent(fileContent, snippet2) {
|
|
14272
14873
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -14324,7 +14925,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
14324
14925
|
}
|
|
14325
14926
|
async function describeProjectInstrumentation(ctx) {
|
|
14326
14927
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
14327
|
-
const envNeat = await fileExists2(
|
|
14928
|
+
const envNeat = await fileExists2(path65.join(ctx.scanPath, ".env.neat"));
|
|
14328
14929
|
const registryInstrPackages = new Set(
|
|
14329
14930
|
registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
14330
14931
|
);
|
|
@@ -14346,7 +14947,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
14346
14947
|
);
|
|
14347
14948
|
}
|
|
14348
14949
|
for (const file of hookFiles) {
|
|
14349
|
-
const content = await
|
|
14950
|
+
const content = await fs32.readFile(path65.join(ctx.scanPath, file), "utf8");
|
|
14350
14951
|
if (content.includes(args.registration_snippet)) {
|
|
14351
14952
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
14352
14953
|
}
|
|
@@ -14358,18 +14959,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
14358
14959
|
);
|
|
14359
14960
|
}
|
|
14360
14961
|
const primaryFile = primary.file;
|
|
14361
|
-
const primaryPath =
|
|
14962
|
+
const primaryPath = path65.join(ctx.scanPath, primaryFile);
|
|
14362
14963
|
const filesTouched = [];
|
|
14363
14964
|
const depsAdded = [];
|
|
14364
|
-
const pkgPath =
|
|
14965
|
+
const pkgPath = path65.join(ctx.scanPath, "package.json");
|
|
14365
14966
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
14366
14967
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
14367
14968
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
14368
|
-
await
|
|
14969
|
+
await fs32.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
14369
14970
|
filesTouched.push("package.json");
|
|
14370
14971
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
14371
14972
|
}
|
|
14372
|
-
await
|
|
14973
|
+
await fs32.writeFile(primaryPath, primary.patched, "utf8");
|
|
14373
14974
|
filesTouched.push(primaryFile);
|
|
14374
14975
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
14375
14976
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -14400,7 +15001,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
14400
15001
|
};
|
|
14401
15002
|
}
|
|
14402
15003
|
for (const file of hookFiles) {
|
|
14403
|
-
const content = await
|
|
15004
|
+
const content = await fs32.readFile(path65.join(ctx.scanPath, file), "utf8");
|
|
14404
15005
|
if (content.includes(args.registration_snippet)) {
|
|
14405
15006
|
return {
|
|
14406
15007
|
library: args.library,
|
|
@@ -14435,28 +15036,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
14435
15036
|
if (!await fileExists2(logPath)) {
|
|
14436
15037
|
return { undone: false, message: "no apply found for library" };
|
|
14437
15038
|
}
|
|
14438
|
-
const raw = await
|
|
15039
|
+
const raw = await fs32.readFile(logPath, "utf8");
|
|
14439
15040
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
14440
15041
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
14441
15042
|
if (!match) {
|
|
14442
15043
|
return { undone: false, message: "no apply found for library" };
|
|
14443
15044
|
}
|
|
14444
|
-
const pkgPath =
|
|
15045
|
+
const pkgPath = path65.join(ctx.scanPath, "package.json");
|
|
14445
15046
|
if (await fileExists2(pkgPath)) {
|
|
14446
15047
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
14447
15048
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
14448
15049
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
14449
15050
|
pkg.dependencies = rest;
|
|
14450
|
-
await
|
|
15051
|
+
await fs32.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
14451
15052
|
}
|
|
14452
15053
|
}
|
|
14453
15054
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
14454
15055
|
for (const file of hookFiles) {
|
|
14455
|
-
const filePath =
|
|
14456
|
-
const content = await
|
|
15056
|
+
const filePath = path65.join(ctx.scanPath, file);
|
|
15057
|
+
const content = await fs32.readFile(filePath, "utf8");
|
|
14457
15058
|
if (content.includes(match.registration_snippet)) {
|
|
14458
15059
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
14459
|
-
await
|
|
15060
|
+
await fs32.writeFile(filePath, filtered, "utf8");
|
|
14460
15061
|
break;
|
|
14461
15062
|
}
|
|
14462
15063
|
}
|
|
@@ -14567,8 +15168,8 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
14567
15168
|
|
|
14568
15169
|
// src/connectors-config.ts
|
|
14569
15170
|
import os4 from "os";
|
|
14570
|
-
import
|
|
14571
|
-
import { promises as
|
|
15171
|
+
import path66 from "path";
|
|
15172
|
+
import { promises as fs33 } from "fs";
|
|
14572
15173
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
14573
15174
|
var EnvRefUnsetError = class extends Error {
|
|
14574
15175
|
ref;
|
|
@@ -14582,17 +15183,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
14582
15183
|
};
|
|
14583
15184
|
function neatHome2() {
|
|
14584
15185
|
const override = process.env.NEAT_HOME;
|
|
14585
|
-
if (override && override.length > 0) return
|
|
14586
|
-
return
|
|
15186
|
+
if (override && override.length > 0) return path66.resolve(override);
|
|
15187
|
+
return path66.join(os4.homedir(), ".neat");
|
|
14587
15188
|
}
|
|
14588
15189
|
function connectorsConfigPath(home = neatHome2()) {
|
|
14589
|
-
return
|
|
15190
|
+
return path66.join(home, "connectors.json");
|
|
14590
15191
|
}
|
|
14591
15192
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
14592
15193
|
async function warnIfModeLooserThan0600(file) {
|
|
14593
15194
|
if (process.platform === "win32") return;
|
|
14594
15195
|
try {
|
|
14595
|
-
const stat = await
|
|
15196
|
+
const stat = await fs33.stat(file);
|
|
14596
15197
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
14597
15198
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
14598
15199
|
console.warn(
|
|
@@ -14606,7 +15207,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
14606
15207
|
const file = connectorsConfigPath(home);
|
|
14607
15208
|
let raw;
|
|
14608
15209
|
try {
|
|
14609
|
-
raw = await
|
|
15210
|
+
raw = await fs33.readFile(file, "utf8");
|
|
14610
15211
|
} catch (err) {
|
|
14611
15212
|
if (err.code === "ENOENT") {
|
|
14612
15213
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -14717,7 +15318,7 @@ function connectorMatchesProject(entry, project) {
|
|
|
14717
15318
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
14718
15319
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
14719
15320
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
14720
|
-
return
|
|
15321
|
+
return path66.join(home, "connectors.json.lock");
|
|
14721
15322
|
}
|
|
14722
15323
|
function isEnvRef(value) {
|
|
14723
15324
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -14730,9 +15331,9 @@ function redactCredentialRef(ref) {
|
|
|
14730
15331
|
return out;
|
|
14731
15332
|
}
|
|
14732
15333
|
async function writeConfigAtomically0600(file, contents) {
|
|
14733
|
-
await
|
|
15334
|
+
await fs33.mkdir(path66.dirname(file), { recursive: true });
|
|
14734
15335
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
14735
|
-
const fd = await
|
|
15336
|
+
const fd = await fs33.open(tmp, "w", 384);
|
|
14736
15337
|
try {
|
|
14737
15338
|
await fd.writeFile(contents, "utf8");
|
|
14738
15339
|
await fd.chmod(384);
|
|
@@ -14740,14 +15341,14 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
14740
15341
|
} finally {
|
|
14741
15342
|
await fd.close();
|
|
14742
15343
|
}
|
|
14743
|
-
await
|
|
15344
|
+
await fs33.rename(tmp, file);
|
|
14744
15345
|
}
|
|
14745
15346
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
14746
15347
|
const deadline = Date.now() + timeoutMs;
|
|
14747
|
-
await
|
|
15348
|
+
await fs33.mkdir(path66.dirname(lockPath), { recursive: true });
|
|
14748
15349
|
for (; ; ) {
|
|
14749
15350
|
try {
|
|
14750
|
-
const fd = await
|
|
15351
|
+
const fd = await fs33.open(lockPath, "wx");
|
|
14751
15352
|
try {
|
|
14752
15353
|
await fd.writeFile(`${process.pid}
|
|
14753
15354
|
`, "utf8");
|
|
@@ -14767,7 +15368,7 @@ async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEO
|
|
|
14767
15368
|
}
|
|
14768
15369
|
}
|
|
14769
15370
|
async function releaseConnectorsLock(lockPath) {
|
|
14770
|
-
await
|
|
15371
|
+
await fs33.unlink(lockPath).catch(() => {
|
|
14771
15372
|
});
|
|
14772
15373
|
}
|
|
14773
15374
|
async function withConnectorsLock(home, fn) {
|
|
@@ -14892,7 +15493,7 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
14892
15493
|
}
|
|
14893
15494
|
|
|
14894
15495
|
// src/connectors/index.ts
|
|
14895
|
-
import { NodeType as
|
|
15496
|
+
import { NodeType as NodeType29, parseFileId as parseFileId2, Provenance as Provenance26 } from "@neat.is/types";
|
|
14896
15497
|
var NO_ENV = "unknown";
|
|
14897
15498
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
14898
15499
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
@@ -14911,7 +15512,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
14911
15512
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
14912
15513
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
14913
15514
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
14914
|
-
if (attrs.type !==
|
|
15515
|
+
if (attrs.type !== NodeType29.RouteNode || !attrs.path) return void 0;
|
|
14915
15516
|
const site = { relPath: attrs.path };
|
|
14916
15517
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
14917
15518
|
return site;
|
|
@@ -15396,10 +15997,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
15396
15997
|
// src/connectors/supabase/map.ts
|
|
15397
15998
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
15398
15999
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
15399
|
-
function targetFromRestPath(
|
|
15400
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
16000
|
+
function targetFromRestPath(path67) {
|
|
16001
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path67);
|
|
15401
16002
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
15402
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
16003
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path67);
|
|
15403
16004
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
15404
16005
|
return null;
|
|
15405
16006
|
}
|
|
@@ -15616,7 +16217,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
15616
16217
|
}
|
|
15617
16218
|
|
|
15618
16219
|
// src/connectors/railway/index.ts
|
|
15619
|
-
import { EdgeType as EdgeType27, NodeType as
|
|
16220
|
+
import { EdgeType as EdgeType27, NodeType as NodeType30, serviceId as serviceId11 } from "@neat.is/types";
|
|
15620
16221
|
|
|
15621
16222
|
// src/connectors/railway/client.ts
|
|
15622
16223
|
var DEFAULT_RAILWAY_API_URL = "https://backboard.railway.com/graphql/v2";
|
|
@@ -15766,7 +16367,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
15766
16367
|
const out = [];
|
|
15767
16368
|
graph.forEachNode((_id, attrs) => {
|
|
15768
16369
|
const node = attrs;
|
|
15769
|
-
if (node.type !==
|
|
16370
|
+
if (node.type !== NodeType30.RouteNode) return;
|
|
15770
16371
|
const route = attrs;
|
|
15771
16372
|
if (route.service !== serviceName) return;
|
|
15772
16373
|
out.push({
|
|
@@ -15875,7 +16476,7 @@ function createRailwayResolveTarget(config) {
|
|
|
15875
16476
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
15876
16477
|
const peerName = config.serviceNameById[signal.targetName];
|
|
15877
16478
|
if (!peerName) return null;
|
|
15878
|
-
return { targetNodeId:
|
|
16479
|
+
return { targetNodeId: serviceId11(peerName), serviceName, edgeType: EdgeType27.CONNECTS_TO };
|
|
15879
16480
|
}
|
|
15880
16481
|
return null;
|
|
15881
16482
|
};
|
|
@@ -15994,9 +16595,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
15994
16595
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
15995
16596
|
if (secondSep === -1) return null;
|
|
15996
16597
|
const method = rest.slice(0, secondSep);
|
|
15997
|
-
const
|
|
15998
|
-
if (!resourceName || !method || !
|
|
15999
|
-
return { resourceName, method, path:
|
|
16598
|
+
const path67 = rest.slice(secondSep + 1);
|
|
16599
|
+
if (!resourceName || !method || !path67) return null;
|
|
16600
|
+
return { resourceName, method, path: path67 };
|
|
16000
16601
|
}
|
|
16001
16602
|
function resourceNameFor(type, labels) {
|
|
16002
16603
|
if (!labels) return null;
|
|
@@ -16034,14 +16635,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
16034
16635
|
if (!req) return null;
|
|
16035
16636
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
16036
16637
|
const method = req.requestMethod.toUpperCase();
|
|
16037
|
-
const
|
|
16038
|
-
if (
|
|
16638
|
+
const path67 = pathFromRequestUrl(req.requestUrl);
|
|
16639
|
+
if (path67 === null) return null;
|
|
16039
16640
|
const timestamp = entry.timestamp;
|
|
16040
16641
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
16041
16642
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
16042
16643
|
return {
|
|
16043
16644
|
targetKind: resourceType,
|
|
16044
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
16645
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path67 }),
|
|
16045
16646
|
callCount: 1,
|
|
16046
16647
|
errorCount: isError ? 1 : 0,
|
|
16047
16648
|
lastObservedIso: timestamp
|
|
@@ -16057,7 +16658,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
16057
16658
|
}
|
|
16058
16659
|
|
|
16059
16660
|
// src/connectors/firebase/resolve.ts
|
|
16060
|
-
import { NodeType as
|
|
16661
|
+
import { NodeType as NodeType31, EdgeType as EdgeType28 } from "@neat.is/types";
|
|
16061
16662
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
16062
16663
|
switch (resourceType) {
|
|
16063
16664
|
case "cloud_function":
|
|
@@ -16072,7 +16673,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
16072
16673
|
const entries = [];
|
|
16073
16674
|
graph.forEachNode((_id, attrs) => {
|
|
16074
16675
|
const node = attrs;
|
|
16075
|
-
if (node.type !==
|
|
16676
|
+
if (node.type !== NodeType31.RouteNode) return;
|
|
16076
16677
|
const route = attrs;
|
|
16077
16678
|
if (route.service !== serviceName) return;
|
|
16078
16679
|
entries.push({
|
|
@@ -16127,7 +16728,7 @@ function createFirebaseConnector(graph, serviceMap) {
|
|
|
16127
16728
|
}
|
|
16128
16729
|
|
|
16129
16730
|
// src/connectors/cloudflare/connector.ts
|
|
16130
|
-
import { EdgeType as EdgeType29, NodeType as
|
|
16731
|
+
import { EdgeType as EdgeType29, NodeType as NodeType32, fileId as fileId5, infraId as infraId21 } from "@neat.is/types";
|
|
16131
16732
|
|
|
16132
16733
|
// src/connectors/cloudflare/client.ts
|
|
16133
16734
|
import { randomUUID } from "crypto";
|
|
@@ -16238,7 +16839,7 @@ function mapEventToSignal(event) {
|
|
|
16238
16839
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
16239
16840
|
const statusCode = metadata?.statusCode;
|
|
16240
16841
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
16241
|
-
const
|
|
16842
|
+
const path67 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
16242
16843
|
return {
|
|
16243
16844
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
16244
16845
|
targetName: scriptName,
|
|
@@ -16246,7 +16847,7 @@ function mapEventToSignal(event) {
|
|
|
16246
16847
|
errorCount: isError ? 1 : 0,
|
|
16247
16848
|
lastObservedIso: observedAt.toISOString(),
|
|
16248
16849
|
method,
|
|
16249
|
-
...
|
|
16850
|
+
...path67 ? { path: path67 } : {},
|
|
16250
16851
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
16251
16852
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
16252
16853
|
};
|
|
@@ -16286,19 +16887,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
16286
16887
|
graph.forEachNode((id, attrs) => {
|
|
16287
16888
|
if (found) return;
|
|
16288
16889
|
const a = attrs;
|
|
16289
|
-
if (a.type ===
|
|
16890
|
+
if (a.type === NodeType32.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
16290
16891
|
found = id;
|
|
16291
16892
|
}
|
|
16292
16893
|
});
|
|
16293
16894
|
return found;
|
|
16294
16895
|
}
|
|
16295
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
16296
|
-
const normalizedPath = normalizePathTemplate(
|
|
16896
|
+
function findMatchingRouteNode(graph, serviceName, method, path67) {
|
|
16897
|
+
const normalizedPath = normalizePathTemplate(path67);
|
|
16297
16898
|
let found = null;
|
|
16298
16899
|
graph.forEachNode((id, attrs) => {
|
|
16299
16900
|
if (found) return;
|
|
16300
16901
|
const a = attrs;
|
|
16301
|
-
if (a.type !==
|
|
16902
|
+
if (a.type !== NodeType32.RouteNode || a.service !== serviceName) return;
|
|
16302
16903
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
16303
16904
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
16304
16905
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -16310,10 +16911,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
16310
16911
|
return (signal) => {
|
|
16311
16912
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
16312
16913
|
const scriptName = signal.targetName;
|
|
16313
|
-
const { method, path:
|
|
16914
|
+
const { method, path: path67 } = signal;
|
|
16314
16915
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
16315
|
-
if (!method || !
|
|
16316
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
16916
|
+
if (!method || !path67) return wholeFileId;
|
|
16917
|
+
return findMatchingRouteNode(graph, serviceName, method, path67) ?? wholeFileId;
|
|
16317
16918
|
};
|
|
16318
16919
|
const mapping = config.workers?.[scriptName];
|
|
16319
16920
|
if (mapping) {
|
|
@@ -16643,9 +17244,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
16643
17244
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
16644
17245
|
if (secondSep === -1) return null;
|
|
16645
17246
|
const method = rest.slice(0, secondSep);
|
|
16646
|
-
const
|
|
16647
|
-
if (!serviceName || !method || !
|
|
16648
|
-
return { serviceName, method, path:
|
|
17247
|
+
const path67 = rest.slice(secondSep + 1);
|
|
17248
|
+
if (!serviceName || !method || !path67) return null;
|
|
17249
|
+
return { serviceName, method, path: path67 };
|
|
16649
17250
|
}
|
|
16650
17251
|
|
|
16651
17252
|
// src/connectors/cloud-run/map.ts
|
|
@@ -16674,14 +17275,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
16674
17275
|
if (!req) return null;
|
|
16675
17276
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
16676
17277
|
const method = req.requestMethod.toUpperCase();
|
|
16677
|
-
const
|
|
16678
|
-
if (
|
|
17278
|
+
const path67 = pathFromRequestUrl2(req.requestUrl);
|
|
17279
|
+
if (path67 === null) return null;
|
|
16679
17280
|
const timestamp = entry.timestamp;
|
|
16680
17281
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
16681
17282
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
16682
17283
|
return {
|
|
16683
17284
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
16684
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
17285
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path67 }),
|
|
16685
17286
|
callCount: 1,
|
|
16686
17287
|
errorCount: isError ? 1 : 0,
|
|
16687
17288
|
lastObservedIso: timestamp
|
|
@@ -16697,14 +17298,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
16697
17298
|
}
|
|
16698
17299
|
|
|
16699
17300
|
// src/connectors/cloud-run/resolve.ts
|
|
16700
|
-
import { EdgeType as EdgeType31, NodeType as
|
|
17301
|
+
import { EdgeType as EdgeType31, NodeType as NodeType33, infraId as infraId23 } from "@neat.is/types";
|
|
16701
17302
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
16702
17303
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
16703
17304
|
let found = null;
|
|
16704
17305
|
graph.forEachNode((_id, attrs) => {
|
|
16705
17306
|
if (found) return;
|
|
16706
17307
|
const node = attrs;
|
|
16707
|
-
if (node.type !==
|
|
17308
|
+
if (node.type !== NodeType33.RouteNode) return;
|
|
16708
17309
|
const route = attrs;
|
|
16709
17310
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
16710
17311
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -16719,14 +17320,14 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
16719
17320
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
16720
17321
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
16721
17322
|
if (!identity) return null;
|
|
16722
|
-
const { serviceName: gcpServiceName, method, path:
|
|
17323
|
+
const { serviceName: gcpServiceName, method, path: path67 } = identity;
|
|
16723
17324
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
16724
17325
|
if (mappedService) {
|
|
16725
17326
|
const routeNodeId = findMatchingRouteNode2(
|
|
16726
17327
|
graph,
|
|
16727
17328
|
mappedService,
|
|
16728
17329
|
method,
|
|
16729
|
-
normalizePathTemplate(
|
|
17330
|
+
normalizePathTemplate(path67)
|
|
16730
17331
|
);
|
|
16731
17332
|
if (routeNodeId) {
|
|
16732
17333
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType31.CALLS };
|
|
@@ -16775,7 +17376,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
16775
17376
|
}
|
|
16776
17377
|
|
|
16777
17378
|
// src/connectors/render/index.ts
|
|
16778
|
-
import { EdgeType as EdgeType32, NodeType as
|
|
17379
|
+
import { EdgeType as EdgeType32, NodeType as NodeType34 } from "@neat.is/types";
|
|
16779
17380
|
|
|
16780
17381
|
// src/connectors/render/types.ts
|
|
16781
17382
|
function readRenderToken(credentials) {
|
|
@@ -16851,7 +17452,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
16851
17452
|
const out = [];
|
|
16852
17453
|
graph.forEachNode((_id, attrs) => {
|
|
16853
17454
|
const node = attrs;
|
|
16854
|
-
if (node.type !==
|
|
17455
|
+
if (node.type !== NodeType34.RouteNode) return;
|
|
16855
17456
|
const route = attrs;
|
|
16856
17457
|
if (route.service !== serviceName) return;
|
|
16857
17458
|
out.push({
|
|
@@ -17335,7 +17936,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
17335
17936
|
}
|
|
17336
17937
|
|
|
17337
17938
|
// src/connectors/eas/resolve.ts
|
|
17338
|
-
import { EdgeType as EdgeType34, NodeType as
|
|
17939
|
+
import { EdgeType as EdgeType34, NodeType as NodeType35, parseFileId as parseFileId3 } from "@neat.is/types";
|
|
17339
17940
|
var NO_ENV2 = "unknown";
|
|
17340
17941
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
17341
17942
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -17363,7 +17964,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
17363
17964
|
graph.forEachNode((id, attrs) => {
|
|
17364
17965
|
if (scoped) return;
|
|
17365
17966
|
const node = attrs;
|
|
17366
|
-
if (node.type !==
|
|
17967
|
+
if (node.type !== NodeType35.ConfigNode) return;
|
|
17367
17968
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
17368
17969
|
if (anyMatch === null) anyMatch = id;
|
|
17369
17970
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -18975,4 +19576,4 @@ export {
|
|
|
18975
19576
|
deprovisionConnector,
|
|
18976
19577
|
buildApi
|
|
18977
19578
|
};
|
|
18978
|
-
//# sourceMappingURL=chunk-
|
|
19579
|
+
//# sourceMappingURL=chunk-T4UGEMWN.js.map
|