@neat.is/core 0.6.0 → 0.6.1

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.
@@ -31,7 +31,7 @@ import {
31
31
  touchLastSeen,
32
32
  upsertObservedEdge,
33
33
  writeAtomically
34
- } from "./chunk-CS4GHQO3.js";
34
+ } from "./chunk-7C6ZULNU.js";
35
35
  import {
36
36
  assertBindAuthority,
37
37
  buildOtelReceiver,
@@ -2765,4 +2765,4 @@ export {
2765
2765
  resolveHost,
2766
2766
  startDaemon
2767
2767
  };
2768
- //# sourceMappingURL=chunk-5PVQJLPR.js.map
2768
+ //# sourceMappingURL=chunk-3YUNROLT.js.map
@@ -4653,6 +4653,7 @@ async function addConfigNodes(graph, services, scanPath) {
4653
4653
  import path22 from "path";
4654
4654
  import Parser2 from "tree-sitter";
4655
4655
  import JavaScript2 from "tree-sitter-javascript";
4656
+ import Python2 from "tree-sitter-python";
4656
4657
  import {
4657
4658
  EdgeType as EdgeType8,
4658
4659
  NodeType as NodeType9,
@@ -4672,6 +4673,11 @@ function makeJsParser2() {
4672
4673
  p.setLanguage(JavaScript2);
4673
4674
  return p;
4674
4675
  }
4676
+ function makePyParser2() {
4677
+ const p = new Parser2();
4678
+ p.setLanguage(Python2);
4679
+ return p;
4680
+ }
4675
4681
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
4676
4682
  "get",
4677
4683
  "post",
@@ -4684,6 +4690,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
4684
4690
  ]);
4685
4691
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
4686
4692
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
4693
+ var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
4687
4694
  function canonicalizeTemplate(raw) {
4688
4695
  let p = raw.split("?")[0].split("#")[0];
4689
4696
  if (!p.startsWith("/")) p = "/" + p;
@@ -4911,8 +4918,102 @@ function nextRoutesFromFile(source, relFile, parser) {
4911
4918
  }
4912
4919
  return [];
4913
4920
  }
4921
+ function pyStaticStringText(node) {
4922
+ if (node.type !== "string") return null;
4923
+ for (let i = 0; i < node.namedChildCount; i++) {
4924
+ const child = node.namedChild(i);
4925
+ if (child?.type === "interpolation") return null;
4926
+ if (child?.type === "string_content") return child.text;
4927
+ }
4928
+ return "";
4929
+ }
4930
+ function keywordArrayStrings(argsNode, key) {
4931
+ for (let i = 0; i < argsNode.namedChildCount; i++) {
4932
+ const arg = argsNode.namedChild(i);
4933
+ if (arg?.type !== "keyword_argument") continue;
4934
+ if (arg.childForFieldName("name")?.text !== key) continue;
4935
+ const val = arg.childForFieldName("value");
4936
+ if (!val || val.type !== "list") return [];
4937
+ const out = [];
4938
+ for (let j = 0; j < val.namedChildCount; j++) {
4939
+ const el = val.namedChild(j);
4940
+ if (el?.type === "string") {
4941
+ const s = pyStaticStringText(el);
4942
+ if (s) out.push(s);
4943
+ }
4944
+ }
4945
+ return out;
4946
+ }
4947
+ return [];
4948
+ }
4949
+ function collectApiRouterPrefixes(root) {
4950
+ const prefixes = /* @__PURE__ */ new Map();
4951
+ walk(root, (node) => {
4952
+ if (node.type !== "assignment") return;
4953
+ const right = node.childForFieldName("right");
4954
+ if (!right || right.type !== "call") return;
4955
+ const fn = right.childForFieldName("function");
4956
+ if (!fn) return;
4957
+ const ctor = fn.type === "attribute" ? fn.childForFieldName("attribute")?.text : fn.text;
4958
+ if (ctor !== "APIRouter") return;
4959
+ const left = node.childForFieldName("left");
4960
+ if (!left || left.type !== "identifier") return;
4961
+ const args = right.childForFieldName("arguments");
4962
+ if (!args) return;
4963
+ for (let i = 0; i < args.namedChildCount; i++) {
4964
+ const arg = args.namedChild(i);
4965
+ if (arg?.type !== "keyword_argument") continue;
4966
+ if (arg.childForFieldName("name")?.text !== "prefix") continue;
4967
+ const val = arg.childForFieldName("value");
4968
+ const p = val ? pyStaticStringText(val) : null;
4969
+ if (p !== null) prefixes.set(left.text, p);
4970
+ }
4971
+ });
4972
+ return prefixes;
4973
+ }
4974
+ function fastapiRoutesFromSource(source, parser) {
4975
+ const tree = parseSource2(parser, source);
4976
+ const prefixes = collectApiRouterPrefixes(tree.rootNode);
4977
+ const out = [];
4978
+ walk(tree.rootNode, (node) => {
4979
+ if (node.type !== "decorator") return;
4980
+ const call = node.namedChild(0);
4981
+ if (!call || call.type !== "call") return;
4982
+ const fn = call.childForFieldName("function");
4983
+ if (!fn || fn.type !== "attribute") return;
4984
+ const method = fn.childForFieldName("attribute")?.text?.toLowerCase();
4985
+ if (!method) return;
4986
+ const isVerb = FASTAPI_METHODS.has(method);
4987
+ if (!isVerb && method !== "api_route") return;
4988
+ const args = call.childForFieldName("arguments");
4989
+ const first = args?.namedChild(0);
4990
+ if (!first || first.type !== "string") return;
4991
+ const rawPath = pyStaticStringText(first);
4992
+ if (rawPath === null || !rawPath.startsWith("/")) return;
4993
+ const obj = fn.childForFieldName("object")?.text;
4994
+ const prefix = obj ? prefixes.get(obj) ?? "" : "";
4995
+ const pathTemplate = canonicalizeTemplate(prefix + rawPath);
4996
+ const line = node.startPosition.row + 1;
4997
+ if (isVerb) {
4998
+ out.push({ method: method.toUpperCase(), pathTemplate, line, framework: "fastapi" });
4999
+ return;
5000
+ }
5001
+ const methods = keywordArrayStrings(args, "methods");
5002
+ const list = methods.length > 0 ? methods : ["ALL"];
5003
+ for (const m of list) {
5004
+ out.push({
5005
+ method: m === "ALL" ? "ALL" : m.toUpperCase(),
5006
+ pathTemplate,
5007
+ line,
5008
+ framework: "fastapi"
5009
+ });
5010
+ }
5011
+ });
5012
+ return out;
5013
+ }
4914
5014
  async function addRoutes(graph, services) {
4915
5015
  const jsParser = makeJsParser2();
5016
+ const pyParser = makePyParser2();
4916
5017
  let nodesAdded = 0;
4917
5018
  let edgesAdded = 0;
4918
5019
  for (const service of services) {
@@ -4924,15 +5025,20 @@ async function addRoutes(graph, services) {
4924
5025
  const hasFastify = deps["fastify"] !== void 0;
4925
5026
  const hasHono = deps["hono"] !== void 0;
4926
5027
  const hasNext = deps["next"] !== void 0;
4927
- if (!hasExpress && !hasFastify && !hasHono && !hasNext) continue;
5028
+ const hasFastapi = deps["fastapi"] !== void 0;
5029
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasFastapi) continue;
4928
5030
  const files = await loadSourceFiles(service.dir);
4929
5031
  for (const file of files) {
4930
5032
  if (isTestPath(file.path)) continue;
4931
- if (!JS_ROUTE_EXTENSIONS.has(path22.extname(file.path))) continue;
5033
+ const ext = path22.extname(file.path);
5034
+ const isPy = ext === ".py";
5035
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy) continue;
4932
5036
  const relFile = toPosix2(path22.relative(service.dir, file.path));
4933
5037
  let routes;
4934
5038
  try {
4935
- if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
5039
+ if (isPy) {
5040
+ routes = hasFastapi ? fastapiRoutesFromSource(file.content, pyParser) : [];
5041
+ } else if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
4936
5042
  routes = nextRoutesFromFile(file.content, relFile, jsParser);
4937
5043
  } else if (hasExpress || hasFastify || hasHono) {
4938
5044
  routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify, hasHono);
@@ -5126,7 +5232,7 @@ import {
5126
5232
  import path24 from "path";
5127
5233
  import Parser3 from "tree-sitter";
5128
5234
  import JavaScript3 from "tree-sitter-javascript";
5129
- import Python2 from "tree-sitter-python";
5235
+ import Python3 from "tree-sitter-python";
5130
5236
  import {
5131
5237
  EdgeType as EdgeType10,
5132
5238
  Provenance as Provenance9,
@@ -5186,14 +5292,14 @@ function makeJsParser3() {
5186
5292
  p.setLanguage(JavaScript3);
5187
5293
  return p;
5188
5294
  }
5189
- function makePyParser2() {
5295
+ function makePyParser3() {
5190
5296
  const p = new Parser3();
5191
- p.setLanguage(Python2);
5297
+ p.setLanguage(Python3);
5192
5298
  return p;
5193
5299
  }
5194
5300
  async function addHttpCallEdges(graph, services) {
5195
5301
  const jsParser = makeJsParser3();
5196
- const pyParser = makePyParser2();
5302
+ const pyParser = makePyParser3();
5197
5303
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
5198
5304
  let nodesAdded = 0;
5199
5305
  let edgesAdded = 0;
@@ -9743,4 +9849,4 @@ export {
9743
9849
  recordConnectorPoll,
9744
9850
  buildApi
9745
9851
  };
9746
- //# sourceMappingURL=chunk-CS4GHQO3.js.map
9852
+ //# sourceMappingURL=chunk-7C6ZULNU.js.map