@neat.is/core 0.7.6 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6H757ZNM.js → chunk-4GCV46AP.js} +2 -2
- package/dist/{chunk-XT4NNFH6.js → chunk-UI3AFJAF.js} +765 -216
- package/dist/chunk-UI3AFJAF.js.map +1 -0
- package/dist/{chunk-P2ZEKJ35.js → chunk-Y43UCVZS.js} +9 -6
- package/dist/chunk-Y43UCVZS.js.map +1 -0
- package/dist/{chunk-XHSK4BGL.js → chunk-YVZRBT4C.js} +3 -3
- package/dist/cli.cjs +1143 -590
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +945 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +954 -401
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-APVZSB6W.js → otel-grpc-DPSAGRUU.js} +3 -3
- package/dist/server.cjs +877 -324
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-P2ZEKJ35.js.map +0 -1
- package/dist/chunk-XT4NNFH6.js.map +0 -1
- /package/dist/{chunk-6H757ZNM.js.map → chunk-4GCV46AP.js.map} +0 -0
- /package/dist/{chunk-XHSK4BGL.js.map → chunk-YVZRBT4C.js.map} +0 -0
- /package/dist/{otel-grpc-APVZSB6W.js.map → otel-grpc-DPSAGRUU.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
mountBearerAuth,
|
|
4
4
|
readAuthEnv,
|
|
5
5
|
tableFromSqlStatement
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Y43UCVZS.js";
|
|
7
7
|
|
|
8
8
|
// src/graph.ts
|
|
9
9
|
import GraphDefault from "graphology";
|
|
@@ -1247,14 +1247,14 @@ function buildServiceHostIndex(services) {
|
|
|
1247
1247
|
}
|
|
1248
1248
|
async function walkSourceFiles(dir) {
|
|
1249
1249
|
const out = [];
|
|
1250
|
-
async function
|
|
1250
|
+
async function walk9(current) {
|
|
1251
1251
|
const entries = await fs5.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
1252
1252
|
for (const entry of entries) {
|
|
1253
1253
|
const full = path5.join(current, entry.name);
|
|
1254
1254
|
if (entry.isDirectory()) {
|
|
1255
1255
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
1256
1256
|
if (await isPythonVenvDir(full)) continue;
|
|
1257
|
-
await
|
|
1257
|
+
await walk9(full);
|
|
1258
1258
|
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(path5.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
1259
1259
|
// would attribute our instrumentation imports to the user's service.
|
|
1260
1260
|
!isNeatAuthoredSourceFile(entry.name)) {
|
|
@@ -1262,7 +1262,7 @@ async function walkSourceFiles(dir) {
|
|
|
1262
1262
|
}
|
|
1263
1263
|
}
|
|
1264
1264
|
}
|
|
1265
|
-
await
|
|
1265
|
+
await walk9(dir);
|
|
1266
1266
|
return out;
|
|
1267
1267
|
}
|
|
1268
1268
|
async function loadSourceFiles(dir) {
|
|
@@ -1781,7 +1781,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
|
1781
1781
|
]);
|
|
1782
1782
|
var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
1783
1783
|
var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
1784
|
-
function
|
|
1784
|
+
function goRouterRoutesFromSource(source, parser, framework) {
|
|
1785
1785
|
const tree = parseSource2(parser, source);
|
|
1786
1786
|
const prefixes = /* @__PURE__ */ new Map();
|
|
1787
1787
|
const out = [];
|
|
@@ -1791,10 +1791,12 @@ function ginRoutesFromSource(source, parser) {
|
|
|
1791
1791
|
const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
|
|
1792
1792
|
if (name && value?.type === "call_expression") {
|
|
1793
1793
|
const fn2 = value.childForFieldName("function");
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1794
|
+
if (fn2?.childForFieldName("field")?.text === "Group") {
|
|
1795
|
+
const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
|
|
1796
|
+
if (leaf2 !== null) {
|
|
1797
|
+
const parent = fn2.childForFieldName("operand")?.text ?? "";
|
|
1798
|
+
prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
|
|
1799
|
+
}
|
|
1798
1800
|
}
|
|
1799
1801
|
}
|
|
1800
1802
|
return;
|
|
@@ -1805,18 +1807,32 @@ function ginRoutesFromSource(source, parser) {
|
|
|
1805
1807
|
const method = fn.childForFieldName("field")?.text?.toUpperCase();
|
|
1806
1808
|
if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
|
|
1807
1809
|
const receiver = fn.childForFieldName("operand")?.text ?? "";
|
|
1808
|
-
const
|
|
1809
|
-
if (
|
|
1810
|
-
const leaf = first.text.slice(1, -1);
|
|
1810
|
+
const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
1811
|
+
if (leaf === null) return;
|
|
1811
1812
|
out.push({
|
|
1812
|
-
method
|
|
1813
|
+
method,
|
|
1813
1814
|
pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
|
|
1814
1815
|
line: node.startPosition.row + 1,
|
|
1815
|
-
framework
|
|
1816
|
+
framework
|
|
1816
1817
|
});
|
|
1817
1818
|
});
|
|
1818
1819
|
return out;
|
|
1819
1820
|
}
|
|
1821
|
+
function goStringLiteral(node) {
|
|
1822
|
+
if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
|
|
1823
|
+
return node.text.slice(1, -1);
|
|
1824
|
+
}
|
|
1825
|
+
return null;
|
|
1826
|
+
}
|
|
1827
|
+
function ginRoutesFromSource(source, parser) {
|
|
1828
|
+
return goRouterRoutesFromSource(source, parser, "gin");
|
|
1829
|
+
}
|
|
1830
|
+
function echoRoutesFromSource(source, parser) {
|
|
1831
|
+
return goRouterRoutesFromSource(source, parser, "echo");
|
|
1832
|
+
}
|
|
1833
|
+
function fiberRoutesFromSource(source, parser) {
|
|
1834
|
+
return goRouterRoutesFromSource(source, parser, "fiber");
|
|
1835
|
+
}
|
|
1820
1836
|
var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
|
|
1821
1837
|
var NESTJS_METHODS = /* @__PURE__ */ new Map([
|
|
1822
1838
|
["Get", "GET"],
|
|
@@ -2401,9 +2417,9 @@ function rubyRocketRoute(args) {
|
|
|
2401
2417
|
if (!pair || pair.type !== "pair") continue;
|
|
2402
2418
|
const k = pair.childForFieldName("key");
|
|
2403
2419
|
if (k?.type !== "string") continue;
|
|
2404
|
-
const
|
|
2405
|
-
if (
|
|
2406
|
-
return { path:
|
|
2420
|
+
const path64 = rubyLiteral(k);
|
|
2421
|
+
if (path64 === null) continue;
|
|
2422
|
+
return { path: path64, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
2407
2423
|
}
|
|
2408
2424
|
return null;
|
|
2409
2425
|
}
|
|
@@ -3080,9 +3096,11 @@ async function addRoutes(graph, services) {
|
|
|
3080
3096
|
const hasFlask = deps["flask"] !== void 0;
|
|
3081
3097
|
const hasDjango = deps["django"] !== void 0;
|
|
3082
3098
|
const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
|
|
3099
|
+
const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
|
|
3100
|
+
const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
|
|
3083
3101
|
const hasRails = deps["rails"] !== void 0;
|
|
3084
3102
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
3085
|
-
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
|
|
3103
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
|
|
3086
3104
|
continue;
|
|
3087
3105
|
const files = await loadSourceFiles(service.dir);
|
|
3088
3106
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -3106,7 +3124,10 @@ async function addRoutes(graph, services) {
|
|
|
3106
3124
|
} else if (isRb) {
|
|
3107
3125
|
routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
|
|
3108
3126
|
} else if (isGo) {
|
|
3109
|
-
routes =
|
|
3127
|
+
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
3128
|
+
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
3129
|
+
else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
|
|
3130
|
+
else routes = [];
|
|
3110
3131
|
} else if (isPy) {
|
|
3111
3132
|
routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
|
|
3112
3133
|
if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
|
|
@@ -4740,19 +4761,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
4740
4761
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
4741
4762
|
let best = { path: [start], edges: [] };
|
|
4742
4763
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
4743
|
-
function step(node,
|
|
4744
|
-
if (
|
|
4745
|
-
best = { path: [...
|
|
4764
|
+
function step(node, path64, edges) {
|
|
4765
|
+
if (path64.length > best.path.length) {
|
|
4766
|
+
best = { path: [...path64], edges: [...edges] };
|
|
4746
4767
|
}
|
|
4747
|
-
if (
|
|
4768
|
+
if (path64.length - 1 >= maxDepth) return;
|
|
4748
4769
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
4749
4770
|
for (const [srcId, edge] of incoming) {
|
|
4750
4771
|
if (visited.has(srcId)) continue;
|
|
4751
4772
|
visited.add(srcId);
|
|
4752
|
-
|
|
4773
|
+
path64.push(srcId);
|
|
4753
4774
|
edges.push(edge);
|
|
4754
|
-
step(srcId,
|
|
4755
|
-
|
|
4775
|
+
step(srcId, path64, edges);
|
|
4776
|
+
path64.pop();
|
|
4756
4777
|
edges.pop();
|
|
4757
4778
|
visited.delete(srcId);
|
|
4758
4779
|
}
|
|
@@ -4760,11 +4781,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
4760
4781
|
step(start, [start], []);
|
|
4761
4782
|
return best;
|
|
4762
4783
|
}
|
|
4763
|
-
function databaseRootCauseShape(graph, origin,
|
|
4784
|
+
function databaseRootCauseShape(graph, origin, walk9) {
|
|
4764
4785
|
const targetDb = origin;
|
|
4765
4786
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
4766
4787
|
if (candidatePairs.length === 0) return null;
|
|
4767
|
-
for (const id of
|
|
4788
|
+
for (const id of walk9.path) {
|
|
4768
4789
|
const owner = resolveOwningService(graph, id);
|
|
4769
4790
|
if (!owner) continue;
|
|
4770
4791
|
const { id: serviceId9, svc } = owner;
|
|
@@ -4791,8 +4812,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
|
|
|
4791
4812
|
}
|
|
4792
4813
|
return null;
|
|
4793
4814
|
}
|
|
4794
|
-
function serviceRootCauseShape(graph, _origin,
|
|
4795
|
-
for (const id of
|
|
4815
|
+
function serviceRootCauseShape(graph, _origin, walk9) {
|
|
4816
|
+
for (const id of walk9.path) {
|
|
4796
4817
|
const owner = resolveOwningService(graph, id);
|
|
4797
4818
|
if (!owner) continue;
|
|
4798
4819
|
const { id: serviceId9, svc } = owner;
|
|
@@ -4828,15 +4849,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
|
|
|
4828
4849
|
}
|
|
4829
4850
|
return null;
|
|
4830
4851
|
}
|
|
4831
|
-
function fileRootCauseShape(graph, origin,
|
|
4852
|
+
function fileRootCauseShape(graph, origin, walk9) {
|
|
4832
4853
|
const owner = resolveOwningService(graph, origin.id);
|
|
4833
4854
|
if (!owner) return null;
|
|
4834
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
4855
|
+
return serviceRootCauseShape(graph, owner.svc, walk9);
|
|
4835
4856
|
}
|
|
4836
|
-
function symbolRootCauseShape(graph, origin,
|
|
4857
|
+
function symbolRootCauseShape(graph, origin, walk9) {
|
|
4837
4858
|
const owner = resolveOwningService(graph, origin.id);
|
|
4838
4859
|
if (!owner) return null;
|
|
4839
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
4860
|
+
return serviceRootCauseShape(graph, owner.svc, walk9);
|
|
4840
4861
|
}
|
|
4841
4862
|
var rootCauseShapes = {
|
|
4842
4863
|
[NodeType5.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -4849,16 +4870,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
4849
4870
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
4850
4871
|
const shape = rootCauseShapes[origin.type];
|
|
4851
4872
|
if (shape) {
|
|
4852
|
-
const
|
|
4853
|
-
const match = shape(graph, origin,
|
|
4873
|
+
const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
4874
|
+
const match = shape(graph, origin, walk9);
|
|
4854
4875
|
if (match) {
|
|
4855
4876
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
4856
4877
|
return RootCauseResultSchema.parse({
|
|
4857
4878
|
rootCauseNode: match.rootCauseNode,
|
|
4858
4879
|
rootCauseReason: reason,
|
|
4859
|
-
traversalPath:
|
|
4860
|
-
edgeProvenances:
|
|
4861
|
-
confidence: confidenceFromMix(
|
|
4880
|
+
traversalPath: walk9.path,
|
|
4881
|
+
edgeProvenances: walk9.edges.map((e) => e.provenance),
|
|
4882
|
+
confidence: confidenceFromMix(walk9.edges),
|
|
4862
4883
|
fixRecommendation: match.fixRecommendation
|
|
4863
4884
|
});
|
|
4864
4885
|
}
|
|
@@ -4959,26 +4980,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
|
|
|
4959
4980
|
return best;
|
|
4960
4981
|
}
|
|
4961
4982
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
4962
|
-
const
|
|
4983
|
+
const path64 = [originServiceId];
|
|
4963
4984
|
const edges = [];
|
|
4964
4985
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
4965
4986
|
let current = originServiceId;
|
|
4966
4987
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
4967
4988
|
const hop = dominantFailingCall(graph, current, visited);
|
|
4968
4989
|
if (!hop) break;
|
|
4969
|
-
|
|
4990
|
+
path64.push(hop.nextService);
|
|
4970
4991
|
edges.push(hop.edge);
|
|
4971
4992
|
visited.add(hop.nextService);
|
|
4972
4993
|
current = hop.nextService;
|
|
4973
4994
|
}
|
|
4974
4995
|
if (edges.length === 0) return null;
|
|
4975
|
-
return { path:
|
|
4996
|
+
return { path: path64, edges, culprit: current };
|
|
4976
4997
|
}
|
|
4977
4998
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
4978
4999
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
4979
5000
|
if (!chain) return null;
|
|
4980
5001
|
const culprit = chain.culprit;
|
|
4981
|
-
const
|
|
5002
|
+
const path64 = [...chain.path];
|
|
4982
5003
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
4983
5004
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
4984
5005
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -4986,14 +5007,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
4986
5007
|
if (loc) {
|
|
4987
5008
|
let rootCauseNode = culprit;
|
|
4988
5009
|
if (loc.fileNode) {
|
|
4989
|
-
|
|
5010
|
+
path64.push(loc.fileNode);
|
|
4990
5011
|
edgeProvenances.push(Provenance6.OBSERVED);
|
|
4991
5012
|
rootCauseNode = loc.fileNode;
|
|
4992
5013
|
}
|
|
4993
5014
|
return RootCauseResultSchema.parse({
|
|
4994
5015
|
rootCauseNode,
|
|
4995
5016
|
rootCauseReason: loc.rootCauseReason,
|
|
4996
|
-
traversalPath:
|
|
5017
|
+
traversalPath: path64,
|
|
4997
5018
|
edgeProvenances,
|
|
4998
5019
|
confidence,
|
|
4999
5020
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -5005,7 +5026,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
5005
5026
|
return RootCauseResultSchema.parse({
|
|
5006
5027
|
rootCauseNode: culprit,
|
|
5007
5028
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
5008
|
-
traversalPath:
|
|
5029
|
+
traversalPath: path64,
|
|
5009
5030
|
edgeProvenances,
|
|
5010
5031
|
confidence,
|
|
5011
5032
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -5243,6 +5264,12 @@ function parseGoMod(source) {
|
|
|
5243
5264
|
}
|
|
5244
5265
|
return { module, ...goVersion ? { goVersion } : {}, dependencies };
|
|
5245
5266
|
}
|
|
5267
|
+
function goFramework(deps) {
|
|
5268
|
+
if (deps["github.com/gin-gonic/gin"]) return "gin";
|
|
5269
|
+
if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
|
|
5270
|
+
if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
|
|
5271
|
+
return void 0;
|
|
5272
|
+
}
|
|
5246
5273
|
async function discoverGoService(scanPath, dir) {
|
|
5247
5274
|
let raw;
|
|
5248
5275
|
try {
|
|
@@ -5254,6 +5281,7 @@ async function discoverGoService(scanPath, dir) {
|
|
|
5254
5281
|
if (!mod) return null;
|
|
5255
5282
|
const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
|
|
5256
5283
|
const pkg = { name, dependencies: mod.dependencies };
|
|
5284
|
+
const framework = goFramework(mod.dependencies);
|
|
5257
5285
|
const node = {
|
|
5258
5286
|
id: serviceId2(name),
|
|
5259
5287
|
type: NodeType6.ServiceNode,
|
|
@@ -5261,7 +5289,7 @@ async function discoverGoService(scanPath, dir) {
|
|
|
5261
5289
|
language: "go",
|
|
5262
5290
|
dependencies: mod.dependencies,
|
|
5263
5291
|
repoPath: path10.relative(scanPath, dir),
|
|
5264
|
-
...
|
|
5292
|
+
...framework ? { framework } : {}
|
|
5265
5293
|
};
|
|
5266
5294
|
return { pkg, dir, node };
|
|
5267
5295
|
}
|
|
@@ -6168,7 +6196,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6168
6196
|
return best;
|
|
6169
6197
|
};
|
|
6170
6198
|
const requests = [];
|
|
6171
|
-
const
|
|
6199
|
+
const walk9 = (node) => {
|
|
6172
6200
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
6173
6201
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
6174
6202
|
if (self && self.kind === "class") {
|
|
@@ -6214,10 +6242,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
6214
6242
|
}
|
|
6215
6243
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6216
6244
|
const child = node.namedChild(i);
|
|
6217
|
-
if (child)
|
|
6245
|
+
if (child) walk9(child);
|
|
6218
6246
|
}
|
|
6219
6247
|
};
|
|
6220
|
-
|
|
6248
|
+
walk9(root);
|
|
6221
6249
|
for (const req of requests) {
|
|
6222
6250
|
const targetSid = resolveTarget(req.targetName, req.wantKind);
|
|
6223
6251
|
if (!targetSid) continue;
|
|
@@ -7237,20 +7265,20 @@ import {
|
|
|
7237
7265
|
} from "@neat.is/types";
|
|
7238
7266
|
async function walkConfigFiles(dir) {
|
|
7239
7267
|
const out = [];
|
|
7240
|
-
async function
|
|
7268
|
+
async function walk9(current) {
|
|
7241
7269
|
const entries = await fs16.readdir(current, { withFileTypes: true });
|
|
7242
7270
|
for (const entry of entries) {
|
|
7243
7271
|
const full = path28.join(current, entry.name);
|
|
7244
7272
|
if (entry.isDirectory()) {
|
|
7245
7273
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7246
7274
|
if (await isPythonVenvDir(full)) continue;
|
|
7247
|
-
await
|
|
7275
|
+
await walk9(full);
|
|
7248
7276
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
7249
7277
|
out.push(full);
|
|
7250
7278
|
}
|
|
7251
7279
|
}
|
|
7252
7280
|
}
|
|
7253
|
-
await
|
|
7281
|
+
await walk9(dir);
|
|
7254
7282
|
return out;
|
|
7255
7283
|
}
|
|
7256
7284
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -7346,20 +7374,20 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
7346
7374
|
}
|
|
7347
7375
|
async function walkProtoFiles(dir) {
|
|
7348
7376
|
const out = [];
|
|
7349
|
-
async function
|
|
7377
|
+
async function walk9(current) {
|
|
7350
7378
|
const entries = await fs17.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
7351
7379
|
for (const entry of entries) {
|
|
7352
7380
|
const full = path29.join(current, entry.name);
|
|
7353
7381
|
if (entry.isDirectory()) {
|
|
7354
7382
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7355
7383
|
if (await isPythonVenvDir(full)) continue;
|
|
7356
|
-
await
|
|
7384
|
+
await walk9(full);
|
|
7357
7385
|
} else if (entry.isFile() && path29.extname(entry.name) === PROTO_EXTENSION) {
|
|
7358
7386
|
out.push(full);
|
|
7359
7387
|
}
|
|
7360
7388
|
}
|
|
7361
7389
|
}
|
|
7362
|
-
await
|
|
7390
|
+
await walk9(dir);
|
|
7363
7391
|
return out;
|
|
7364
7392
|
}
|
|
7365
7393
|
async function addGrpcMethods(graph, services) {
|
|
@@ -8190,7 +8218,7 @@ function isFirestoreClientFactory(node) {
|
|
|
8190
8218
|
}
|
|
8191
8219
|
function firestoreClientVars(root) {
|
|
8192
8220
|
const vars = /* @__PURE__ */ new Set();
|
|
8193
|
-
const
|
|
8221
|
+
const walk9 = (node) => {
|
|
8194
8222
|
if (node.type === "variable_declarator") {
|
|
8195
8223
|
const name = node.childForFieldName("name");
|
|
8196
8224
|
let value = node.childForFieldName("value");
|
|
@@ -8199,9 +8227,9 @@ function firestoreClientVars(root) {
|
|
|
8199
8227
|
vars.add(name.text);
|
|
8200
8228
|
}
|
|
8201
8229
|
}
|
|
8202
|
-
for (const c of namedChildren(node))
|
|
8230
|
+
for (const c of namedChildren(node)) walk9(c);
|
|
8203
8231
|
};
|
|
8204
|
-
|
|
8232
|
+
walk9(root);
|
|
8205
8233
|
return vars;
|
|
8206
8234
|
}
|
|
8207
8235
|
function isClientExpr(node, clientVars) {
|
|
@@ -8356,7 +8384,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
8356
8384
|
}
|
|
8357
8385
|
s.add(field);
|
|
8358
8386
|
};
|
|
8359
|
-
const
|
|
8387
|
+
const walk9 = (node) => {
|
|
8360
8388
|
if (node.type === "call_expression") {
|
|
8361
8389
|
const fn = node.childForFieldName("function");
|
|
8362
8390
|
const line = node.startPosition.row + 1;
|
|
@@ -8396,9 +8424,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
8396
8424
|
}
|
|
8397
8425
|
}
|
|
8398
8426
|
}
|
|
8399
|
-
for (const c of namedChildren(node))
|
|
8427
|
+
for (const c of namedChildren(node)) walk9(c);
|
|
8400
8428
|
};
|
|
8401
|
-
|
|
8429
|
+
walk9(tree.rootNode);
|
|
8402
8430
|
const out = [];
|
|
8403
8431
|
for (const [collPath, line] of collLine) {
|
|
8404
8432
|
const byField = writes.get(collPath);
|
|
@@ -9189,7 +9217,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
9189
9217
|
const tree = parseSource3(parserForExt2(path41.extname(file.path)), file.content);
|
|
9190
9218
|
const out = [];
|
|
9191
9219
|
const seen = /* @__PURE__ */ new Set();
|
|
9192
|
-
const
|
|
9220
|
+
const walk9 = (node) => {
|
|
9193
9221
|
if (node.type === "call_expression") {
|
|
9194
9222
|
const fn = node.childForFieldName("function");
|
|
9195
9223
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -9217,9 +9245,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
9217
9245
|
}
|
|
9218
9246
|
}
|
|
9219
9247
|
}
|
|
9220
|
-
for (const c of namedChildren4(node))
|
|
9248
|
+
for (const c of namedChildren4(node)) walk9(c);
|
|
9221
9249
|
};
|
|
9222
|
-
|
|
9250
|
+
walk9(tree.rootNode);
|
|
9223
9251
|
return out;
|
|
9224
9252
|
}
|
|
9225
9253
|
function enclosingVarName(call) {
|
|
@@ -9241,7 +9269,7 @@ function enclosingVarName(call) {
|
|
|
9241
9269
|
function collectDrizzleTables(root) {
|
|
9242
9270
|
const tables = [];
|
|
9243
9271
|
const varToTable = /* @__PURE__ */ new Map();
|
|
9244
|
-
const
|
|
9272
|
+
const walk9 = (node) => {
|
|
9245
9273
|
if (node.type === "call_expression") {
|
|
9246
9274
|
const fn = node.childForFieldName("function");
|
|
9247
9275
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -9256,9 +9284,9 @@ function collectDrizzleTables(root) {
|
|
|
9256
9284
|
}
|
|
9257
9285
|
}
|
|
9258
9286
|
}
|
|
9259
|
-
for (const c of namedChildren4(node))
|
|
9287
|
+
for (const c of namedChildren4(node)) walk9(c);
|
|
9260
9288
|
};
|
|
9261
|
-
|
|
9289
|
+
walk9(root);
|
|
9262
9290
|
return { tables, varToTable };
|
|
9263
9291
|
}
|
|
9264
9292
|
function referencesTargetVar(call) {
|
|
@@ -9281,7 +9309,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
9281
9309
|
const seen = /* @__PURE__ */ new Set();
|
|
9282
9310
|
for (const table of tables) {
|
|
9283
9311
|
if (!table.object) continue;
|
|
9284
|
-
const
|
|
9312
|
+
const walk9 = (node) => {
|
|
9285
9313
|
if (node.type === "call_expression") {
|
|
9286
9314
|
const targetVar = referencesTargetVar(node);
|
|
9287
9315
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -9302,9 +9330,9 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
9302
9330
|
}
|
|
9303
9331
|
}
|
|
9304
9332
|
}
|
|
9305
|
-
for (const c of namedChildren4(node))
|
|
9333
|
+
for (const c of namedChildren4(node)) walk9(c);
|
|
9306
9334
|
};
|
|
9307
|
-
|
|
9335
|
+
walk9(table.object);
|
|
9308
9336
|
}
|
|
9309
9337
|
return out;
|
|
9310
9338
|
}
|
|
@@ -10379,6 +10407,521 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
10379
10407
|
return out;
|
|
10380
10408
|
}
|
|
10381
10409
|
|
|
10410
|
+
// src/extract/calls/gorm.ts
|
|
10411
|
+
import path46 from "path";
|
|
10412
|
+
import Parser15 from "tree-sitter";
|
|
10413
|
+
import Go4 from "tree-sitter-go";
|
|
10414
|
+
import { infraId as infraId16 } from "@neat.is/types";
|
|
10415
|
+
var GORM_IMPORT_RE = /gorm\.io\/gorm/;
|
|
10416
|
+
var PARSE_CHUNK11 = 16384;
|
|
10417
|
+
function makeGoParser3() {
|
|
10418
|
+
const p = new Parser15();
|
|
10419
|
+
p.setLanguage(Go4);
|
|
10420
|
+
return p;
|
|
10421
|
+
}
|
|
10422
|
+
function parseSource10(parser, source) {
|
|
10423
|
+
return parser.parse(
|
|
10424
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
|
|
10425
|
+
);
|
|
10426
|
+
}
|
|
10427
|
+
function walk8(node, visit) {
|
|
10428
|
+
visit(node);
|
|
10429
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
10430
|
+
const c = node.namedChild(i);
|
|
10431
|
+
if (c) walk8(c, visit);
|
|
10432
|
+
}
|
|
10433
|
+
}
|
|
10434
|
+
var COMMON_INITIALISMS = [
|
|
10435
|
+
"ASCII",
|
|
10436
|
+
"HTTPS",
|
|
10437
|
+
"UTF8",
|
|
10438
|
+
"XSRF",
|
|
10439
|
+
"HTML",
|
|
10440
|
+
"HTTP",
|
|
10441
|
+
"JSON",
|
|
10442
|
+
"UUID",
|
|
10443
|
+
"XMPP",
|
|
10444
|
+
"ACL",
|
|
10445
|
+
"API",
|
|
10446
|
+
"CPU",
|
|
10447
|
+
"CSS",
|
|
10448
|
+
"DNS",
|
|
10449
|
+
"EOF",
|
|
10450
|
+
"GUID",
|
|
10451
|
+
"LHS",
|
|
10452
|
+
"QPS",
|
|
10453
|
+
"RAM",
|
|
10454
|
+
"RHS",
|
|
10455
|
+
"RPC",
|
|
10456
|
+
"SLA",
|
|
10457
|
+
"SQL",
|
|
10458
|
+
"SSH",
|
|
10459
|
+
"TCP",
|
|
10460
|
+
"TLS",
|
|
10461
|
+
"TTL",
|
|
10462
|
+
"UDP",
|
|
10463
|
+
"UID",
|
|
10464
|
+
"URI",
|
|
10465
|
+
"URL",
|
|
10466
|
+
"UID",
|
|
10467
|
+
"XSS",
|
|
10468
|
+
"ID",
|
|
10469
|
+
"IP",
|
|
10470
|
+
"UI",
|
|
10471
|
+
"VM",
|
|
10472
|
+
"XML"
|
|
10473
|
+
].sort((a, b) => b.length - a.length);
|
|
10474
|
+
function titleCase(word) {
|
|
10475
|
+
return word.charAt(0) + word.slice(1).toLowerCase();
|
|
10476
|
+
}
|
|
10477
|
+
function replaceInitialisms(name) {
|
|
10478
|
+
let out = "";
|
|
10479
|
+
let i = 0;
|
|
10480
|
+
while (i < name.length) {
|
|
10481
|
+
let matched = false;
|
|
10482
|
+
for (const init of COMMON_INITIALISMS) {
|
|
10483
|
+
if (name.startsWith(init, i)) {
|
|
10484
|
+
out += titleCase(init);
|
|
10485
|
+
i += init.length;
|
|
10486
|
+
matched = true;
|
|
10487
|
+
break;
|
|
10488
|
+
}
|
|
10489
|
+
}
|
|
10490
|
+
if (!matched) {
|
|
10491
|
+
out += name[i];
|
|
10492
|
+
i++;
|
|
10493
|
+
}
|
|
10494
|
+
}
|
|
10495
|
+
return out;
|
|
10496
|
+
}
|
|
10497
|
+
var isUpper = (c) => c >= "A" && c <= "Z";
|
|
10498
|
+
var isDigit = (c) => c >= "0" && c <= "9";
|
|
10499
|
+
function toDBName(name) {
|
|
10500
|
+
if (name === "") return "";
|
|
10501
|
+
const value = replaceInitialisms(name);
|
|
10502
|
+
if (value.length === 1) return value.toLowerCase();
|
|
10503
|
+
let buf = "";
|
|
10504
|
+
let lastCase = false;
|
|
10505
|
+
let curCase = isUpper(value[0]);
|
|
10506
|
+
for (let i = 0; i < value.length - 1; i++) {
|
|
10507
|
+
const v = value[i];
|
|
10508
|
+
const nextCase = isUpper(value[i + 1]);
|
|
10509
|
+
const nextNumber = isDigit(value[i + 1]);
|
|
10510
|
+
if (curCase) {
|
|
10511
|
+
if (lastCase && (nextCase || nextNumber)) {
|
|
10512
|
+
buf += v.toLowerCase();
|
|
10513
|
+
} else {
|
|
10514
|
+
if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
|
|
10515
|
+
buf += v.toLowerCase();
|
|
10516
|
+
}
|
|
10517
|
+
} else {
|
|
10518
|
+
buf += v;
|
|
10519
|
+
}
|
|
10520
|
+
lastCase = curCase;
|
|
10521
|
+
curCase = nextCase;
|
|
10522
|
+
}
|
|
10523
|
+
const last = value[value.length - 1];
|
|
10524
|
+
if (curCase) {
|
|
10525
|
+
if (!lastCase && value.length > 1) buf += "_";
|
|
10526
|
+
buf += last.toLowerCase();
|
|
10527
|
+
} else {
|
|
10528
|
+
buf += last;
|
|
10529
|
+
}
|
|
10530
|
+
return buf;
|
|
10531
|
+
}
|
|
10532
|
+
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
10533
|
+
"equipment",
|
|
10534
|
+
"information",
|
|
10535
|
+
"rice",
|
|
10536
|
+
"money",
|
|
10537
|
+
"species",
|
|
10538
|
+
"series",
|
|
10539
|
+
"fish",
|
|
10540
|
+
"sheep",
|
|
10541
|
+
"jeans",
|
|
10542
|
+
"police"
|
|
10543
|
+
]);
|
|
10544
|
+
var IRREGULAR = [
|
|
10545
|
+
["person", "people"],
|
|
10546
|
+
["man", "men"],
|
|
10547
|
+
["child", "children"],
|
|
10548
|
+
["sex", "sexes"],
|
|
10549
|
+
["move", "moves"]
|
|
10550
|
+
];
|
|
10551
|
+
var PLURAL_RULES = [
|
|
10552
|
+
[/(quiz)$/i, "$1zes"],
|
|
10553
|
+
[/^(ox)$/i, "$1en"],
|
|
10554
|
+
[/([ml])ouse$/i, "$1ice"],
|
|
10555
|
+
[/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
|
|
10556
|
+
[/(x|ch|ss|sh)$/i, "$1es"],
|
|
10557
|
+
[/([^aeiouy]|qu)y$/i, "$1ies"],
|
|
10558
|
+
[/(hive)$/i, "$1s"],
|
|
10559
|
+
[/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
|
|
10560
|
+
[/sis$/i, "ses"],
|
|
10561
|
+
[/([ti])um$/i, "$1a"],
|
|
10562
|
+
[/([ti])a$/i, "$1a"],
|
|
10563
|
+
[/(buffal|tomat)o$/i, "$1oes"],
|
|
10564
|
+
[/(bu)s$/i, "$1ses"],
|
|
10565
|
+
[/(alias|status)$/i, "$1es"],
|
|
10566
|
+
[/(octop|vir)i$/i, "$1i"],
|
|
10567
|
+
[/(octop|vir)us$/i, "$1i"],
|
|
10568
|
+
[/(ax|test)is$/i, "$1es"],
|
|
10569
|
+
[/s$/i, "s"]
|
|
10570
|
+
];
|
|
10571
|
+
function pluralize3(word) {
|
|
10572
|
+
if (word === "") return word;
|
|
10573
|
+
const lower = word.toLowerCase();
|
|
10574
|
+
for (const u of UNCOUNTABLE) {
|
|
10575
|
+
if (lower === u || lower.endsWith("_" + u)) return word;
|
|
10576
|
+
}
|
|
10577
|
+
for (const [sing, plur] of IRREGULAR) {
|
|
10578
|
+
const re = new RegExp(sing + "$", "i");
|
|
10579
|
+
if (re.test(word)) return word.replace(re, plur);
|
|
10580
|
+
}
|
|
10581
|
+
for (const [re, rep] of PLURAL_RULES) {
|
|
10582
|
+
if (re.test(word)) return word.replace(re, rep);
|
|
10583
|
+
}
|
|
10584
|
+
return word + "s";
|
|
10585
|
+
}
|
|
10586
|
+
function deriveTableName(structName) {
|
|
10587
|
+
return pluralize3(toDBName(structName));
|
|
10588
|
+
}
|
|
10589
|
+
function stringLiteralValue(node) {
|
|
10590
|
+
if (!node) return null;
|
|
10591
|
+
if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
|
|
10592
|
+
const t = node.text;
|
|
10593
|
+
return t.length >= 2 ? t.slice(1, -1) : "";
|
|
10594
|
+
}
|
|
10595
|
+
return null;
|
|
10596
|
+
}
|
|
10597
|
+
function parseGormTag(tagNode) {
|
|
10598
|
+
const tag = {};
|
|
10599
|
+
if (!tagNode) return tag;
|
|
10600
|
+
let inner = tagNode.text;
|
|
10601
|
+
if (inner.length >= 2) inner = inner.slice(1, -1);
|
|
10602
|
+
if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
|
|
10603
|
+
const m = inner.match(/gorm:"([^"]*)"/);
|
|
10604
|
+
if (!m) return tag;
|
|
10605
|
+
for (const part of m[1].split(";")) {
|
|
10606
|
+
if (part === "") continue;
|
|
10607
|
+
const idx = part.indexOf(":");
|
|
10608
|
+
const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
|
|
10609
|
+
const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
|
|
10610
|
+
if (key === "-") tag.skip = true;
|
|
10611
|
+
else if (key === "column") tag.column = value;
|
|
10612
|
+
else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
|
|
10613
|
+
else if (key === "foreignkey") tag.foreignKey = value;
|
|
10614
|
+
else if (key === "many2many") tag.many2many = value;
|
|
10615
|
+
else if (key === "embedded") tag.embedded = true;
|
|
10616
|
+
else if (key === "embeddedprefix") tag.embeddedPrefix = value;
|
|
10617
|
+
}
|
|
10618
|
+
return tag;
|
|
10619
|
+
}
|
|
10620
|
+
function unwrapType(typeNode) {
|
|
10621
|
+
let isSlice = false;
|
|
10622
|
+
let isPointer = false;
|
|
10623
|
+
let n = typeNode;
|
|
10624
|
+
while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
|
|
10625
|
+
if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
|
|
10626
|
+
if (n.type === "pointer_type") isPointer = true;
|
|
10627
|
+
n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
|
|
10628
|
+
}
|
|
10629
|
+
if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
|
|
10630
|
+
if (n.type === "type_identifier") {
|
|
10631
|
+
return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
|
|
10632
|
+
}
|
|
10633
|
+
if (n.type === "qualified_type") {
|
|
10634
|
+
const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
|
|
10635
|
+
const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
|
|
10636
|
+
return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
|
|
10637
|
+
}
|
|
10638
|
+
return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
|
|
10639
|
+
}
|
|
10640
|
+
function readField(fieldDecl) {
|
|
10641
|
+
const names = [];
|
|
10642
|
+
let tagNode = null;
|
|
10643
|
+
for (let i = 0; i < fieldDecl.namedChildCount; i++) {
|
|
10644
|
+
const c = fieldDecl.namedChild(i);
|
|
10645
|
+
if (!c) continue;
|
|
10646
|
+
if (c.type === "field_identifier") names.push(c.text);
|
|
10647
|
+
else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
|
|
10648
|
+
}
|
|
10649
|
+
const typeNode = fieldDecl.childForFieldName("type");
|
|
10650
|
+
const t = unwrapType(typeNode);
|
|
10651
|
+
return {
|
|
10652
|
+
names,
|
|
10653
|
+
typeName: t.name,
|
|
10654
|
+
qualifier: t.qualifier,
|
|
10655
|
+
isSlice: t.isSlice,
|
|
10656
|
+
isPointer: t.isPointer,
|
|
10657
|
+
isQualified: t.isQualified,
|
|
10658
|
+
tag: parseGormTag(tagNode),
|
|
10659
|
+
line: fieldDecl.startPosition.row + 1
|
|
10660
|
+
};
|
|
10661
|
+
}
|
|
10662
|
+
function collectStructs(tree) {
|
|
10663
|
+
const structs = /* @__PURE__ */ new Map();
|
|
10664
|
+
walk8(tree.rootNode, (node) => {
|
|
10665
|
+
if (node.type !== "type_spec") return;
|
|
10666
|
+
const nameNode = node.childForFieldName("name");
|
|
10667
|
+
const typeNode = node.childForFieldName("type");
|
|
10668
|
+
if (!nameNode || typeNode?.type !== "struct_type") return;
|
|
10669
|
+
const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
|
|
10670
|
+
const fields = [];
|
|
10671
|
+
if (list && list.type === "field_declaration_list") {
|
|
10672
|
+
for (let i = 0; i < list.namedChildCount; i++) {
|
|
10673
|
+
const fd = list.namedChild(i);
|
|
10674
|
+
if (fd?.type === "field_declaration") fields.push(readField(fd));
|
|
10675
|
+
}
|
|
10676
|
+
}
|
|
10677
|
+
structs.set(nameNode.text, {
|
|
10678
|
+
name: nameNode.text,
|
|
10679
|
+
fields,
|
|
10680
|
+
line: node.startPosition.row + 1
|
|
10681
|
+
});
|
|
10682
|
+
});
|
|
10683
|
+
return structs;
|
|
10684
|
+
}
|
|
10685
|
+
var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
|
|
10686
|
+
"AutoMigrate",
|
|
10687
|
+
"Model",
|
|
10688
|
+
"Create",
|
|
10689
|
+
"Find",
|
|
10690
|
+
"First",
|
|
10691
|
+
"Take",
|
|
10692
|
+
"Last",
|
|
10693
|
+
"Save",
|
|
10694
|
+
"Delete",
|
|
10695
|
+
"Where",
|
|
10696
|
+
"FirstOrCreate",
|
|
10697
|
+
"FirstOrInit"
|
|
10698
|
+
]);
|
|
10699
|
+
function compositeStructName(arg) {
|
|
10700
|
+
let n = arg;
|
|
10701
|
+
if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
|
|
10702
|
+
if (!n || n.type !== "composite_literal") return null;
|
|
10703
|
+
const typeNode = n.childForFieldName("type");
|
|
10704
|
+
if (!typeNode) return null;
|
|
10705
|
+
if (typeNode.type === "type_identifier") return typeNode.text;
|
|
10706
|
+
if (typeNode.type === "qualified_type") {
|
|
10707
|
+
return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
|
|
10708
|
+
}
|
|
10709
|
+
return null;
|
|
10710
|
+
}
|
|
10711
|
+
function collectCallModels(tree) {
|
|
10712
|
+
const models = /* @__PURE__ */ new Set();
|
|
10713
|
+
walk8(tree.rootNode, (node) => {
|
|
10714
|
+
if (node.type !== "call_expression") return;
|
|
10715
|
+
const fn = node.childForFieldName("function");
|
|
10716
|
+
if (fn?.type !== "selector_expression") return;
|
|
10717
|
+
const method = fn.childForFieldName("field")?.text;
|
|
10718
|
+
if (!method || !GORM_MODEL_METHODS.has(method)) return;
|
|
10719
|
+
const args = node.childForFieldName("arguments");
|
|
10720
|
+
if (!args) return;
|
|
10721
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
10722
|
+
const arg = args.namedChild(i);
|
|
10723
|
+
if (!arg) continue;
|
|
10724
|
+
const name = compositeStructName(arg);
|
|
10725
|
+
if (name) models.add(name);
|
|
10726
|
+
}
|
|
10727
|
+
});
|
|
10728
|
+
return models;
|
|
10729
|
+
}
|
|
10730
|
+
function collectTableNameOverrides(tree) {
|
|
10731
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
10732
|
+
const declarers = /* @__PURE__ */ new Set();
|
|
10733
|
+
walk8(tree.rootNode, (node) => {
|
|
10734
|
+
if (node.type !== "method_declaration") return;
|
|
10735
|
+
if (node.childForFieldName("name")?.text !== "TableName") return;
|
|
10736
|
+
const receiver = node.childForFieldName("receiver");
|
|
10737
|
+
if (!receiver) return;
|
|
10738
|
+
let recvType = null;
|
|
10739
|
+
for (let i = 0; i < receiver.namedChildCount; i++) {
|
|
10740
|
+
const pd = receiver.namedChild(i);
|
|
10741
|
+
if (pd?.type !== "parameter_declaration") continue;
|
|
10742
|
+
const t = unwrapType(pd.childForFieldName("type"));
|
|
10743
|
+
recvType = t.name;
|
|
10744
|
+
}
|
|
10745
|
+
if (!recvType) return;
|
|
10746
|
+
declarers.add(recvType);
|
|
10747
|
+
const body = node.childForFieldName("body");
|
|
10748
|
+
if (!body) return;
|
|
10749
|
+
let literal = null;
|
|
10750
|
+
walk8(body, (n) => {
|
|
10751
|
+
if (literal !== null) return;
|
|
10752
|
+
if (n.type !== "return_statement") return;
|
|
10753
|
+
const exprList = n.namedChild(0);
|
|
10754
|
+
const first = exprList?.namedChild(0) ?? exprList;
|
|
10755
|
+
const v = stringLiteralValue(first);
|
|
10756
|
+
if (v) literal = v;
|
|
10757
|
+
});
|
|
10758
|
+
if (literal !== null) overrides.set(recvType, literal);
|
|
10759
|
+
});
|
|
10760
|
+
return { overrides, declarers };
|
|
10761
|
+
}
|
|
10762
|
+
function isRelationField(field, structs) {
|
|
10763
|
+
if (field.names.length === 0) return false;
|
|
10764
|
+
if (field.isQualified) return false;
|
|
10765
|
+
if (!field.typeName) return false;
|
|
10766
|
+
return structs.has(field.typeName);
|
|
10767
|
+
}
|
|
10768
|
+
function isGormModelEmbed(field) {
|
|
10769
|
+
return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
|
|
10770
|
+
}
|
|
10771
|
+
function analyze(tree) {
|
|
10772
|
+
const structs = collectStructs(tree);
|
|
10773
|
+
const { overrides, declarers } = collectTableNameOverrides(tree);
|
|
10774
|
+
const callModels = collectCallModels(tree);
|
|
10775
|
+
const models = /* @__PURE__ */ new Set();
|
|
10776
|
+
for (const [name, info] of structs) {
|
|
10777
|
+
if (info.fields.some(isGormModelEmbed)) models.add(name);
|
|
10778
|
+
}
|
|
10779
|
+
for (const name of callModels) if (structs.has(name)) models.add(name);
|
|
10780
|
+
for (const name of declarers) if (structs.has(name)) models.add(name);
|
|
10781
|
+
let grew = true;
|
|
10782
|
+
while (grew) {
|
|
10783
|
+
grew = false;
|
|
10784
|
+
for (const name of Array.from(models)) {
|
|
10785
|
+
const info = structs.get(name);
|
|
10786
|
+
if (!info) continue;
|
|
10787
|
+
for (const field of info.fields) {
|
|
10788
|
+
if (!isRelationField(field, structs)) continue;
|
|
10789
|
+
const target = field.typeName;
|
|
10790
|
+
if (!models.has(target) && structs.has(target)) {
|
|
10791
|
+
models.add(target);
|
|
10792
|
+
grew = true;
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
}
|
|
10796
|
+
}
|
|
10797
|
+
const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
|
|
10798
|
+
return { structs, models, tableFor };
|
|
10799
|
+
}
|
|
10800
|
+
function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
10801
|
+
if (seen.has(struct.name)) return;
|
|
10802
|
+
seen.add(struct.name);
|
|
10803
|
+
const add = (col) => {
|
|
10804
|
+
const full = prefix + col;
|
|
10805
|
+
if (!emitted.has(full)) {
|
|
10806
|
+
emitted.add(full);
|
|
10807
|
+
out.push(full);
|
|
10808
|
+
}
|
|
10809
|
+
};
|
|
10810
|
+
for (const field of struct.fields) {
|
|
10811
|
+
if (field.tag.skip) continue;
|
|
10812
|
+
if (field.names.length === 0) {
|
|
10813
|
+
if (isGormModelEmbed(field)) {
|
|
10814
|
+
add("id");
|
|
10815
|
+
add("created_at");
|
|
10816
|
+
add("updated_at");
|
|
10817
|
+
add("deleted_at");
|
|
10818
|
+
} else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
|
|
10819
|
+
collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
|
|
10820
|
+
}
|
|
10821
|
+
continue;
|
|
10822
|
+
}
|
|
10823
|
+
if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
|
|
10824
|
+
collectColumns(
|
|
10825
|
+
structs.get(field.typeName),
|
|
10826
|
+
structs,
|
|
10827
|
+
seen,
|
|
10828
|
+
prefix + (field.tag.embeddedPrefix ?? ""),
|
|
10829
|
+
out,
|
|
10830
|
+
emitted
|
|
10831
|
+
);
|
|
10832
|
+
continue;
|
|
10833
|
+
}
|
|
10834
|
+
if (isRelationField(field, structs)) continue;
|
|
10835
|
+
if (field.names.length === 1 && field.tag.column) {
|
|
10836
|
+
add(field.tag.column);
|
|
10837
|
+
} else {
|
|
10838
|
+
for (const n of field.names) add(toDBName(n));
|
|
10839
|
+
}
|
|
10840
|
+
}
|
|
10841
|
+
seen.delete(struct.name);
|
|
10842
|
+
}
|
|
10843
|
+
function gormEndpointsFromFile(file, serviceDir) {
|
|
10844
|
+
if (path46.extname(file.path) !== ".go") return [];
|
|
10845
|
+
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
10846
|
+
const tree = parseSource10(makeGoParser3(), file.content);
|
|
10847
|
+
const { structs, models, tableFor } = analyze(tree);
|
|
10848
|
+
const out = [];
|
|
10849
|
+
const seenTables = /* @__PURE__ */ new Set();
|
|
10850
|
+
for (const name of models) {
|
|
10851
|
+
const struct = structs.get(name);
|
|
10852
|
+
if (!struct) continue;
|
|
10853
|
+
const table = tableFor(name);
|
|
10854
|
+
if (seenTables.has(table)) continue;
|
|
10855
|
+
seenTables.add(table);
|
|
10856
|
+
const columns = [];
|
|
10857
|
+
collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
|
|
10858
|
+
out.push({
|
|
10859
|
+
infraId: infraId16("sql-table", table),
|
|
10860
|
+
name: table,
|
|
10861
|
+
kind: "sql-table",
|
|
10862
|
+
edgeType: "CALLS",
|
|
10863
|
+
confidenceKind: "structural",
|
|
10864
|
+
...columns.length > 0 ? { columns } : {},
|
|
10865
|
+
evidence: {
|
|
10866
|
+
file: toPosix(path46.relative(serviceDir, file.path)),
|
|
10867
|
+
line: struct.line,
|
|
10868
|
+
snippet: snippet(file.content, struct.line)
|
|
10869
|
+
}
|
|
10870
|
+
});
|
|
10871
|
+
}
|
|
10872
|
+
return out;
|
|
10873
|
+
}
|
|
10874
|
+
function gormForeignKeys(file, serviceDir) {
|
|
10875
|
+
if (path46.extname(file.path) !== ".go") return [];
|
|
10876
|
+
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
10877
|
+
const tree = parseSource10(makeGoParser3(), file.content);
|
|
10878
|
+
const { structs, models, tableFor } = analyze(tree);
|
|
10879
|
+
const out = [];
|
|
10880
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10881
|
+
const emit = (childTable, parentTable, line) => {
|
|
10882
|
+
if (!childTable || !parentTable || childTable === parentTable) return;
|
|
10883
|
+
const key = `${childTable}->${parentTable}`;
|
|
10884
|
+
if (seen.has(key)) return;
|
|
10885
|
+
seen.add(key);
|
|
10886
|
+
out.push({
|
|
10887
|
+
childTable,
|
|
10888
|
+
parentTable,
|
|
10889
|
+
evidence: {
|
|
10890
|
+
file: toPosix(path46.relative(serviceDir, file.path)),
|
|
10891
|
+
line,
|
|
10892
|
+
snippet: snippet(file.content, line)
|
|
10893
|
+
}
|
|
10894
|
+
});
|
|
10895
|
+
};
|
|
10896
|
+
for (const name of models) {
|
|
10897
|
+
const struct = structs.get(name);
|
|
10898
|
+
if (!struct) continue;
|
|
10899
|
+
const thisTable = tableFor(name);
|
|
10900
|
+
const scalarNames = new Set(
|
|
10901
|
+
struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
|
|
10902
|
+
);
|
|
10903
|
+
for (const field of struct.fields) {
|
|
10904
|
+
if (field.tag.skip) continue;
|
|
10905
|
+
if (!isRelationField(field, structs)) continue;
|
|
10906
|
+
const relTable = tableFor(field.typeName);
|
|
10907
|
+
if (field.tag.many2many) {
|
|
10908
|
+
emit(field.tag.many2many, thisTable, field.line);
|
|
10909
|
+
emit(field.tag.many2many, relTable, field.line);
|
|
10910
|
+
continue;
|
|
10911
|
+
}
|
|
10912
|
+
if (field.isSlice) {
|
|
10913
|
+
emit(relTable, thisTable, field.line);
|
|
10914
|
+
continue;
|
|
10915
|
+
}
|
|
10916
|
+
const convFk = field.names[0] + "ID";
|
|
10917
|
+
const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
|
|
10918
|
+
if (belongsTo) emit(thisTable, relTable, field.line);
|
|
10919
|
+
else emit(relTable, thisTable, field.line);
|
|
10920
|
+
}
|
|
10921
|
+
}
|
|
10922
|
+
return out;
|
|
10923
|
+
}
|
|
10924
|
+
|
|
10382
10925
|
// src/extract/calls/index.ts
|
|
10383
10926
|
function edgeTypeFromEndpoint(ep) {
|
|
10384
10927
|
switch (ep.edgeType) {
|
|
@@ -10420,6 +10963,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
10420
10963
|
} catch (err) {
|
|
10421
10964
|
recordExtractionError("go SQL call extraction", file.path, err);
|
|
10422
10965
|
}
|
|
10966
|
+
try {
|
|
10967
|
+
endpoints.push(...gormEndpointsFromFile(file, service.dir));
|
|
10968
|
+
} catch (err) {
|
|
10969
|
+
recordExtractionError("gorm data-axis extraction", file.path, err);
|
|
10970
|
+
}
|
|
10423
10971
|
try {
|
|
10424
10972
|
endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
|
|
10425
10973
|
endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
|
|
@@ -10535,7 +11083,7 @@ import {
|
|
|
10535
11083
|
Provenance as Provenance16,
|
|
10536
11084
|
confidenceForExtracted as confidenceForExtracted13,
|
|
10537
11085
|
extractedEdgeId as extractedEdgeId10,
|
|
10538
|
-
infraId as
|
|
11086
|
+
infraId as infraId17
|
|
10539
11087
|
} from "@neat.is/types";
|
|
10540
11088
|
async function addTableEdges(graph, services) {
|
|
10541
11089
|
let nodesAdded = 0;
|
|
@@ -10550,6 +11098,7 @@ async function addTableEdges(graph, services) {
|
|
|
10550
11098
|
refs.push(...sqlalchemyForeignKeys(file, service.dir));
|
|
10551
11099
|
refs.push(...railsSchemaForeignKeys(file, service.dir));
|
|
10552
11100
|
refs.push(...laravelMigrationForeignKeys(file, service.dir));
|
|
11101
|
+
refs.push(...gormForeignKeys(file, service.dir));
|
|
10553
11102
|
modelRefs.push(...railsModelForeignKeys(file, service.dir));
|
|
10554
11103
|
modelRefs.push(...laravelModelForeignKeys(file, service.dir));
|
|
10555
11104
|
} catch (err) {
|
|
@@ -10563,8 +11112,8 @@ async function addTableEdges(graph, services) {
|
|
|
10563
11112
|
}
|
|
10564
11113
|
refs.push(...modelRefs);
|
|
10565
11114
|
for (const ref of refs) {
|
|
10566
|
-
const childId =
|
|
10567
|
-
const parentId =
|
|
11115
|
+
const childId = infraId17("sql-table", ref.childTable);
|
|
11116
|
+
const parentId = infraId17("sql-table", ref.parentTable);
|
|
10568
11117
|
if (childId === parentId) continue;
|
|
10569
11118
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
10570
11119
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
@@ -10599,14 +11148,14 @@ function ensureTableNode(graph, id, name) {
|
|
|
10599
11148
|
}
|
|
10600
11149
|
|
|
10601
11150
|
// src/extract/infra/docker-compose.ts
|
|
10602
|
-
import
|
|
11151
|
+
import path47 from "path";
|
|
10603
11152
|
import { EdgeType as EdgeType17, Provenance as Provenance18, confidenceForExtracted as confidenceForExtracted15 } from "@neat.is/types";
|
|
10604
11153
|
|
|
10605
11154
|
// src/extract/infra/shared.ts
|
|
10606
|
-
import { NodeType as NodeType20, Provenance as Provenance17, confidenceForExtracted as confidenceForExtracted14, infraId as
|
|
11155
|
+
import { NodeType as NodeType20, Provenance as Provenance17, confidenceForExtracted as confidenceForExtracted14, infraId as infraId18 } from "@neat.is/types";
|
|
10607
11156
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
10608
11157
|
return {
|
|
10609
|
-
id:
|
|
11158
|
+
id: infraId18(kind, name),
|
|
10610
11159
|
type: NodeType20.InfraNode,
|
|
10611
11160
|
name,
|
|
10612
11161
|
provider,
|
|
@@ -10669,7 +11218,7 @@ function dependsOnList(value) {
|
|
|
10669
11218
|
}
|
|
10670
11219
|
function serviceNameToServiceNode(name, services) {
|
|
10671
11220
|
for (const s of services) {
|
|
10672
|
-
if (s.node.name === name ||
|
|
11221
|
+
if (s.node.name === name || path47.basename(s.dir) === name) return s.node.id;
|
|
10673
11222
|
}
|
|
10674
11223
|
return null;
|
|
10675
11224
|
}
|
|
@@ -10678,7 +11227,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
10678
11227
|
let edgesAdded = 0;
|
|
10679
11228
|
let composePath = null;
|
|
10680
11229
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
10681
|
-
const abs =
|
|
11230
|
+
const abs = path47.join(scanPath, name);
|
|
10682
11231
|
if (await exists(abs)) {
|
|
10683
11232
|
composePath = abs;
|
|
10684
11233
|
break;
|
|
@@ -10691,13 +11240,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
10691
11240
|
} catch (err) {
|
|
10692
11241
|
recordExtractionError(
|
|
10693
11242
|
"infra docker-compose",
|
|
10694
|
-
|
|
11243
|
+
path47.relative(scanPath, composePath),
|
|
10695
11244
|
err
|
|
10696
11245
|
);
|
|
10697
11246
|
return { nodesAdded, edgesAdded };
|
|
10698
11247
|
}
|
|
10699
11248
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
10700
|
-
const evidenceFile =
|
|
11249
|
+
const evidenceFile = path47.relative(scanPath, composePath).split(path47.sep).join("/");
|
|
10701
11250
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
10702
11251
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
10703
11252
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -10738,7 +11287,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
10738
11287
|
}
|
|
10739
11288
|
|
|
10740
11289
|
// src/extract/infra/dockerfile.ts
|
|
10741
|
-
import
|
|
11290
|
+
import path48 from "path";
|
|
10742
11291
|
import { promises as fs18 } from "fs";
|
|
10743
11292
|
import { EdgeType as EdgeType18, Provenance as Provenance19, confidenceForExtracted as confidenceForExtracted16 } from "@neat.is/types";
|
|
10744
11293
|
function readDockerfile(content) {
|
|
@@ -10769,7 +11318,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
10769
11318
|
let nodesAdded = 0;
|
|
10770
11319
|
let edgesAdded = 0;
|
|
10771
11320
|
for (const service of services) {
|
|
10772
|
-
const dockerfilePath =
|
|
11321
|
+
const dockerfilePath = path48.join(service.dir, "Dockerfile");
|
|
10773
11322
|
if (!await exists(dockerfilePath)) continue;
|
|
10774
11323
|
let content;
|
|
10775
11324
|
try {
|
|
@@ -10777,7 +11326,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
10777
11326
|
} catch (err) {
|
|
10778
11327
|
recordExtractionError(
|
|
10779
11328
|
"infra dockerfile",
|
|
10780
|
-
|
|
11329
|
+
path48.relative(scanPath, dockerfilePath),
|
|
10781
11330
|
err
|
|
10782
11331
|
);
|
|
10783
11332
|
continue;
|
|
@@ -10789,8 +11338,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
10789
11338
|
graph.addNode(node.id, node);
|
|
10790
11339
|
nodesAdded++;
|
|
10791
11340
|
}
|
|
10792
|
-
const relDockerfile = toPosix(
|
|
10793
|
-
const evidenceFile = toPosix(
|
|
11341
|
+
const relDockerfile = toPosix(path48.relative(service.dir, dockerfilePath));
|
|
11342
|
+
const evidenceFile = toPosix(path48.relative(scanPath, dockerfilePath));
|
|
10794
11343
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
10795
11344
|
graph,
|
|
10796
11345
|
service.pkg.name,
|
|
@@ -10842,7 +11391,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
10842
11391
|
|
|
10843
11392
|
// src/extract/infra/terraform.ts
|
|
10844
11393
|
import { promises as fs19 } from "fs";
|
|
10845
|
-
import
|
|
11394
|
+
import path49 from "path";
|
|
10846
11395
|
import { EdgeType as EdgeType19, Provenance as Provenance20, confidenceForExtracted as confidenceForExtracted17 } from "@neat.is/types";
|
|
10847
11396
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
10848
11397
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
@@ -10853,11 +11402,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
10853
11402
|
for (const entry of entries) {
|
|
10854
11403
|
if (entry.isDirectory()) {
|
|
10855
11404
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
10856
|
-
const child =
|
|
11405
|
+
const child = path49.join(start, entry.name);
|
|
10857
11406
|
if (await isPythonVenvDir(child)) continue;
|
|
10858
11407
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
10859
11408
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
10860
|
-
out.push(
|
|
11409
|
+
out.push(path49.join(start, entry.name));
|
|
10861
11410
|
}
|
|
10862
11411
|
}
|
|
10863
11412
|
return out;
|
|
@@ -10889,7 +11438,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
10889
11438
|
const files = await walkTfFiles(scanPath);
|
|
10890
11439
|
for (const file of files) {
|
|
10891
11440
|
const content = await fs19.readFile(file, "utf8");
|
|
10892
|
-
const evidenceFile = toPosix(
|
|
11441
|
+
const evidenceFile = toPosix(path49.relative(scanPath, file));
|
|
10893
11442
|
const resources = [];
|
|
10894
11443
|
const byKey = /* @__PURE__ */ new Map();
|
|
10895
11444
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -10946,7 +11495,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
10946
11495
|
|
|
10947
11496
|
// src/extract/infra/k8s.ts
|
|
10948
11497
|
import { promises as fs20 } from "fs";
|
|
10949
|
-
import
|
|
11498
|
+
import path50 from "path";
|
|
10950
11499
|
import { parseAllDocuments as parseAllDocuments2 } from "yaml";
|
|
10951
11500
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
10952
11501
|
Service: "k8s-service",
|
|
@@ -10964,11 +11513,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
10964
11513
|
for (const entry of entries) {
|
|
10965
11514
|
if (entry.isDirectory()) {
|
|
10966
11515
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
10967
|
-
const child =
|
|
11516
|
+
const child = path50.join(start, entry.name);
|
|
10968
11517
|
if (await isPythonVenvDir(child)) continue;
|
|
10969
11518
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
10970
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
10971
|
-
out.push(
|
|
11519
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path50.extname(entry.name))) {
|
|
11520
|
+
out.push(path50.join(start, entry.name));
|
|
10972
11521
|
}
|
|
10973
11522
|
}
|
|
10974
11523
|
return out;
|
|
@@ -11001,13 +11550,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
11001
11550
|
|
|
11002
11551
|
// src/extract/infra/cloudflare.ts
|
|
11003
11552
|
import { promises as fs21 } from "fs";
|
|
11004
|
-
import
|
|
11553
|
+
import path51 from "path";
|
|
11005
11554
|
import { parse as parseToml2 } from "smol-toml";
|
|
11006
11555
|
import { EdgeType as EdgeType20, Provenance as Provenance21, confidenceForExtracted as confidenceForExtracted18 } from "@neat.is/types";
|
|
11007
11556
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
11008
11557
|
async function readWranglerConfig(dir) {
|
|
11009
11558
|
for (const filename of WRANGLER_FILENAMES) {
|
|
11010
|
-
const abs =
|
|
11559
|
+
const abs = path51.join(dir, filename);
|
|
11011
11560
|
if (!await exists(abs)) continue;
|
|
11012
11561
|
const raw = await fs21.readFile(abs, "utf8");
|
|
11013
11562
|
const config = filename === "wrangler.toml" ? parseToml2(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -11070,11 +11619,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
11070
11619
|
try {
|
|
11071
11620
|
read = await readWranglerConfig(service.dir);
|
|
11072
11621
|
} catch (err) {
|
|
11073
|
-
recordExtractionError("infra cloudflare",
|
|
11622
|
+
recordExtractionError("infra cloudflare", path51.relative(scanPath, service.dir), err);
|
|
11074
11623
|
continue;
|
|
11075
11624
|
}
|
|
11076
11625
|
if (!read || !read.config.name) continue;
|
|
11077
|
-
const evidenceFile = toPosix(
|
|
11626
|
+
const evidenceFile = toPosix(path51.relative(scanPath, path51.join(service.dir, read.relFile)));
|
|
11078
11627
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
11079
11628
|
}
|
|
11080
11629
|
for (const worker of discovered) {
|
|
@@ -11086,7 +11635,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
11086
11635
|
}
|
|
11087
11636
|
let anchorId = service.node.id;
|
|
11088
11637
|
if (config.main) {
|
|
11089
|
-
const entryRelPath = toPosix(
|
|
11638
|
+
const entryRelPath = toPosix(path51.normalize(config.main));
|
|
11090
11639
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
11091
11640
|
graph,
|
|
11092
11641
|
service.pkg.name,
|
|
@@ -11233,12 +11782,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
11233
11782
|
|
|
11234
11783
|
// src/extract/infra/vercel.ts
|
|
11235
11784
|
import { promises as fs22 } from "fs";
|
|
11236
|
-
import
|
|
11785
|
+
import path52 from "path";
|
|
11237
11786
|
import { EdgeType as EdgeType21 } from "@neat.is/types";
|
|
11238
11787
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
11239
11788
|
async function readVercelConfig(dir) {
|
|
11240
11789
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
11241
|
-
const abs =
|
|
11790
|
+
const abs = path52.join(dir, filename);
|
|
11242
11791
|
if (!await exists(abs)) continue;
|
|
11243
11792
|
const raw = await fs22.readFile(abs, "utf8");
|
|
11244
11793
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -11247,7 +11796,7 @@ async function readVercelConfig(dir) {
|
|
|
11247
11796
|
return null;
|
|
11248
11797
|
}
|
|
11249
11798
|
async function readLinkedProjectName(dir) {
|
|
11250
|
-
const abs =
|
|
11799
|
+
const abs = path52.join(dir, ".vercel", "project.json");
|
|
11251
11800
|
if (!await exists(abs)) return void 0;
|
|
11252
11801
|
const parsed = JSON.parse(await fs22.readFile(abs, "utf8"));
|
|
11253
11802
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -11265,7 +11814,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
11265
11814
|
read = await readVercelConfig(service.dir);
|
|
11266
11815
|
projectName = await readLinkedProjectName(service.dir);
|
|
11267
11816
|
} catch (err) {
|
|
11268
|
-
recordExtractionError("infra vercel",
|
|
11817
|
+
recordExtractionError("infra vercel", path52.relative(scanPath, service.dir), err);
|
|
11269
11818
|
continue;
|
|
11270
11819
|
}
|
|
11271
11820
|
if (!read && !projectName) continue;
|
|
@@ -11281,7 +11830,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
11281
11830
|
const anchorId = service.node.id;
|
|
11282
11831
|
if (!read) continue;
|
|
11283
11832
|
const { config, relFile, raw } = read;
|
|
11284
|
-
const evidenceFile = toPosix(
|
|
11833
|
+
const evidenceFile = toPosix(path52.relative(scanPath, path52.join(service.dir, relFile)));
|
|
11285
11834
|
const add = (edgeType, kind, name) => {
|
|
11286
11835
|
if (!name) return;
|
|
11287
11836
|
const result = emitPlatformResourceEdge(
|
|
@@ -11310,13 +11859,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
11310
11859
|
|
|
11311
11860
|
// src/extract/infra/railway.ts
|
|
11312
11861
|
import { promises as fs23 } from "fs";
|
|
11313
|
-
import
|
|
11862
|
+
import path53 from "path";
|
|
11314
11863
|
import { parse as parseToml3 } from "smol-toml";
|
|
11315
11864
|
import { EdgeType as EdgeType22 } from "@neat.is/types";
|
|
11316
11865
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
11317
11866
|
async function readRailwayConfig(dir) {
|
|
11318
11867
|
for (const filename of RAILWAY_FILENAMES) {
|
|
11319
|
-
const abs =
|
|
11868
|
+
const abs = path53.join(dir, filename);
|
|
11320
11869
|
if (!await exists(abs)) continue;
|
|
11321
11870
|
const raw = await fs23.readFile(abs, "utf8");
|
|
11322
11871
|
const config = filename === "railway.toml" ? parseToml3(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -11332,7 +11881,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
11332
11881
|
try {
|
|
11333
11882
|
read = await readRailwayConfig(service.dir);
|
|
11334
11883
|
} catch (err) {
|
|
11335
|
-
recordExtractionError("infra railway",
|
|
11884
|
+
recordExtractionError("infra railway", path53.relative(scanPath, service.dir), err);
|
|
11336
11885
|
continue;
|
|
11337
11886
|
}
|
|
11338
11887
|
if (!read) continue;
|
|
@@ -11342,7 +11891,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
11342
11891
|
}
|
|
11343
11892
|
const anchorId = service.node.id;
|
|
11344
11893
|
const { config, relFile, raw } = read;
|
|
11345
|
-
const evidenceFile = toPosix(
|
|
11894
|
+
const evidenceFile = toPosix(path53.relative(scanPath, path53.join(service.dir, relFile)));
|
|
11346
11895
|
const add = (edgeType, kind, name) => {
|
|
11347
11896
|
if (!name) return;
|
|
11348
11897
|
const result = emitPlatformResourceEdge(
|
|
@@ -11367,12 +11916,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
11367
11916
|
|
|
11368
11917
|
// src/extract/infra/supabase.ts
|
|
11369
11918
|
import { promises as fs24 } from "fs";
|
|
11370
|
-
import
|
|
11919
|
+
import path54 from "path";
|
|
11371
11920
|
import { parse as parseToml4 } from "smol-toml";
|
|
11372
11921
|
import { EdgeType as EdgeType23 } from "@neat.is/types";
|
|
11373
11922
|
async function readSupabaseConfig(dir) {
|
|
11374
|
-
const relFile =
|
|
11375
|
-
const abs =
|
|
11923
|
+
const relFile = path54.join("supabase", "config.toml");
|
|
11924
|
+
const abs = path54.join(dir, relFile);
|
|
11376
11925
|
if (!await exists(abs)) return null;
|
|
11377
11926
|
const raw = await fs24.readFile(abs, "utf8");
|
|
11378
11927
|
const config = parseToml4(raw);
|
|
@@ -11386,7 +11935,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
11386
11935
|
try {
|
|
11387
11936
|
read = await readSupabaseConfig(service.dir);
|
|
11388
11937
|
} catch (err) {
|
|
11389
|
-
recordExtractionError("infra supabase",
|
|
11938
|
+
recordExtractionError("infra supabase", path54.relative(scanPath, service.dir), err);
|
|
11390
11939
|
continue;
|
|
11391
11940
|
}
|
|
11392
11941
|
if (!read) continue;
|
|
@@ -11401,7 +11950,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
11401
11950
|
});
|
|
11402
11951
|
}
|
|
11403
11952
|
const anchorId = service.node.id;
|
|
11404
|
-
const evidenceFile = toPosix(
|
|
11953
|
+
const evidenceFile = toPosix(path54.relative(scanPath, path54.join(service.dir, relFile)));
|
|
11405
11954
|
const add = (edgeType, kind, name) => {
|
|
11406
11955
|
if (!name) return;
|
|
11407
11956
|
const result = emitPlatformResourceEdge(
|
|
@@ -11442,8 +11991,8 @@ async function addInfra(graph, scanPath, services) {
|
|
|
11442
11991
|
}
|
|
11443
11992
|
|
|
11444
11993
|
// src/extract/zod-shapes.ts
|
|
11445
|
-
import
|
|
11446
|
-
import
|
|
11994
|
+
import path55 from "path";
|
|
11995
|
+
import Parser16 from "tree-sitter";
|
|
11447
11996
|
import JavaScript8 from "tree-sitter-javascript";
|
|
11448
11997
|
import {
|
|
11449
11998
|
EdgeType as EdgeType24,
|
|
@@ -11451,12 +12000,12 @@ import {
|
|
|
11451
12000
|
Provenance as Provenance22,
|
|
11452
12001
|
confidenceForExtracted as confidenceForExtracted19,
|
|
11453
12002
|
extractedEdgeId as extractedEdgeId11,
|
|
11454
|
-
infraId as
|
|
12003
|
+
infraId as infraId19
|
|
11455
12004
|
} from "@neat.is/types";
|
|
11456
12005
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
11457
12006
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
11458
12007
|
function parserForExt3(ext) {
|
|
11459
|
-
const p = new
|
|
12008
|
+
const p = new Parser16();
|
|
11460
12009
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? JavaScript8);
|
|
11461
12010
|
return p;
|
|
11462
12011
|
}
|
|
@@ -11544,7 +12093,7 @@ function topLevelSchemas(root) {
|
|
|
11544
12093
|
}
|
|
11545
12094
|
function zodShapesFromFile(file, serviceDir) {
|
|
11546
12095
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
11547
|
-
const tree = parseSource3(parserForExt3(
|
|
12096
|
+
const tree = parseSource3(parserForExt3(path55.extname(file.path)), file.content);
|
|
11548
12097
|
const out = [];
|
|
11549
12098
|
const seen = /* @__PURE__ */ new Set();
|
|
11550
12099
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -11558,11 +12107,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
11558
12107
|
seen.add(name);
|
|
11559
12108
|
const line = call.startPosition.row + 1;
|
|
11560
12109
|
out.push({
|
|
11561
|
-
infraId:
|
|
12110
|
+
infraId: infraId19("zod-schema", name),
|
|
11562
12111
|
name,
|
|
11563
12112
|
fields,
|
|
11564
12113
|
evidence: {
|
|
11565
|
-
file:
|
|
12114
|
+
file: path55.relative(serviceDir, file.path),
|
|
11566
12115
|
line,
|
|
11567
12116
|
snippet: snippet(file.content, line)
|
|
11568
12117
|
}
|
|
@@ -11798,11 +12347,11 @@ async function addFirestoreRules(graph, services) {
|
|
|
11798
12347
|
}
|
|
11799
12348
|
|
|
11800
12349
|
// src/extract/index.ts
|
|
11801
|
-
import
|
|
12350
|
+
import path57 from "path";
|
|
11802
12351
|
|
|
11803
12352
|
// src/extract/retire.ts
|
|
11804
12353
|
import { existsSync as existsSync2 } from "fs";
|
|
11805
|
-
import
|
|
12354
|
+
import path56 from "path";
|
|
11806
12355
|
import { NodeType as NodeType23, Provenance as Provenance23 } from "@neat.is/types";
|
|
11807
12356
|
function dropOrphanedFileNodes(graph) {
|
|
11808
12357
|
const orphans = [];
|
|
@@ -11836,11 +12385,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
11836
12385
|
if (edge.provenance !== Provenance23.EXTRACTED) return;
|
|
11837
12386
|
const evidenceFile = edge.evidence?.file;
|
|
11838
12387
|
if (!evidenceFile) return;
|
|
11839
|
-
if (
|
|
12388
|
+
if (path56.isAbsolute(evidenceFile)) {
|
|
11840
12389
|
if (!existsSync2(evidenceFile)) toDrop.push(id);
|
|
11841
12390
|
return;
|
|
11842
12391
|
}
|
|
11843
|
-
const found = bases.some((base) => existsSync2(
|
|
12392
|
+
const found = bases.some((base) => existsSync2(path56.join(base, evidenceFile)));
|
|
11844
12393
|
if (!found) toDrop.push(id);
|
|
11845
12394
|
});
|
|
11846
12395
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -11897,7 +12446,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
11897
12446
|
}
|
|
11898
12447
|
const droppedEntries = drainDroppedExtracted();
|
|
11899
12448
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
11900
|
-
const rejectedPath =
|
|
12449
|
+
const rejectedPath = path57.join(path57.dirname(opts.errorsPath), "rejected.ndjson");
|
|
11901
12450
|
try {
|
|
11902
12451
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
11903
12452
|
} catch (err) {
|
|
@@ -12272,7 +12821,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
12272
12821
|
|
|
12273
12822
|
// src/persist.ts
|
|
12274
12823
|
import { promises as fs25 } from "fs";
|
|
12275
|
-
import
|
|
12824
|
+
import path58 from "path";
|
|
12276
12825
|
import { NodeType as NodeType25, Provenance as Provenance25, observedEdgeId as observedEdgeId2 } from "@neat.is/types";
|
|
12277
12826
|
var SCHEMA_VERSION = 7;
|
|
12278
12827
|
function migrateV1ToV2(payload) {
|
|
@@ -12327,7 +12876,7 @@ function migrateV2ToV3(payload) {
|
|
|
12327
12876
|
return { ...payload, schemaVersion: 3 };
|
|
12328
12877
|
}
|
|
12329
12878
|
async function ensureDir(filePath) {
|
|
12330
|
-
await fs25.mkdir(
|
|
12879
|
+
await fs25.mkdir(path58.dirname(filePath), { recursive: true });
|
|
12331
12880
|
}
|
|
12332
12881
|
async function saveGraphToDisk(graph, outPath) {
|
|
12333
12882
|
await ensureDir(outPath);
|
|
@@ -12492,23 +13041,23 @@ function canonicalJson(value) {
|
|
|
12492
13041
|
}
|
|
12493
13042
|
|
|
12494
13043
|
// src/projects.ts
|
|
12495
|
-
import
|
|
13044
|
+
import path59 from "path";
|
|
12496
13045
|
function pathsForProject(project, baseDir) {
|
|
12497
13046
|
if (project === DEFAULT_PROJECT) {
|
|
12498
13047
|
return {
|
|
12499
|
-
snapshotPath:
|
|
12500
|
-
errorsPath:
|
|
12501
|
-
staleEventsPath:
|
|
12502
|
-
embeddingsCachePath:
|
|
12503
|
-
policyViolationsPath:
|
|
13048
|
+
snapshotPath: path59.join(baseDir, "graph.json"),
|
|
13049
|
+
errorsPath: path59.join(baseDir, "errors.ndjson"),
|
|
13050
|
+
staleEventsPath: path59.join(baseDir, "stale-events.ndjson"),
|
|
13051
|
+
embeddingsCachePath: path59.join(baseDir, "embeddings.json"),
|
|
13052
|
+
policyViolationsPath: path59.join(baseDir, "policy-violations.ndjson")
|
|
12504
13053
|
};
|
|
12505
13054
|
}
|
|
12506
13055
|
return {
|
|
12507
|
-
snapshotPath:
|
|
12508
|
-
errorsPath:
|
|
12509
|
-
staleEventsPath:
|
|
12510
|
-
embeddingsCachePath:
|
|
12511
|
-
policyViolationsPath:
|
|
13056
|
+
snapshotPath: path59.join(baseDir, `${project}.json`),
|
|
13057
|
+
errorsPath: path59.join(baseDir, `errors.${project}.ndjson`),
|
|
13058
|
+
staleEventsPath: path59.join(baseDir, `stale-events.${project}.ndjson`),
|
|
13059
|
+
embeddingsCachePath: path59.join(baseDir, `embeddings.${project}.json`),
|
|
13060
|
+
policyViolationsPath: path59.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
12512
13061
|
};
|
|
12513
13062
|
}
|
|
12514
13063
|
var Projects = class {
|
|
@@ -12549,7 +13098,7 @@ function parseExtraProjects(raw) {
|
|
|
12549
13098
|
// src/registry.ts
|
|
12550
13099
|
import { promises as fs27 } from "fs";
|
|
12551
13100
|
import os2 from "os";
|
|
12552
|
-
import
|
|
13101
|
+
import path60 from "path";
|
|
12553
13102
|
import {
|
|
12554
13103
|
RegistryFileSchema
|
|
12555
13104
|
} from "@neat.is/types";
|
|
@@ -12557,20 +13106,20 @@ var LOCK_TIMEOUT_MS = 5e3;
|
|
|
12557
13106
|
var LOCK_RETRY_MS = 50;
|
|
12558
13107
|
function neatHome() {
|
|
12559
13108
|
const override = process.env.NEAT_HOME;
|
|
12560
|
-
if (override && override.length > 0) return
|
|
12561
|
-
return
|
|
13109
|
+
if (override && override.length > 0) return path60.resolve(override);
|
|
13110
|
+
return path60.join(os2.homedir(), ".neat");
|
|
12562
13111
|
}
|
|
12563
13112
|
function registryPath() {
|
|
12564
|
-
return
|
|
13113
|
+
return path60.join(neatHome(), "projects.json");
|
|
12565
13114
|
}
|
|
12566
13115
|
function registryLockPath() {
|
|
12567
|
-
return
|
|
13116
|
+
return path60.join(neatHome(), "projects.json.lock");
|
|
12568
13117
|
}
|
|
12569
13118
|
function daemonPidPath() {
|
|
12570
|
-
return
|
|
13119
|
+
return path60.join(neatHome(), "neatd.pid");
|
|
12571
13120
|
}
|
|
12572
13121
|
function daemonsDir() {
|
|
12573
|
-
return
|
|
13122
|
+
return path60.join(neatHome(), "daemons");
|
|
12574
13123
|
}
|
|
12575
13124
|
function isFiniteInt(v) {
|
|
12576
13125
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -12611,7 +13160,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
12611
13160
|
const out = [];
|
|
12612
13161
|
for (const name of names) {
|
|
12613
13162
|
if (!name.endsWith(".json")) continue;
|
|
12614
|
-
const file =
|
|
13163
|
+
const file = path60.join(dir, name);
|
|
12615
13164
|
let raw;
|
|
12616
13165
|
try {
|
|
12617
13166
|
raw = await fs27.readFile(file, "utf8");
|
|
@@ -12732,7 +13281,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
12732
13281
|
}
|
|
12733
13282
|
}
|
|
12734
13283
|
async function normalizeProjectPath(input) {
|
|
12735
|
-
const resolved =
|
|
13284
|
+
const resolved = path60.resolve(input);
|
|
12736
13285
|
try {
|
|
12737
13286
|
return await fs27.realpath(resolved);
|
|
12738
13287
|
} catch {
|
|
@@ -12740,7 +13289,7 @@ async function normalizeProjectPath(input) {
|
|
|
12740
13289
|
}
|
|
12741
13290
|
}
|
|
12742
13291
|
async function writeAtomically(target, contents) {
|
|
12743
|
-
await fs27.mkdir(
|
|
13292
|
+
await fs27.mkdir(path60.dirname(target), { recursive: true });
|
|
12744
13293
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
12745
13294
|
const fd = await fs27.open(tmp, "w");
|
|
12746
13295
|
try {
|
|
@@ -12753,7 +13302,7 @@ async function writeAtomically(target, contents) {
|
|
|
12753
13302
|
}
|
|
12754
13303
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
12755
13304
|
const deadline = Date.now() + timeoutMs;
|
|
12756
|
-
await fs27.mkdir(
|
|
13305
|
+
await fs27.mkdir(path60.dirname(lockPath), { recursive: true });
|
|
12757
13306
|
let probedHolder = false;
|
|
12758
13307
|
while (true) {
|
|
12759
13308
|
try {
|
|
@@ -12949,13 +13498,13 @@ import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } f
|
|
|
12949
13498
|
|
|
12950
13499
|
// src/extend/index.ts
|
|
12951
13500
|
import { promises as fs29 } from "fs";
|
|
12952
|
-
import
|
|
13501
|
+
import path62 from "path";
|
|
12953
13502
|
import os3 from "os";
|
|
12954
13503
|
import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
|
|
12955
13504
|
|
|
12956
13505
|
// src/installers/package-manager.ts
|
|
12957
13506
|
import { promises as fs28 } from "fs";
|
|
12958
|
-
import
|
|
13507
|
+
import path61 from "path";
|
|
12959
13508
|
import { spawn } from "child_process";
|
|
12960
13509
|
var LOCKFILE_PRIORITY = [
|
|
12961
13510
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -12977,22 +13526,22 @@ async function exists2(p) {
|
|
|
12977
13526
|
}
|
|
12978
13527
|
}
|
|
12979
13528
|
async function detectPackageManager(serviceDir) {
|
|
12980
|
-
let dir =
|
|
13529
|
+
let dir = path61.resolve(serviceDir);
|
|
12981
13530
|
const stops = /* @__PURE__ */ new Set();
|
|
12982
13531
|
for (let i = 0; i < 64; i++) {
|
|
12983
13532
|
if (stops.has(dir)) break;
|
|
12984
13533
|
stops.add(dir);
|
|
12985
13534
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
12986
|
-
const lockPath =
|
|
13535
|
+
const lockPath = path61.join(dir, candidate.lockfile);
|
|
12987
13536
|
if (await exists2(lockPath)) {
|
|
12988
13537
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
12989
13538
|
}
|
|
12990
13539
|
}
|
|
12991
|
-
const parent =
|
|
13540
|
+
const parent = path61.dirname(dir);
|
|
12992
13541
|
if (parent === dir) break;
|
|
12993
13542
|
dir = parent;
|
|
12994
13543
|
}
|
|
12995
|
-
return { pm: "npm", cwd:
|
|
13544
|
+
return { pm: "npm", cwd: path61.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
12996
13545
|
}
|
|
12997
13546
|
async function runPackageManagerInstall(cmd) {
|
|
12998
13547
|
return new Promise((resolve) => {
|
|
@@ -13041,7 +13590,7 @@ async function fileExists2(p) {
|
|
|
13041
13590
|
}
|
|
13042
13591
|
}
|
|
13043
13592
|
async function readPackageJson(scanPath) {
|
|
13044
|
-
const pkgPath =
|
|
13593
|
+
const pkgPath = path62.join(scanPath, "package.json");
|
|
13045
13594
|
const raw = await fs29.readFile(pkgPath, "utf8");
|
|
13046
13595
|
return JSON.parse(raw);
|
|
13047
13596
|
}
|
|
@@ -13055,27 +13604,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
13055
13604
|
]);
|
|
13056
13605
|
async function findHookFiles(scanPath) {
|
|
13057
13606
|
const found = [];
|
|
13058
|
-
const
|
|
13607
|
+
const walk9 = async (dir) => {
|
|
13059
13608
|
const entries = await fs29.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
13060
13609
|
for (const entry of entries) {
|
|
13061
13610
|
if (entry.isDirectory()) {
|
|
13062
13611
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
13063
|
-
await
|
|
13612
|
+
await walk9(path62.join(dir, entry.name));
|
|
13064
13613
|
} else if (entry.isFile()) {
|
|
13065
13614
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
13066
|
-
const rel =
|
|
13067
|
-
found.push(rel.split(
|
|
13615
|
+
const rel = path62.relative(scanPath, path62.join(dir, entry.name));
|
|
13616
|
+
found.push(rel.split(path62.sep).join("/"));
|
|
13068
13617
|
}
|
|
13069
13618
|
}
|
|
13070
13619
|
}
|
|
13071
13620
|
};
|
|
13072
|
-
await
|
|
13621
|
+
await walk9(scanPath);
|
|
13073
13622
|
return found.sort();
|
|
13074
13623
|
}
|
|
13075
13624
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
13076
13625
|
let fallback = null;
|
|
13077
13626
|
for (const file of hookFiles) {
|
|
13078
|
-
const content = await fs29.readFile(
|
|
13627
|
+
const content = await fs29.readFile(path62.join(scanPath, file), "utf8");
|
|
13079
13628
|
const patched = splicedContent(content, snippet2);
|
|
13080
13629
|
if (patched !== null) return { file, content, patched };
|
|
13081
13630
|
if (fallback === null) fallback = { file, content };
|
|
@@ -13083,11 +13632,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
13083
13632
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
13084
13633
|
}
|
|
13085
13634
|
function extendLogPath() {
|
|
13086
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
13635
|
+
return process.env.NEAT_EXTEND_LOG ?? path62.join(os3.homedir(), ".neat", "extend-log.ndjson");
|
|
13087
13636
|
}
|
|
13088
13637
|
async function appendExtendLog(entry) {
|
|
13089
13638
|
const logPath = extendLogPath();
|
|
13090
|
-
await fs29.mkdir(
|
|
13639
|
+
await fs29.mkdir(path62.dirname(logPath), { recursive: true });
|
|
13091
13640
|
await fs29.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
13092
13641
|
}
|
|
13093
13642
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -13146,7 +13695,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
13146
13695
|
}
|
|
13147
13696
|
async function describeProjectInstrumentation(ctx) {
|
|
13148
13697
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
13149
|
-
const envNeat = await fileExists2(
|
|
13698
|
+
const envNeat = await fileExists2(path62.join(ctx.scanPath, ".env.neat"));
|
|
13150
13699
|
const registryInstrPackages = new Set(
|
|
13151
13700
|
registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
13152
13701
|
);
|
|
@@ -13168,7 +13717,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
13168
13717
|
);
|
|
13169
13718
|
}
|
|
13170
13719
|
for (const file of hookFiles) {
|
|
13171
|
-
const content = await fs29.readFile(
|
|
13720
|
+
const content = await fs29.readFile(path62.join(ctx.scanPath, file), "utf8");
|
|
13172
13721
|
if (content.includes(args.registration_snippet)) {
|
|
13173
13722
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
13174
13723
|
}
|
|
@@ -13180,10 +13729,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
13180
13729
|
);
|
|
13181
13730
|
}
|
|
13182
13731
|
const primaryFile = primary.file;
|
|
13183
|
-
const primaryPath =
|
|
13732
|
+
const primaryPath = path62.join(ctx.scanPath, primaryFile);
|
|
13184
13733
|
const filesTouched = [];
|
|
13185
13734
|
const depsAdded = [];
|
|
13186
|
-
const pkgPath =
|
|
13735
|
+
const pkgPath = path62.join(ctx.scanPath, "package.json");
|
|
13187
13736
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
13188
13737
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
13189
13738
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -13222,7 +13771,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
13222
13771
|
};
|
|
13223
13772
|
}
|
|
13224
13773
|
for (const file of hookFiles) {
|
|
13225
|
-
const content = await fs29.readFile(
|
|
13774
|
+
const content = await fs29.readFile(path62.join(ctx.scanPath, file), "utf8");
|
|
13226
13775
|
if (content.includes(args.registration_snippet)) {
|
|
13227
13776
|
return {
|
|
13228
13777
|
library: args.library,
|
|
@@ -13263,7 +13812,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
13263
13812
|
if (!match) {
|
|
13264
13813
|
return { undone: false, message: "no apply found for library" };
|
|
13265
13814
|
}
|
|
13266
|
-
const pkgPath =
|
|
13815
|
+
const pkgPath = path62.join(ctx.scanPath, "package.json");
|
|
13267
13816
|
if (await fileExists2(pkgPath)) {
|
|
13268
13817
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
13269
13818
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -13274,7 +13823,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
13274
13823
|
}
|
|
13275
13824
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
13276
13825
|
for (const file of hookFiles) {
|
|
13277
|
-
const filePath =
|
|
13826
|
+
const filePath = path62.join(ctx.scanPath, file);
|
|
13278
13827
|
const content = await fs29.readFile(filePath, "utf8");
|
|
13279
13828
|
if (content.includes(match.registration_snippet)) {
|
|
13280
13829
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -13389,7 +13938,7 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
13389
13938
|
|
|
13390
13939
|
// src/connectors-config.ts
|
|
13391
13940
|
import os4 from "os";
|
|
13392
|
-
import
|
|
13941
|
+
import path63 from "path";
|
|
13393
13942
|
import { promises as fs30 } from "fs";
|
|
13394
13943
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
13395
13944
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -13404,11 +13953,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
13404
13953
|
};
|
|
13405
13954
|
function neatHome2() {
|
|
13406
13955
|
const override = process.env.NEAT_HOME;
|
|
13407
|
-
if (override && override.length > 0) return
|
|
13408
|
-
return
|
|
13956
|
+
if (override && override.length > 0) return path63.resolve(override);
|
|
13957
|
+
return path63.join(os4.homedir(), ".neat");
|
|
13409
13958
|
}
|
|
13410
13959
|
function connectorsConfigPath(home = neatHome2()) {
|
|
13411
|
-
return
|
|
13960
|
+
return path63.join(home, "connectors.json");
|
|
13412
13961
|
}
|
|
13413
13962
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
13414
13963
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -13539,7 +14088,7 @@ function connectorMatchesProject(entry, project) {
|
|
|
13539
14088
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
13540
14089
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
13541
14090
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
13542
|
-
return
|
|
14091
|
+
return path63.join(home, "connectors.json.lock");
|
|
13543
14092
|
}
|
|
13544
14093
|
function isEnvRef(value) {
|
|
13545
14094
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -13552,7 +14101,7 @@ function redactCredentialRef(ref) {
|
|
|
13552
14101
|
return out;
|
|
13553
14102
|
}
|
|
13554
14103
|
async function writeConfigAtomically0600(file, contents) {
|
|
13555
|
-
await fs30.mkdir(
|
|
14104
|
+
await fs30.mkdir(path63.dirname(file), { recursive: true });
|
|
13556
14105
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
13557
14106
|
const fd = await fs30.open(tmp, "w", 384);
|
|
13558
14107
|
try {
|
|
@@ -13566,7 +14115,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
13566
14115
|
}
|
|
13567
14116
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
13568
14117
|
const deadline = Date.now() + timeoutMs;
|
|
13569
|
-
await fs30.mkdir(
|
|
14118
|
+
await fs30.mkdir(path63.dirname(lockPath), { recursive: true });
|
|
13570
14119
|
for (; ; ) {
|
|
13571
14120
|
try {
|
|
13572
14121
|
const fd = await fs30.open(lockPath, "wx");
|
|
@@ -14201,10 +14750,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
14201
14750
|
// src/connectors/supabase/map.ts
|
|
14202
14751
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
14203
14752
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
14204
|
-
function targetFromRestPath(
|
|
14205
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
14753
|
+
function targetFromRestPath(path64) {
|
|
14754
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path64);
|
|
14206
14755
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
14207
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
14756
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path64);
|
|
14208
14757
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
14209
14758
|
return null;
|
|
14210
14759
|
}
|
|
@@ -14313,21 +14862,21 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
14313
14862
|
}
|
|
14314
14863
|
|
|
14315
14864
|
// src/connectors/supabase/resolve.ts
|
|
14316
|
-
import { EdgeType as EdgeType26, infraId as
|
|
14865
|
+
import { EdgeType as EdgeType26, infraId as infraId20 } from "@neat.is/types";
|
|
14317
14866
|
function createSupabaseResolveTarget(graph, config) {
|
|
14318
14867
|
return (signal, _ctx) => {
|
|
14319
14868
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
14320
14869
|
return null;
|
|
14321
14870
|
}
|
|
14322
|
-
const subResourceId =
|
|
14871
|
+
const subResourceId = infraId20(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
14323
14872
|
if (graph.hasNode(subResourceId)) {
|
|
14324
14873
|
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: EdgeType26.CALLS };
|
|
14325
14874
|
}
|
|
14326
|
-
const bareResourceId =
|
|
14875
|
+
const bareResourceId = infraId20(signal.targetKind, signal.targetName);
|
|
14327
14876
|
if (graph.hasNode(bareResourceId)) {
|
|
14328
14877
|
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: EdgeType26.CALLS };
|
|
14329
14878
|
}
|
|
14330
|
-
const projectLevelId =
|
|
14879
|
+
const projectLevelId = infraId20("supabase", config.nodeRef);
|
|
14331
14880
|
if (graph.hasNode(projectLevelId)) {
|
|
14332
14881
|
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: EdgeType26.CALLS };
|
|
14333
14882
|
}
|
|
@@ -14799,9 +15348,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
14799
15348
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
14800
15349
|
if (secondSep === -1) return null;
|
|
14801
15350
|
const method = rest.slice(0, secondSep);
|
|
14802
|
-
const
|
|
14803
|
-
if (!resourceName || !method || !
|
|
14804
|
-
return { resourceName, method, path:
|
|
15351
|
+
const path64 = rest.slice(secondSep + 1);
|
|
15352
|
+
if (!resourceName || !method || !path64) return null;
|
|
15353
|
+
return { resourceName, method, path: path64 };
|
|
14805
15354
|
}
|
|
14806
15355
|
function resourceNameFor(type, labels) {
|
|
14807
15356
|
if (!labels) return null;
|
|
@@ -14839,14 +15388,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
14839
15388
|
if (!req) return null;
|
|
14840
15389
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
14841
15390
|
const method = req.requestMethod.toUpperCase();
|
|
14842
|
-
const
|
|
14843
|
-
if (
|
|
15391
|
+
const path64 = pathFromRequestUrl(req.requestUrl);
|
|
15392
|
+
if (path64 === null) return null;
|
|
14844
15393
|
const timestamp = entry.timestamp;
|
|
14845
15394
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
14846
15395
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
14847
15396
|
return {
|
|
14848
15397
|
targetKind: resourceType,
|
|
14849
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
15398
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path64 }),
|
|
14850
15399
|
callCount: 1,
|
|
14851
15400
|
errorCount: isError ? 1 : 0,
|
|
14852
15401
|
lastObservedIso: timestamp
|
|
@@ -14932,7 +15481,7 @@ function createFirebaseConnector(graph, serviceMap) {
|
|
|
14932
15481
|
}
|
|
14933
15482
|
|
|
14934
15483
|
// src/connectors/cloudflare/connector.ts
|
|
14935
|
-
import { EdgeType as EdgeType29, NodeType as NodeType29, fileId as fileId4, infraId as
|
|
15484
|
+
import { EdgeType as EdgeType29, NodeType as NodeType29, fileId as fileId4, infraId as infraId21 } from "@neat.is/types";
|
|
14936
15485
|
|
|
14937
15486
|
// src/connectors/cloudflare/client.ts
|
|
14938
15487
|
import { randomUUID } from "crypto";
|
|
@@ -15043,7 +15592,7 @@ function mapEventToSignal(event) {
|
|
|
15043
15592
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
15044
15593
|
const statusCode = metadata?.statusCode;
|
|
15045
15594
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
15046
|
-
const
|
|
15595
|
+
const path64 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
15047
15596
|
return {
|
|
15048
15597
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
15049
15598
|
targetName: scriptName,
|
|
@@ -15051,7 +15600,7 @@ function mapEventToSignal(event) {
|
|
|
15051
15600
|
errorCount: isError ? 1 : 0,
|
|
15052
15601
|
lastObservedIso: observedAt.toISOString(),
|
|
15053
15602
|
method,
|
|
15054
|
-
...
|
|
15603
|
+
...path64 ? { path: path64 } : {},
|
|
15055
15604
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
15056
15605
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
15057
15606
|
};
|
|
@@ -15097,8 +15646,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
15097
15646
|
});
|
|
15098
15647
|
return found;
|
|
15099
15648
|
}
|
|
15100
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
15101
|
-
const normalizedPath = normalizePathTemplate(
|
|
15649
|
+
function findMatchingRouteNode(graph, serviceName, method, path64) {
|
|
15650
|
+
const normalizedPath = normalizePathTemplate(path64);
|
|
15102
15651
|
let found = null;
|
|
15103
15652
|
graph.forEachNode((id, attrs) => {
|
|
15104
15653
|
if (found) return;
|
|
@@ -15115,10 +15664,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
15115
15664
|
return (signal) => {
|
|
15116
15665
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
15117
15666
|
const scriptName = signal.targetName;
|
|
15118
|
-
const { method, path:
|
|
15667
|
+
const { method, path: path64 } = signal;
|
|
15119
15668
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
15120
|
-
if (!method || !
|
|
15121
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
15669
|
+
if (!method || !path64) return wholeFileId;
|
|
15670
|
+
return findMatchingRouteNode(graph, serviceName, method, path64) ?? wholeFileId;
|
|
15122
15671
|
};
|
|
15123
15672
|
const mapping = config.workers?.[scriptName];
|
|
15124
15673
|
if (mapping) {
|
|
@@ -15139,7 +15688,7 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
15139
15688
|
};
|
|
15140
15689
|
}
|
|
15141
15690
|
return {
|
|
15142
|
-
targetNodeId:
|
|
15691
|
+
targetNodeId: infraId21("cloudflare-worker", scriptName),
|
|
15143
15692
|
serviceName: scriptName,
|
|
15144
15693
|
edgeType: EdgeType29.CALLS,
|
|
15145
15694
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
@@ -15323,12 +15872,12 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
15323
15872
|
}
|
|
15324
15873
|
|
|
15325
15874
|
// src/connectors/neon/resolve.ts
|
|
15326
|
-
import { EdgeType as EdgeType30, infraId as
|
|
15875
|
+
import { EdgeType as EdgeType30, infraId as infraId22 } from "@neat.is/types";
|
|
15327
15876
|
function createNeonResolveTarget(config) {
|
|
15328
15877
|
return (signal) => {
|
|
15329
15878
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
15330
15879
|
return {
|
|
15331
|
-
targetNodeId:
|
|
15880
|
+
targetNodeId: infraId22("sql-table", signal.targetName),
|
|
15332
15881
|
serviceName: config.serviceName,
|
|
15333
15882
|
edgeType: EdgeType30.CALLS,
|
|
15334
15883
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
@@ -15448,9 +15997,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
15448
15997
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
15449
15998
|
if (secondSep === -1) return null;
|
|
15450
15999
|
const method = rest.slice(0, secondSep);
|
|
15451
|
-
const
|
|
15452
|
-
if (!serviceName || !method || !
|
|
15453
|
-
return { serviceName, method, path:
|
|
16000
|
+
const path64 = rest.slice(secondSep + 1);
|
|
16001
|
+
if (!serviceName || !method || !path64) return null;
|
|
16002
|
+
return { serviceName, method, path: path64 };
|
|
15454
16003
|
}
|
|
15455
16004
|
|
|
15456
16005
|
// src/connectors/cloud-run/map.ts
|
|
@@ -15479,14 +16028,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
15479
16028
|
if (!req) return null;
|
|
15480
16029
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
15481
16030
|
const method = req.requestMethod.toUpperCase();
|
|
15482
|
-
const
|
|
15483
|
-
if (
|
|
16031
|
+
const path64 = pathFromRequestUrl2(req.requestUrl);
|
|
16032
|
+
if (path64 === null) return null;
|
|
15484
16033
|
const timestamp = entry.timestamp;
|
|
15485
16034
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
15486
16035
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
15487
16036
|
return {
|
|
15488
16037
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
15489
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
16038
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path64 }),
|
|
15490
16039
|
callCount: 1,
|
|
15491
16040
|
errorCount: isError ? 1 : 0,
|
|
15492
16041
|
lastObservedIso: timestamp
|
|
@@ -15502,7 +16051,7 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
15502
16051
|
}
|
|
15503
16052
|
|
|
15504
16053
|
// src/connectors/cloud-run/resolve.ts
|
|
15505
|
-
import { EdgeType as EdgeType31, NodeType as NodeType30, infraId as
|
|
16054
|
+
import { EdgeType as EdgeType31, NodeType as NodeType30, infraId as infraId23 } from "@neat.is/types";
|
|
15506
16055
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
15507
16056
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
15508
16057
|
let found = null;
|
|
@@ -15524,21 +16073,21 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
15524
16073
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
15525
16074
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
15526
16075
|
if (!identity) return null;
|
|
15527
|
-
const { serviceName: gcpServiceName, method, path:
|
|
16076
|
+
const { serviceName: gcpServiceName, method, path: path64 } = identity;
|
|
15528
16077
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
15529
16078
|
if (mappedService) {
|
|
15530
16079
|
const routeNodeId = findMatchingRouteNode2(
|
|
15531
16080
|
graph,
|
|
15532
16081
|
mappedService,
|
|
15533
16082
|
method,
|
|
15534
|
-
normalizePathTemplate(
|
|
16083
|
+
normalizePathTemplate(path64)
|
|
15535
16084
|
);
|
|
15536
16085
|
if (routeNodeId) {
|
|
15537
16086
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType31.CALLS };
|
|
15538
16087
|
}
|
|
15539
16088
|
}
|
|
15540
16089
|
return {
|
|
15541
|
-
targetNodeId:
|
|
16090
|
+
targetNodeId: infraId23(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
15542
16091
|
serviceName: mappedService ?? gcpServiceName,
|
|
15543
16092
|
edgeType: EdgeType31.CALLS,
|
|
15544
16093
|
ensureInfraNode: {
|
|
@@ -15870,17 +16419,17 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
15870
16419
|
}
|
|
15871
16420
|
|
|
15872
16421
|
// src/connectors/planetscale/resolve.ts
|
|
15873
|
-
import { EdgeType as EdgeType33, infraId as
|
|
16422
|
+
import { EdgeType as EdgeType33, infraId as infraId24 } from "@neat.is/types";
|
|
15874
16423
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
15875
16424
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
15876
16425
|
const databaseName = `${config.organization}/${config.database}`;
|
|
15877
16426
|
return (signal, _ctx) => {
|
|
15878
16427
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
15879
|
-
const tableId =
|
|
16428
|
+
const tableId = infraId24("sql-table", signal.targetName);
|
|
15880
16429
|
if (graph.hasNode(tableId)) {
|
|
15881
16430
|
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: EdgeType33.CALLS };
|
|
15882
16431
|
}
|
|
15883
|
-
const providerId =
|
|
16432
|
+
const providerId = infraId24(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
15884
16433
|
return {
|
|
15885
16434
|
targetNodeId: providerId,
|
|
15886
16435
|
serviceName: config.serviceName,
|
|
@@ -17385,4 +17934,4 @@ export {
|
|
|
17385
17934
|
deprovisionConnector,
|
|
17386
17935
|
buildApi
|
|
17387
17936
|
};
|
|
17388
|
-
//# sourceMappingURL=chunk-
|
|
17937
|
+
//# sourceMappingURL=chunk-UI3AFJAF.js.map
|