@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.
package/dist/neatd.js CHANGED
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  reconcileDaemonRecordSync,
4
4
  startDaemon
5
- } from "./chunk-5PVQJLPR.js";
5
+ } from "./chunk-3YUNROLT.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-CS4GHQO3.js";
9
+ } from "./chunk-7C6ZULNU.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
package/dist/server.cjs CHANGED
@@ -5863,6 +5863,7 @@ init_cjs_shims();
5863
5863
  var import_node_path24 = __toESM(require("path"), 1);
5864
5864
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
5865
5865
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
5866
+ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
5866
5867
  var import_types12 = require("@neat.is/types");
5867
5868
  var PARSE_CHUNK2 = 16384;
5868
5869
  function parseSource2(parser, source) {
@@ -5875,6 +5876,11 @@ function makeJsParser2() {
5875
5876
  p.setLanguage(import_tree_sitter_javascript2.default);
5876
5877
  return p;
5877
5878
  }
5879
+ function makePyParser2() {
5880
+ const p = new import_tree_sitter2.default();
5881
+ p.setLanguage(import_tree_sitter_python2.default);
5882
+ return p;
5883
+ }
5878
5884
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
5879
5885
  "get",
5880
5886
  "post",
@@ -5887,6 +5893,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
5887
5893
  ]);
5888
5894
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
5889
5895
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
5896
+ var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
5890
5897
  function canonicalizeTemplate(raw) {
5891
5898
  let p = raw.split("?")[0].split("#")[0];
5892
5899
  if (!p.startsWith("/")) p = "/" + p;
@@ -6114,8 +6121,102 @@ function nextRoutesFromFile(source, relFile, parser) {
6114
6121
  }
6115
6122
  return [];
6116
6123
  }
6124
+ function pyStaticStringText(node) {
6125
+ if (node.type !== "string") return null;
6126
+ for (let i = 0; i < node.namedChildCount; i++) {
6127
+ const child = node.namedChild(i);
6128
+ if (child?.type === "interpolation") return null;
6129
+ if (child?.type === "string_content") return child.text;
6130
+ }
6131
+ return "";
6132
+ }
6133
+ function keywordArrayStrings(argsNode, key) {
6134
+ for (let i = 0; i < argsNode.namedChildCount; i++) {
6135
+ const arg = argsNode.namedChild(i);
6136
+ if (arg?.type !== "keyword_argument") continue;
6137
+ if (arg.childForFieldName("name")?.text !== key) continue;
6138
+ const val = arg.childForFieldName("value");
6139
+ if (!val || val.type !== "list") return [];
6140
+ const out = [];
6141
+ for (let j = 0; j < val.namedChildCount; j++) {
6142
+ const el = val.namedChild(j);
6143
+ if (el?.type === "string") {
6144
+ const s = pyStaticStringText(el);
6145
+ if (s) out.push(s);
6146
+ }
6147
+ }
6148
+ return out;
6149
+ }
6150
+ return [];
6151
+ }
6152
+ function collectApiRouterPrefixes(root) {
6153
+ const prefixes = /* @__PURE__ */ new Map();
6154
+ walk(root, (node) => {
6155
+ if (node.type !== "assignment") return;
6156
+ const right = node.childForFieldName("right");
6157
+ if (!right || right.type !== "call") return;
6158
+ const fn = right.childForFieldName("function");
6159
+ if (!fn) return;
6160
+ const ctor = fn.type === "attribute" ? fn.childForFieldName("attribute")?.text : fn.text;
6161
+ if (ctor !== "APIRouter") return;
6162
+ const left = node.childForFieldName("left");
6163
+ if (!left || left.type !== "identifier") return;
6164
+ const args = right.childForFieldName("arguments");
6165
+ if (!args) return;
6166
+ for (let i = 0; i < args.namedChildCount; i++) {
6167
+ const arg = args.namedChild(i);
6168
+ if (arg?.type !== "keyword_argument") continue;
6169
+ if (arg.childForFieldName("name")?.text !== "prefix") continue;
6170
+ const val = arg.childForFieldName("value");
6171
+ const p = val ? pyStaticStringText(val) : null;
6172
+ if (p !== null) prefixes.set(left.text, p);
6173
+ }
6174
+ });
6175
+ return prefixes;
6176
+ }
6177
+ function fastapiRoutesFromSource(source, parser) {
6178
+ const tree = parseSource2(parser, source);
6179
+ const prefixes = collectApiRouterPrefixes(tree.rootNode);
6180
+ const out = [];
6181
+ walk(tree.rootNode, (node) => {
6182
+ if (node.type !== "decorator") return;
6183
+ const call = node.namedChild(0);
6184
+ if (!call || call.type !== "call") return;
6185
+ const fn = call.childForFieldName("function");
6186
+ if (!fn || fn.type !== "attribute") return;
6187
+ const method = fn.childForFieldName("attribute")?.text?.toLowerCase();
6188
+ if (!method) return;
6189
+ const isVerb = FASTAPI_METHODS.has(method);
6190
+ if (!isVerb && method !== "api_route") return;
6191
+ const args = call.childForFieldName("arguments");
6192
+ const first = args?.namedChild(0);
6193
+ if (!first || first.type !== "string") return;
6194
+ const rawPath = pyStaticStringText(first);
6195
+ if (rawPath === null || !rawPath.startsWith("/")) return;
6196
+ const obj = fn.childForFieldName("object")?.text;
6197
+ const prefix = obj ? prefixes.get(obj) ?? "" : "";
6198
+ const pathTemplate = canonicalizeTemplate(prefix + rawPath);
6199
+ const line = node.startPosition.row + 1;
6200
+ if (isVerb) {
6201
+ out.push({ method: method.toUpperCase(), pathTemplate, line, framework: "fastapi" });
6202
+ return;
6203
+ }
6204
+ const methods = keywordArrayStrings(args, "methods");
6205
+ const list = methods.length > 0 ? methods : ["ALL"];
6206
+ for (const m of list) {
6207
+ out.push({
6208
+ method: m === "ALL" ? "ALL" : m.toUpperCase(),
6209
+ pathTemplate,
6210
+ line,
6211
+ framework: "fastapi"
6212
+ });
6213
+ }
6214
+ });
6215
+ return out;
6216
+ }
6117
6217
  async function addRoutes(graph, services) {
6118
6218
  const jsParser = makeJsParser2();
6219
+ const pyParser = makePyParser2();
6119
6220
  let nodesAdded = 0;
6120
6221
  let edgesAdded = 0;
6121
6222
  for (const service of services) {
@@ -6127,15 +6228,20 @@ async function addRoutes(graph, services) {
6127
6228
  const hasFastify = deps["fastify"] !== void 0;
6128
6229
  const hasHono = deps["hono"] !== void 0;
6129
6230
  const hasNext = deps["next"] !== void 0;
6130
- if (!hasExpress && !hasFastify && !hasHono && !hasNext) continue;
6231
+ const hasFastapi = deps["fastapi"] !== void 0;
6232
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasFastapi) continue;
6131
6233
  const files = await loadSourceFiles(service.dir);
6132
6234
  for (const file of files) {
6133
6235
  if (isTestPath(file.path)) continue;
6134
- if (!JS_ROUTE_EXTENSIONS.has(import_node_path24.default.extname(file.path))) continue;
6236
+ const ext = import_node_path24.default.extname(file.path);
6237
+ const isPy = ext === ".py";
6238
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy) continue;
6135
6239
  const relFile = toPosix2(import_node_path24.default.relative(service.dir, file.path));
6136
6240
  let routes;
6137
6241
  try {
6138
- if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
6242
+ if (isPy) {
6243
+ routes = hasFastapi ? fastapiRoutesFromSource(file.content, pyParser) : [];
6244
+ } else if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
6139
6245
  routes = nextRoutesFromFile(file.content, relFile, jsParser);
6140
6246
  } else if (hasExpress || hasFastify || hasHono) {
6141
6247
  routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify, hasHono);
@@ -6319,7 +6425,7 @@ init_cjs_shims();
6319
6425
  var import_node_path26 = __toESM(require("path"), 1);
6320
6426
  var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
6321
6427
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
6322
- var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
6428
+ var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
6323
6429
  var import_types14 = require("@neat.is/types");
6324
6430
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
6325
6431
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
@@ -6374,14 +6480,14 @@ function makeJsParser3() {
6374
6480
  p.setLanguage(import_tree_sitter_javascript3.default);
6375
6481
  return p;
6376
6482
  }
6377
- function makePyParser2() {
6483
+ function makePyParser3() {
6378
6484
  const p = new import_tree_sitter3.default();
6379
- p.setLanguage(import_tree_sitter_python2.default);
6485
+ p.setLanguage(import_tree_sitter_python3.default);
6380
6486
  return p;
6381
6487
  }
6382
6488
  async function addHttpCallEdges(graph, services) {
6383
6489
  const jsParser = makeJsParser3();
6384
- const pyParser = makePyParser2();
6490
+ const pyParser = makePyParser3();
6385
6491
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
6386
6492
  let nodesAdded = 0;
6387
6493
  let edgesAdded = 0;