@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/neatd.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req2, reply, done) => {
64
- const path69 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path69) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path69)) {
64
+ const path70 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path70) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path70)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -343,7 +343,7 @@ function pickEnv(spanAttrs, resourceAttrs) {
343
343
  return ENV_FALLBACK;
344
344
  }
345
345
  function normalizeDbSystem(attrs) {
346
- const raw = attrs["db.system"];
346
+ const raw = attrs["db.system"] ?? attrs["db.system.name"];
347
347
  if (typeof raw !== "string") return void 0;
348
348
  return raw === "mongoose" ? "mongodb" : raw;
349
349
  }
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
415
415
  const v = attrs[key];
416
416
  if (typeof v === "string" && v.length > 0) {
417
417
  const q = v.indexOf("?");
418
- const path69 = q === -1 ? v : v.slice(0, q);
419
- if (path69.length > 0) return path69;
418
+ const path70 = q === -1 ? v : v.slice(0, q);
419
+ if (path70.length > 0) return path70;
420
420
  }
421
421
  }
422
422
  return void 0;
@@ -435,6 +435,9 @@ function parseOtlpRequest(body) {
435
435
  for (const ss of rs.scopeSpans ?? []) {
436
436
  for (const span of ss.spans ?? []) {
437
437
  const attrs = attrsToRecord(span.attributes);
438
+ const dbSqlText = typeof attrs["db.statement"] === "string" ? attrs["db.statement"] : typeof attrs["db.query.text"] === "string" ? attrs["db.query.text"] : void 0;
439
+ const dbSystemName = normalizeDbSystem(attrs);
440
+ const directDbTable = typeof attrs["db.sql.table"] === "string" ? attrs["db.sql.table"] : dbSystemName !== "mongodb" && typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : void 0;
438
441
  const parsed = {
439
442
  service,
440
443
  resourceServiceNamePresent,
@@ -449,11 +452,11 @@ function parseOtlpRequest(body) {
449
452
  durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
450
453
  env: pickEnv(attrs, resourceAttrs),
451
454
  attributes: attrs,
452
- dbSystem: normalizeDbSystem(attrs),
455
+ dbSystem: dbSystemName,
453
456
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
454
457
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
455
- dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
456
- dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
458
+ dbTable: directDbTable ?? (dbSqlText ? tableFromSqlStatement(dbSqlText) ?? void 0 : void 0),
459
+ dbColumns: dbSqlText ? columnsFromSqlStatement(dbSqlText) : void 0,
457
460
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
458
461
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
459
462
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -738,13 +741,13 @@ __export(neatd_exports, {
738
741
  module.exports = __toCommonJS(neatd_exports);
739
742
  init_cjs_shims();
740
743
  var import_node_fs35 = require("fs");
741
- var import_node_path68 = __toESM(require("path"), 1);
744
+ var import_node_path69 = __toESM(require("path"), 1);
742
745
  var import_node_module2 = require("module");
743
746
 
744
747
  // src/daemon.ts
745
748
  init_cjs_shims();
746
749
  var import_node_fs33 = require("fs");
747
- var import_node_path66 = __toESM(require("path"), 1);
750
+ var import_node_path67 = __toESM(require("path"), 1);
748
751
  var import_node_module = require("module");
749
752
 
750
753
  // src/graph.ts
@@ -1277,19 +1280,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1277
1280
  function longestIncomingWalk(graph, start, maxDepth) {
1278
1281
  let best = { path: [start], edges: [] };
1279
1282
  const visited = /* @__PURE__ */ new Set([start]);
1280
- function step(node, path69, edges) {
1281
- if (path69.length > best.path.length) {
1282
- best = { path: [...path69], edges: [...edges] };
1283
+ function step(node, path70, edges) {
1284
+ if (path70.length > best.path.length) {
1285
+ best = { path: [...path70], edges: [...edges] };
1283
1286
  }
1284
- if (path69.length - 1 >= maxDepth) return;
1287
+ if (path70.length - 1 >= maxDepth) return;
1285
1288
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1286
1289
  for (const [srcId, edge] of incoming) {
1287
1290
  if (visited.has(srcId)) continue;
1288
1291
  visited.add(srcId);
1289
- path69.push(srcId);
1292
+ path70.push(srcId);
1290
1293
  edges.push(edge);
1291
- step(srcId, path69, edges);
1292
- path69.pop();
1294
+ step(srcId, path70, edges);
1295
+ path70.pop();
1293
1296
  edges.pop();
1294
1297
  visited.delete(srcId);
1295
1298
  }
@@ -1297,11 +1300,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1297
1300
  step(start, [start], []);
1298
1301
  return best;
1299
1302
  }
1300
- function databaseRootCauseShape(graph, origin, walk8) {
1303
+ function databaseRootCauseShape(graph, origin, walk9) {
1301
1304
  const targetDb = origin;
1302
1305
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1303
1306
  if (candidatePairs.length === 0) return null;
1304
- for (const id of walk8.path) {
1307
+ for (const id of walk9.path) {
1305
1308
  const owner = resolveOwningService(graph, id);
1306
1309
  if (!owner) continue;
1307
1310
  const { id: serviceId9, svc } = owner;
@@ -1328,8 +1331,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
1328
1331
  }
1329
1332
  return null;
1330
1333
  }
1331
- function serviceRootCauseShape(graph, _origin, walk8) {
1332
- for (const id of walk8.path) {
1334
+ function serviceRootCauseShape(graph, _origin, walk9) {
1335
+ for (const id of walk9.path) {
1333
1336
  const owner = resolveOwningService(graph, id);
1334
1337
  if (!owner) continue;
1335
1338
  const { id: serviceId9, svc } = owner;
@@ -1365,15 +1368,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
1365
1368
  }
1366
1369
  return null;
1367
1370
  }
1368
- function fileRootCauseShape(graph, origin, walk8) {
1371
+ function fileRootCauseShape(graph, origin, walk9) {
1369
1372
  const owner = resolveOwningService(graph, origin.id);
1370
1373
  if (!owner) return null;
1371
- return serviceRootCauseShape(graph, owner.svc, walk8);
1374
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1372
1375
  }
1373
- function symbolRootCauseShape(graph, origin, walk8) {
1376
+ function symbolRootCauseShape(graph, origin, walk9) {
1374
1377
  const owner = resolveOwningService(graph, origin.id);
1375
1378
  if (!owner) return null;
1376
- return serviceRootCauseShape(graph, owner.svc, walk8);
1379
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1377
1380
  }
1378
1381
  var rootCauseShapes = {
1379
1382
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1386,16 +1389,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1386
1389
  const origin = graph.getNodeAttributes(errorNodeId);
1387
1390
  const shape = rootCauseShapes[origin.type];
1388
1391
  if (shape) {
1389
- const walk8 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1390
- const match = shape(graph, origin, walk8);
1392
+ const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1393
+ const match = shape(graph, origin, walk9);
1391
1394
  if (match) {
1392
1395
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1393
1396
  return import_types.RootCauseResultSchema.parse({
1394
1397
  rootCauseNode: match.rootCauseNode,
1395
1398
  rootCauseReason: reason,
1396
- traversalPath: walk8.path,
1397
- edgeProvenances: walk8.edges.map((e) => e.provenance),
1398
- confidence: confidenceFromMix(walk8.edges),
1399
+ traversalPath: walk9.path,
1400
+ edgeProvenances: walk9.edges.map((e) => e.provenance),
1401
+ confidence: confidenceFromMix(walk9.edges),
1399
1402
  fixRecommendation: match.fixRecommendation
1400
1403
  });
1401
1404
  }
@@ -1496,26 +1499,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
1496
1499
  return best;
1497
1500
  }
1498
1501
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1499
- const path69 = [originServiceId];
1502
+ const path70 = [originServiceId];
1500
1503
  const edges = [];
1501
1504
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1502
1505
  let current = originServiceId;
1503
1506
  for (let depth = 0; depth < maxDepth; depth++) {
1504
1507
  const hop = dominantFailingCall(graph, current, visited);
1505
1508
  if (!hop) break;
1506
- path69.push(hop.nextService);
1509
+ path70.push(hop.nextService);
1507
1510
  edges.push(hop.edge);
1508
1511
  visited.add(hop.nextService);
1509
1512
  current = hop.nextService;
1510
1513
  }
1511
1514
  if (edges.length === 0) return null;
1512
- return { path: path69, edges, culprit: current };
1515
+ return { path: path70, edges, culprit: current };
1513
1516
  }
1514
1517
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1515
1518
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1516
1519
  if (!chain) return null;
1517
1520
  const culprit = chain.culprit;
1518
- const path69 = [...chain.path];
1521
+ const path70 = [...chain.path];
1519
1522
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1520
1523
  const baseConfidence = confidenceFromMix(chain.edges);
1521
1524
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1523,14 +1526,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1523
1526
  if (loc) {
1524
1527
  let rootCauseNode = culprit;
1525
1528
  if (loc.fileNode) {
1526
- path69.push(loc.fileNode);
1529
+ path70.push(loc.fileNode);
1527
1530
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1528
1531
  rootCauseNode = loc.fileNode;
1529
1532
  }
1530
1533
  return import_types.RootCauseResultSchema.parse({
1531
1534
  rootCauseNode,
1532
1535
  rootCauseReason: loc.rootCauseReason,
1533
- traversalPath: path69,
1536
+ traversalPath: path70,
1534
1537
  edgeProvenances,
1535
1538
  confidence,
1536
1539
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1542,7 +1545,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1542
1545
  return import_types.RootCauseResultSchema.parse({
1543
1546
  rootCauseNode: culprit,
1544
1547
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1545
- traversalPath: path69,
1548
+ traversalPath: path70,
1546
1549
  edgeProvenances,
1547
1550
  confidence,
1548
1551
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2477,14 +2480,14 @@ function buildServiceHostIndex(services) {
2477
2480
  }
2478
2481
  async function walkSourceFiles(dir) {
2479
2482
  const out = [];
2480
- async function walk8(current) {
2483
+ async function walk9(current) {
2481
2484
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2482
2485
  for (const entry2 of entries) {
2483
2486
  const full = import_node_path5.default.join(current, entry2.name);
2484
2487
  if (entry2.isDirectory()) {
2485
2488
  if (IGNORED_DIRS.has(entry2.name)) continue;
2486
2489
  if (await isPythonVenvDir(full)) continue;
2487
- await walk8(full);
2490
+ await walk9(full);
2488
2491
  } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2489
2492
  // would attribute our instrumentation imports to the user's service.
2490
2493
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -2492,7 +2495,7 @@ async function walkSourceFiles(dir) {
2492
2495
  }
2493
2496
  }
2494
2497
  }
2495
- await walk8(dir);
2498
+ await walk9(dir);
2496
2499
  return out;
2497
2500
  }
2498
2501
  async function loadSourceFiles(dir) {
@@ -3006,7 +3009,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
3006
3009
  ]);
3007
3010
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
3008
3011
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
3009
- function ginRoutesFromSource(source, parser) {
3012
+ function goRouterRoutesFromSource(source, parser, framework) {
3010
3013
  const tree = parseSource2(parser, source);
3011
3014
  const prefixes = /* @__PURE__ */ new Map();
3012
3015
  const out = [];
@@ -3016,10 +3019,12 @@ function ginRoutesFromSource(source, parser) {
3016
3019
  const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
3017
3020
  if (name && value?.type === "call_expression") {
3018
3021
  const fn2 = value.childForFieldName("function");
3019
- const field = fn2?.childForFieldName("field")?.text;
3020
- const first2 = value.childForFieldName("arguments")?.namedChild(0);
3021
- if (field === "Group" && first2?.type === "interpreted_string_literal") {
3022
- prefixes.set(name, first2.text.slice(1, -1));
3022
+ if (fn2?.childForFieldName("field")?.text === "Group") {
3023
+ const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
3024
+ if (leaf2 !== null) {
3025
+ const parent = fn2.childForFieldName("operand")?.text ?? "";
3026
+ prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
3027
+ }
3023
3028
  }
3024
3029
  }
3025
3030
  return;
@@ -3030,18 +3035,32 @@ function ginRoutesFromSource(source, parser) {
3030
3035
  const method = fn.childForFieldName("field")?.text?.toUpperCase();
3031
3036
  if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
3032
3037
  const receiver = fn.childForFieldName("operand")?.text ?? "";
3033
- const first = node.childForFieldName("arguments")?.namedChild(0);
3034
- if (first?.type !== "interpreted_string_literal") return;
3035
- const leaf = first.text.slice(1, -1);
3038
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
3039
+ if (leaf === null) return;
3036
3040
  out.push({
3037
- method: method === "ALL" ? "ALL" : method,
3041
+ method,
3038
3042
  pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
3039
3043
  line: node.startPosition.row + 1,
3040
- framework: "gin"
3044
+ framework
3041
3045
  });
3042
3046
  });
3043
3047
  return out;
3044
3048
  }
3049
+ function goStringLiteral(node) {
3050
+ if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
3051
+ return node.text.slice(1, -1);
3052
+ }
3053
+ return null;
3054
+ }
3055
+ function ginRoutesFromSource(source, parser) {
3056
+ return goRouterRoutesFromSource(source, parser, "gin");
3057
+ }
3058
+ function echoRoutesFromSource(source, parser) {
3059
+ return goRouterRoutesFromSource(source, parser, "echo");
3060
+ }
3061
+ function fiberRoutesFromSource(source, parser) {
3062
+ return goRouterRoutesFromSource(source, parser, "fiber");
3063
+ }
3045
3064
  var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
3046
3065
  var NESTJS_METHODS = /* @__PURE__ */ new Map([
3047
3066
  ["Get", "GET"],
@@ -3626,9 +3645,9 @@ function rubyRocketRoute(args) {
3626
3645
  if (!pair || pair.type !== "pair") continue;
3627
3646
  const k = pair.childForFieldName("key");
3628
3647
  if (k?.type !== "string") continue;
3629
- const path69 = rubyLiteral(k);
3630
- if (path69 === null) continue;
3631
- return { path: path69, target: rubyLiteral(pair.childForFieldName("value")) };
3648
+ const path70 = rubyLiteral(k);
3649
+ if (path70 === null) continue;
3650
+ return { path: path70, target: rubyLiteral(pair.childForFieldName("value")) };
3632
3651
  }
3633
3652
  return null;
3634
3653
  }
@@ -4305,9 +4324,11 @@ async function addRoutes(graph, services) {
4305
4324
  const hasFlask = deps["flask"] !== void 0;
4306
4325
  const hasDjango = deps["django"] !== void 0;
4307
4326
  const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
4327
+ const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
4328
+ const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
4308
4329
  const hasRails = deps["rails"] !== void 0;
4309
4330
  const hasLaravel = deps["laravel/framework"] !== void 0;
4310
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
4331
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
4311
4332
  continue;
4312
4333
  const files = await loadSourceFiles(service.dir);
4313
4334
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4331,7 +4352,10 @@ async function addRoutes(graph, services) {
4331
4352
  } else if (isRb) {
4332
4353
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4333
4354
  } else if (isGo) {
4334
- routes = hasGin ? ginRoutesFromSource(file.content, goParser) : [];
4355
+ if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4356
+ else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
4357
+ else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
4358
+ else routes = [];
4335
4359
  } else if (isPy) {
4336
4360
  routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
4337
4361
  if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
@@ -5938,6 +5962,12 @@ function parseGoMod(source) {
5938
5962
  }
5939
5963
  return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
5940
5964
  }
5965
+ function goFramework(deps) {
5966
+ if (deps["github.com/gin-gonic/gin"]) return "gin";
5967
+ if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
5968
+ if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
5969
+ return void 0;
5970
+ }
5941
5971
  async function discoverGoService(scanPath, dir) {
5942
5972
  let raw;
5943
5973
  try {
@@ -5949,6 +5979,7 @@ async function discoverGoService(scanPath, dir) {
5949
5979
  if (!mod) return null;
5950
5980
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
5951
5981
  const pkg = { name, dependencies: mod.dependencies };
5982
+ const framework = goFramework(mod.dependencies);
5952
5983
  const node = {
5953
5984
  id: (0, import_types9.serviceId)(name),
5954
5985
  type: import_types9.NodeType.ServiceNode,
@@ -5956,7 +5987,7 @@ async function discoverGoService(scanPath, dir) {
5956
5987
  language: "go",
5957
5988
  dependencies: mod.dependencies,
5958
5989
  repoPath: import_node_path10.default.relative(scanPath, dir),
5959
- ...mod.dependencies["github.com/gin-gonic/gin"] ? { framework: "gin" } : {}
5990
+ ...framework ? { framework } : {}
5960
5991
  };
5961
5992
  return { pkg, dir, node };
5962
5993
  }
@@ -6856,7 +6887,7 @@ async function addSymbolEdges(graph, services) {
6856
6887
  return best;
6857
6888
  };
6858
6889
  const requests = [];
6859
- const walk8 = (node) => {
6890
+ const walk9 = (node) => {
6860
6891
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
6861
6892
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
6862
6893
  if (self && self.kind === "class") {
@@ -6902,10 +6933,10 @@ async function addSymbolEdges(graph, services) {
6902
6933
  }
6903
6934
  for (let i = 0; i < node.namedChildCount; i++) {
6904
6935
  const child = node.namedChild(i);
6905
- if (child) walk8(child);
6936
+ if (child) walk9(child);
6906
6937
  }
6907
6938
  };
6908
- walk8(root);
6939
+ walk9(root);
6909
6940
  for (const req2 of requests) {
6910
6941
  const targetSid = resolveTarget(req2.targetName, req2.wantKind);
6911
6942
  if (!targetSid) continue;
@@ -7918,20 +7949,20 @@ var import_node_path28 = __toESM(require("path"), 1);
7918
7949
  var import_types18 = require("@neat.is/types");
7919
7950
  async function walkConfigFiles(dir) {
7920
7951
  const out = [];
7921
- async function walk8(current) {
7952
+ async function walk9(current) {
7922
7953
  const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true });
7923
7954
  for (const entry2 of entries) {
7924
7955
  const full = import_node_path28.default.join(current, entry2.name);
7925
7956
  if (entry2.isDirectory()) {
7926
7957
  if (IGNORED_DIRS.has(entry2.name)) continue;
7927
7958
  if (await isPythonVenvDir(full)) continue;
7928
- await walk8(full);
7959
+ await walk9(full);
7929
7960
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
7930
7961
  out.push(full);
7931
7962
  }
7932
7963
  }
7933
7964
  }
7934
- await walk8(dir);
7965
+ await walk9(dir);
7935
7966
  return out;
7936
7967
  }
7937
7968
  async function addConfigNodes(graph, services, scanPath) {
@@ -8021,20 +8052,20 @@ function grpcMethodsFromProto(content, fqPackage) {
8021
8052
  }
8022
8053
  async function walkProtoFiles(dir) {
8023
8054
  const out = [];
8024
- async function walk8(current) {
8055
+ async function walk9(current) {
8025
8056
  const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8026
8057
  for (const entry2 of entries) {
8027
8058
  const full = import_node_path29.default.join(current, entry2.name);
8028
8059
  if (entry2.isDirectory()) {
8029
8060
  if (IGNORED_DIRS.has(entry2.name)) continue;
8030
8061
  if (await isPythonVenvDir(full)) continue;
8031
- await walk8(full);
8062
+ await walk9(full);
8032
8063
  } else if (entry2.isFile() && import_node_path29.default.extname(entry2.name) === PROTO_EXTENSION) {
8033
8064
  out.push(full);
8034
8065
  }
8035
8066
  }
8036
8067
  }
8037
- await walk8(dir);
8068
+ await walk9(dir);
8038
8069
  return out;
8039
8070
  }
8040
8071
  async function addGrpcMethods(graph, services) {
@@ -8102,7 +8133,7 @@ async function addGrpcMethods(graph, services) {
8102
8133
 
8103
8134
  // src/extract/calls/index.ts
8104
8135
  init_cjs_shims();
8105
- var import_types36 = require("@neat.is/types");
8136
+ var import_types37 = require("@neat.is/types");
8106
8137
 
8107
8138
  // src/extract/calls/http.ts
8108
8139
  init_cjs_shims();
@@ -8856,7 +8887,7 @@ function isFirestoreClientFactory(node) {
8856
8887
  }
8857
8888
  function firestoreClientVars(root) {
8858
8889
  const vars = /* @__PURE__ */ new Set();
8859
- const walk8 = (node) => {
8890
+ const walk9 = (node) => {
8860
8891
  if (node.type === "variable_declarator") {
8861
8892
  const name = node.childForFieldName("name");
8862
8893
  let value = node.childForFieldName("value");
@@ -8865,9 +8896,9 @@ function firestoreClientVars(root) {
8865
8896
  vars.add(name.text);
8866
8897
  }
8867
8898
  }
8868
- for (const c of namedChildren(node)) walk8(c);
8899
+ for (const c of namedChildren(node)) walk9(c);
8869
8900
  };
8870
- walk8(root);
8901
+ walk9(root);
8871
8902
  return vars;
8872
8903
  }
8873
8904
  function isClientExpr(node, clientVars) {
@@ -9022,7 +9053,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9022
9053
  }
9023
9054
  s.add(field);
9024
9055
  };
9025
- const walk8 = (node) => {
9056
+ const walk9 = (node) => {
9026
9057
  if (node.type === "call_expression") {
9027
9058
  const fn = node.childForFieldName("function");
9028
9059
  const line = node.startPosition.row + 1;
@@ -9062,9 +9093,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9062
9093
  }
9063
9094
  }
9064
9095
  }
9065
- for (const c of namedChildren(node)) walk8(c);
9096
+ for (const c of namedChildren(node)) walk9(c);
9066
9097
  };
9067
- walk8(tree.rootNode);
9098
+ walk9(tree.rootNode);
9068
9099
  const out = [];
9069
9100
  for (const [collPath, line] of collLine) {
9070
9101
  const byField = writes.get(collPath);
@@ -9859,7 +9890,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9859
9890
  const tree = parseSource3(parserForExt2(import_node_path41.default.extname(file.path)), file.content);
9860
9891
  const out = [];
9861
9892
  const seen = /* @__PURE__ */ new Set();
9862
- const walk8 = (node) => {
9893
+ const walk9 = (node) => {
9863
9894
  if (node.type === "call_expression") {
9864
9895
  const fn = node.childForFieldName("function");
9865
9896
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9887,9 +9918,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9887
9918
  }
9888
9919
  }
9889
9920
  }
9890
- for (const c of namedChildren4(node)) walk8(c);
9921
+ for (const c of namedChildren4(node)) walk9(c);
9891
9922
  };
9892
- walk8(tree.rootNode);
9923
+ walk9(tree.rootNode);
9893
9924
  return out;
9894
9925
  }
9895
9926
  function enclosingVarName(call) {
@@ -9911,7 +9942,7 @@ function enclosingVarName(call) {
9911
9942
  function collectDrizzleTables(root) {
9912
9943
  const tables = [];
9913
9944
  const varToTable = /* @__PURE__ */ new Map();
9914
- const walk8 = (node) => {
9945
+ const walk9 = (node) => {
9915
9946
  if (node.type === "call_expression") {
9916
9947
  const fn = node.childForFieldName("function");
9917
9948
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9926,9 +9957,9 @@ function collectDrizzleTables(root) {
9926
9957
  }
9927
9958
  }
9928
9959
  }
9929
- for (const c of namedChildren4(node)) walk8(c);
9960
+ for (const c of namedChildren4(node)) walk9(c);
9930
9961
  };
9931
- walk8(root);
9962
+ walk9(root);
9932
9963
  return { tables, varToTable };
9933
9964
  }
9934
9965
  function referencesTargetVar(call) {
@@ -9951,7 +9982,7 @@ function drizzleForeignKeys(file, serviceDir) {
9951
9982
  const seen = /* @__PURE__ */ new Set();
9952
9983
  for (const table of tables) {
9953
9984
  if (!table.object) continue;
9954
- const walk8 = (node) => {
9985
+ const walk9 = (node) => {
9955
9986
  if (node.type === "call_expression") {
9956
9987
  const targetVar = referencesTargetVar(node);
9957
9988
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -9972,9 +10003,9 @@ function drizzleForeignKeys(file, serviceDir) {
9972
10003
  }
9973
10004
  }
9974
10005
  }
9975
- for (const c of namedChildren4(node)) walk8(c);
10006
+ for (const c of namedChildren4(node)) walk9(c);
9976
10007
  };
9977
- walk8(table.object);
10008
+ walk9(table.object);
9978
10009
  }
9979
10010
  return out;
9980
10011
  }
@@ -11054,15 +11085,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
11054
11085
  return out;
11055
11086
  }
11056
11087
 
11088
+ // src/extract/calls/gorm.ts
11089
+ init_cjs_shims();
11090
+ var import_node_path48 = __toESM(require("path"), 1);
11091
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
11092
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
11093
+ var import_types36 = require("@neat.is/types");
11094
+ var GORM_IMPORT_RE = /gorm\.io\/gorm/;
11095
+ var PARSE_CHUNK11 = 16384;
11096
+ function makeGoParser3() {
11097
+ const p = new import_tree_sitter15.default();
11098
+ p.setLanguage(import_tree_sitter_go4.default);
11099
+ return p;
11100
+ }
11101
+ function parseSource10(parser, source) {
11102
+ return parser.parse(
11103
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
11104
+ );
11105
+ }
11106
+ function walk8(node, visit) {
11107
+ visit(node);
11108
+ for (let i = 0; i < node.namedChildCount; i++) {
11109
+ const c = node.namedChild(i);
11110
+ if (c) walk8(c, visit);
11111
+ }
11112
+ }
11113
+ var COMMON_INITIALISMS = [
11114
+ "ASCII",
11115
+ "HTTPS",
11116
+ "UTF8",
11117
+ "XSRF",
11118
+ "HTML",
11119
+ "HTTP",
11120
+ "JSON",
11121
+ "UUID",
11122
+ "XMPP",
11123
+ "ACL",
11124
+ "API",
11125
+ "CPU",
11126
+ "CSS",
11127
+ "DNS",
11128
+ "EOF",
11129
+ "GUID",
11130
+ "LHS",
11131
+ "QPS",
11132
+ "RAM",
11133
+ "RHS",
11134
+ "RPC",
11135
+ "SLA",
11136
+ "SQL",
11137
+ "SSH",
11138
+ "TCP",
11139
+ "TLS",
11140
+ "TTL",
11141
+ "UDP",
11142
+ "UID",
11143
+ "URI",
11144
+ "URL",
11145
+ "UID",
11146
+ "XSS",
11147
+ "ID",
11148
+ "IP",
11149
+ "UI",
11150
+ "VM",
11151
+ "XML"
11152
+ ].sort((a, b) => b.length - a.length);
11153
+ function titleCase(word) {
11154
+ return word.charAt(0) + word.slice(1).toLowerCase();
11155
+ }
11156
+ function replaceInitialisms(name) {
11157
+ let out = "";
11158
+ let i = 0;
11159
+ while (i < name.length) {
11160
+ let matched = false;
11161
+ for (const init of COMMON_INITIALISMS) {
11162
+ if (name.startsWith(init, i)) {
11163
+ out += titleCase(init);
11164
+ i += init.length;
11165
+ matched = true;
11166
+ break;
11167
+ }
11168
+ }
11169
+ if (!matched) {
11170
+ out += name[i];
11171
+ i++;
11172
+ }
11173
+ }
11174
+ return out;
11175
+ }
11176
+ var isUpper = (c) => c >= "A" && c <= "Z";
11177
+ var isDigit = (c) => c >= "0" && c <= "9";
11178
+ function toDBName(name) {
11179
+ if (name === "") return "";
11180
+ const value = replaceInitialisms(name);
11181
+ if (value.length === 1) return value.toLowerCase();
11182
+ let buf = "";
11183
+ let lastCase = false;
11184
+ let curCase = isUpper(value[0]);
11185
+ for (let i = 0; i < value.length - 1; i++) {
11186
+ const v = value[i];
11187
+ const nextCase = isUpper(value[i + 1]);
11188
+ const nextNumber = isDigit(value[i + 1]);
11189
+ if (curCase) {
11190
+ if (lastCase && (nextCase || nextNumber)) {
11191
+ buf += v.toLowerCase();
11192
+ } else {
11193
+ if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
11194
+ buf += v.toLowerCase();
11195
+ }
11196
+ } else {
11197
+ buf += v;
11198
+ }
11199
+ lastCase = curCase;
11200
+ curCase = nextCase;
11201
+ }
11202
+ const last = value[value.length - 1];
11203
+ if (curCase) {
11204
+ if (!lastCase && value.length > 1) buf += "_";
11205
+ buf += last.toLowerCase();
11206
+ } else {
11207
+ buf += last;
11208
+ }
11209
+ return buf;
11210
+ }
11211
+ var UNCOUNTABLE = /* @__PURE__ */ new Set([
11212
+ "equipment",
11213
+ "information",
11214
+ "rice",
11215
+ "money",
11216
+ "species",
11217
+ "series",
11218
+ "fish",
11219
+ "sheep",
11220
+ "jeans",
11221
+ "police"
11222
+ ]);
11223
+ var IRREGULAR = [
11224
+ ["person", "people"],
11225
+ ["man", "men"],
11226
+ ["child", "children"],
11227
+ ["sex", "sexes"],
11228
+ ["move", "moves"]
11229
+ ];
11230
+ var PLURAL_RULES = [
11231
+ [/(quiz)$/i, "$1zes"],
11232
+ [/^(ox)$/i, "$1en"],
11233
+ [/([ml])ouse$/i, "$1ice"],
11234
+ [/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
11235
+ [/(x|ch|ss|sh)$/i, "$1es"],
11236
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
11237
+ [/(hive)$/i, "$1s"],
11238
+ [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
11239
+ [/sis$/i, "ses"],
11240
+ [/([ti])um$/i, "$1a"],
11241
+ [/([ti])a$/i, "$1a"],
11242
+ [/(buffal|tomat)o$/i, "$1oes"],
11243
+ [/(bu)s$/i, "$1ses"],
11244
+ [/(alias|status)$/i, "$1es"],
11245
+ [/(octop|vir)i$/i, "$1i"],
11246
+ [/(octop|vir)us$/i, "$1i"],
11247
+ [/(ax|test)is$/i, "$1es"],
11248
+ [/s$/i, "s"]
11249
+ ];
11250
+ function pluralize3(word) {
11251
+ if (word === "") return word;
11252
+ const lower = word.toLowerCase();
11253
+ for (const u of UNCOUNTABLE) {
11254
+ if (lower === u || lower.endsWith("_" + u)) return word;
11255
+ }
11256
+ for (const [sing, plur] of IRREGULAR) {
11257
+ const re = new RegExp(sing + "$", "i");
11258
+ if (re.test(word)) return word.replace(re, plur);
11259
+ }
11260
+ for (const [re, rep] of PLURAL_RULES) {
11261
+ if (re.test(word)) return word.replace(re, rep);
11262
+ }
11263
+ return word + "s";
11264
+ }
11265
+ function deriveTableName(structName) {
11266
+ return pluralize3(toDBName(structName));
11267
+ }
11268
+ function stringLiteralValue(node) {
11269
+ if (!node) return null;
11270
+ if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
11271
+ const t = node.text;
11272
+ return t.length >= 2 ? t.slice(1, -1) : "";
11273
+ }
11274
+ return null;
11275
+ }
11276
+ function parseGormTag(tagNode) {
11277
+ const tag = {};
11278
+ if (!tagNode) return tag;
11279
+ let inner = tagNode.text;
11280
+ if (inner.length >= 2) inner = inner.slice(1, -1);
11281
+ if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
11282
+ const m = inner.match(/gorm:"([^"]*)"/);
11283
+ if (!m) return tag;
11284
+ for (const part of m[1].split(";")) {
11285
+ if (part === "") continue;
11286
+ const idx = part.indexOf(":");
11287
+ const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
11288
+ const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
11289
+ if (key === "-") tag.skip = true;
11290
+ else if (key === "column") tag.column = value;
11291
+ else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
11292
+ else if (key === "foreignkey") tag.foreignKey = value;
11293
+ else if (key === "many2many") tag.many2many = value;
11294
+ else if (key === "embedded") tag.embedded = true;
11295
+ else if (key === "embeddedprefix") tag.embeddedPrefix = value;
11296
+ }
11297
+ return tag;
11298
+ }
11299
+ function unwrapType(typeNode) {
11300
+ let isSlice = false;
11301
+ let isPointer = false;
11302
+ let n = typeNode;
11303
+ while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
11304
+ if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
11305
+ if (n.type === "pointer_type") isPointer = true;
11306
+ n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
11307
+ }
11308
+ if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11309
+ if (n.type === "type_identifier") {
11310
+ return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
11311
+ }
11312
+ if (n.type === "qualified_type") {
11313
+ const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
11314
+ const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
11315
+ return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
11316
+ }
11317
+ return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11318
+ }
11319
+ function readField(fieldDecl) {
11320
+ const names = [];
11321
+ let tagNode = null;
11322
+ for (let i = 0; i < fieldDecl.namedChildCount; i++) {
11323
+ const c = fieldDecl.namedChild(i);
11324
+ if (!c) continue;
11325
+ if (c.type === "field_identifier") names.push(c.text);
11326
+ else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
11327
+ }
11328
+ const typeNode = fieldDecl.childForFieldName("type");
11329
+ const t = unwrapType(typeNode);
11330
+ return {
11331
+ names,
11332
+ typeName: t.name,
11333
+ qualifier: t.qualifier,
11334
+ isSlice: t.isSlice,
11335
+ isPointer: t.isPointer,
11336
+ isQualified: t.isQualified,
11337
+ tag: parseGormTag(tagNode),
11338
+ line: fieldDecl.startPosition.row + 1
11339
+ };
11340
+ }
11341
+ function collectStructs(tree) {
11342
+ const structs = /* @__PURE__ */ new Map();
11343
+ walk8(tree.rootNode, (node) => {
11344
+ if (node.type !== "type_spec") return;
11345
+ const nameNode = node.childForFieldName("name");
11346
+ const typeNode = node.childForFieldName("type");
11347
+ if (!nameNode || typeNode?.type !== "struct_type") return;
11348
+ const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
11349
+ const fields = [];
11350
+ if (list && list.type === "field_declaration_list") {
11351
+ for (let i = 0; i < list.namedChildCount; i++) {
11352
+ const fd = list.namedChild(i);
11353
+ if (fd?.type === "field_declaration") fields.push(readField(fd));
11354
+ }
11355
+ }
11356
+ structs.set(nameNode.text, {
11357
+ name: nameNode.text,
11358
+ fields,
11359
+ line: node.startPosition.row + 1
11360
+ });
11361
+ });
11362
+ return structs;
11363
+ }
11364
+ var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
11365
+ "AutoMigrate",
11366
+ "Model",
11367
+ "Create",
11368
+ "Find",
11369
+ "First",
11370
+ "Take",
11371
+ "Last",
11372
+ "Save",
11373
+ "Delete",
11374
+ "Where",
11375
+ "FirstOrCreate",
11376
+ "FirstOrInit"
11377
+ ]);
11378
+ function compositeStructName(arg) {
11379
+ let n = arg;
11380
+ if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
11381
+ if (!n || n.type !== "composite_literal") return null;
11382
+ const typeNode = n.childForFieldName("type");
11383
+ if (!typeNode) return null;
11384
+ if (typeNode.type === "type_identifier") return typeNode.text;
11385
+ if (typeNode.type === "qualified_type") {
11386
+ return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
11387
+ }
11388
+ return null;
11389
+ }
11390
+ function collectCallModels(tree) {
11391
+ const models = /* @__PURE__ */ new Set();
11392
+ walk8(tree.rootNode, (node) => {
11393
+ if (node.type !== "call_expression") return;
11394
+ const fn = node.childForFieldName("function");
11395
+ if (fn?.type !== "selector_expression") return;
11396
+ const method = fn.childForFieldName("field")?.text;
11397
+ if (!method || !GORM_MODEL_METHODS.has(method)) return;
11398
+ const args = node.childForFieldName("arguments");
11399
+ if (!args) return;
11400
+ for (let i = 0; i < args.namedChildCount; i++) {
11401
+ const arg = args.namedChild(i);
11402
+ if (!arg) continue;
11403
+ const name = compositeStructName(arg);
11404
+ if (name) models.add(name);
11405
+ }
11406
+ });
11407
+ return models;
11408
+ }
11409
+ function collectTableNameOverrides(tree) {
11410
+ const overrides = /* @__PURE__ */ new Map();
11411
+ const declarers = /* @__PURE__ */ new Set();
11412
+ walk8(tree.rootNode, (node) => {
11413
+ if (node.type !== "method_declaration") return;
11414
+ if (node.childForFieldName("name")?.text !== "TableName") return;
11415
+ const receiver = node.childForFieldName("receiver");
11416
+ if (!receiver) return;
11417
+ let recvType = null;
11418
+ for (let i = 0; i < receiver.namedChildCount; i++) {
11419
+ const pd = receiver.namedChild(i);
11420
+ if (pd?.type !== "parameter_declaration") continue;
11421
+ const t = unwrapType(pd.childForFieldName("type"));
11422
+ recvType = t.name;
11423
+ }
11424
+ if (!recvType) return;
11425
+ declarers.add(recvType);
11426
+ const body = node.childForFieldName("body");
11427
+ if (!body) return;
11428
+ let literal = null;
11429
+ walk8(body, (n) => {
11430
+ if (literal !== null) return;
11431
+ if (n.type !== "return_statement") return;
11432
+ const exprList = n.namedChild(0);
11433
+ const first = exprList?.namedChild(0) ?? exprList;
11434
+ const v = stringLiteralValue(first);
11435
+ if (v) literal = v;
11436
+ });
11437
+ if (literal !== null) overrides.set(recvType, literal);
11438
+ });
11439
+ return { overrides, declarers };
11440
+ }
11441
+ function isRelationField(field, structs) {
11442
+ if (field.names.length === 0) return false;
11443
+ if (field.isQualified) return false;
11444
+ if (!field.typeName) return false;
11445
+ return structs.has(field.typeName);
11446
+ }
11447
+ function isGormModelEmbed(field) {
11448
+ return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
11449
+ }
11450
+ function analyze(tree) {
11451
+ const structs = collectStructs(tree);
11452
+ const { overrides, declarers } = collectTableNameOverrides(tree);
11453
+ const callModels = collectCallModels(tree);
11454
+ const models = /* @__PURE__ */ new Set();
11455
+ for (const [name, info] of structs) {
11456
+ if (info.fields.some(isGormModelEmbed)) models.add(name);
11457
+ }
11458
+ for (const name of callModels) if (structs.has(name)) models.add(name);
11459
+ for (const name of declarers) if (structs.has(name)) models.add(name);
11460
+ let grew = true;
11461
+ while (grew) {
11462
+ grew = false;
11463
+ for (const name of Array.from(models)) {
11464
+ const info = structs.get(name);
11465
+ if (!info) continue;
11466
+ for (const field of info.fields) {
11467
+ if (!isRelationField(field, structs)) continue;
11468
+ const target = field.typeName;
11469
+ if (!models.has(target) && structs.has(target)) {
11470
+ models.add(target);
11471
+ grew = true;
11472
+ }
11473
+ }
11474
+ }
11475
+ }
11476
+ const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
11477
+ return { structs, models, tableFor };
11478
+ }
11479
+ function collectColumns(struct, structs, seen, prefix, out, emitted) {
11480
+ if (seen.has(struct.name)) return;
11481
+ seen.add(struct.name);
11482
+ const add = (col) => {
11483
+ const full = prefix + col;
11484
+ if (!emitted.has(full)) {
11485
+ emitted.add(full);
11486
+ out.push(full);
11487
+ }
11488
+ };
11489
+ for (const field of struct.fields) {
11490
+ if (field.tag.skip) continue;
11491
+ if (field.names.length === 0) {
11492
+ if (isGormModelEmbed(field)) {
11493
+ add("id");
11494
+ add("created_at");
11495
+ add("updated_at");
11496
+ add("deleted_at");
11497
+ } else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
11498
+ collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
11499
+ }
11500
+ continue;
11501
+ }
11502
+ if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
11503
+ collectColumns(
11504
+ structs.get(field.typeName),
11505
+ structs,
11506
+ seen,
11507
+ prefix + (field.tag.embeddedPrefix ?? ""),
11508
+ out,
11509
+ emitted
11510
+ );
11511
+ continue;
11512
+ }
11513
+ if (isRelationField(field, structs)) continue;
11514
+ if (field.names.length === 1 && field.tag.column) {
11515
+ add(field.tag.column);
11516
+ } else {
11517
+ for (const n of field.names) add(toDBName(n));
11518
+ }
11519
+ }
11520
+ seen.delete(struct.name);
11521
+ }
11522
+ function gormEndpointsFromFile(file, serviceDir) {
11523
+ if (import_node_path48.default.extname(file.path) !== ".go") return [];
11524
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11525
+ const tree = parseSource10(makeGoParser3(), file.content);
11526
+ const { structs, models, tableFor } = analyze(tree);
11527
+ const out = [];
11528
+ const seenTables = /* @__PURE__ */ new Set();
11529
+ for (const name of models) {
11530
+ const struct = structs.get(name);
11531
+ if (!struct) continue;
11532
+ const table = tableFor(name);
11533
+ if (seenTables.has(table)) continue;
11534
+ seenTables.add(table);
11535
+ const columns = [];
11536
+ collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
11537
+ out.push({
11538
+ infraId: (0, import_types36.infraId)("sql-table", table),
11539
+ name: table,
11540
+ kind: "sql-table",
11541
+ edgeType: "CALLS",
11542
+ confidenceKind: "structural",
11543
+ ...columns.length > 0 ? { columns } : {},
11544
+ evidence: {
11545
+ file: toPosix(import_node_path48.default.relative(serviceDir, file.path)),
11546
+ line: struct.line,
11547
+ snippet: snippet(file.content, struct.line)
11548
+ }
11549
+ });
11550
+ }
11551
+ return out;
11552
+ }
11553
+ function gormForeignKeys(file, serviceDir) {
11554
+ if (import_node_path48.default.extname(file.path) !== ".go") return [];
11555
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11556
+ const tree = parseSource10(makeGoParser3(), file.content);
11557
+ const { structs, models, tableFor } = analyze(tree);
11558
+ const out = [];
11559
+ const seen = /* @__PURE__ */ new Set();
11560
+ const emit = (childTable, parentTable, line) => {
11561
+ if (!childTable || !parentTable || childTable === parentTable) return;
11562
+ const key = `${childTable}->${parentTable}`;
11563
+ if (seen.has(key)) return;
11564
+ seen.add(key);
11565
+ out.push({
11566
+ childTable,
11567
+ parentTable,
11568
+ evidence: {
11569
+ file: toPosix(import_node_path48.default.relative(serviceDir, file.path)),
11570
+ line,
11571
+ snippet: snippet(file.content, line)
11572
+ }
11573
+ });
11574
+ };
11575
+ for (const name of models) {
11576
+ const struct = structs.get(name);
11577
+ if (!struct) continue;
11578
+ const thisTable = tableFor(name);
11579
+ const scalarNames = new Set(
11580
+ struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
11581
+ );
11582
+ for (const field of struct.fields) {
11583
+ if (field.tag.skip) continue;
11584
+ if (!isRelationField(field, structs)) continue;
11585
+ const relTable = tableFor(field.typeName);
11586
+ if (field.tag.many2many) {
11587
+ emit(field.tag.many2many, thisTable, field.line);
11588
+ emit(field.tag.many2many, relTable, field.line);
11589
+ continue;
11590
+ }
11591
+ if (field.isSlice) {
11592
+ emit(relTable, thisTable, field.line);
11593
+ continue;
11594
+ }
11595
+ const convFk = field.names[0] + "ID";
11596
+ const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
11597
+ if (belongsTo) emit(thisTable, relTable, field.line);
11598
+ else emit(relTable, thisTable, field.line);
11599
+ }
11600
+ }
11601
+ return out;
11602
+ }
11603
+
11057
11604
  // src/extract/calls/index.ts
11058
11605
  function edgeTypeFromEndpoint(ep) {
11059
11606
  switch (ep.edgeType) {
11060
11607
  case "PUBLISHES_TO":
11061
- return import_types36.EdgeType.PUBLISHES_TO;
11608
+ return import_types37.EdgeType.PUBLISHES_TO;
11062
11609
  case "CONSUMES_FROM":
11063
- return import_types36.EdgeType.CONSUMES_FROM;
11610
+ return import_types37.EdgeType.CONSUMES_FROM;
11064
11611
  default:
11065
- return import_types36.EdgeType.CALLS;
11612
+ return import_types37.EdgeType.CALLS;
11066
11613
  }
11067
11614
  }
11068
11615
  function isAwsKind(kind) {
@@ -11095,6 +11642,11 @@ async function addExternalEndpointEdges(graph, services) {
11095
11642
  } catch (err) {
11096
11643
  recordExtractionError("go SQL call extraction", file.path, err);
11097
11644
  }
11645
+ try {
11646
+ endpoints.push(...gormEndpointsFromFile(file, service.dir));
11647
+ } catch (err) {
11648
+ recordExtractionError("gorm data-axis extraction", file.path, err);
11649
+ }
11098
11650
  try {
11099
11651
  endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
11100
11652
  endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
@@ -11117,7 +11669,7 @@ async function addExternalEndpointEdges(graph, services) {
11117
11669
  if (!graph.hasNode(ep.infraId)) {
11118
11670
  const node = {
11119
11671
  id: ep.infraId,
11120
- type: import_types36.NodeType.InfraNode,
11672
+ type: import_types37.NodeType.InfraNode,
11121
11673
  name: ep.name,
11122
11674
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
11123
11675
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -11130,21 +11682,21 @@ async function addExternalEndpointEdges(graph, services) {
11130
11682
  }
11131
11683
  if (ep.columns && ep.columns.length > 0) {
11132
11684
  const node = graph.getNodeAttributes(ep.infraId);
11133
- if (node.type === import_types36.NodeType.InfraNode) {
11685
+ if (node.type === import_types37.NodeType.InfraNode) {
11134
11686
  graph.replaceNodeAttributes(ep.infraId, {
11135
11687
  ...node,
11136
11688
  columns: foldColumns(
11137
11689
  node.columns,
11138
11690
  ep.columns,
11139
- import_types36.Provenance.EXTRACTED,
11140
- (0, import_types36.confidenceForExtracted)(ep.confidenceKind)
11691
+ import_types37.Provenance.EXTRACTED,
11692
+ (0, import_types37.confidenceForExtracted)(ep.confidenceKind)
11141
11693
  )
11142
11694
  });
11143
11695
  }
11144
11696
  }
11145
11697
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
11146
11698
  const node = graph.getNodeAttributes(ep.infraId);
11147
- if (node.type === import_types36.NodeType.InfraNode) {
11699
+ if (node.type === import_types37.NodeType.InfraNode) {
11148
11700
  graph.replaceNodeAttributes(ep.infraId, {
11149
11701
  ...node,
11150
11702
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -11152,7 +11704,7 @@ async function addExternalEndpointEdges(graph, services) {
11152
11704
  }
11153
11705
  }
11154
11706
  const edgeType = edgeTypeFromEndpoint(ep);
11155
- const confidence = (0, import_types36.confidenceForExtracted)(ep.confidenceKind);
11707
+ const confidence = (0, import_types37.confidenceForExtracted)(ep.confidenceKind);
11156
11708
  const relFile = toPosix(ep.evidence.file);
11157
11709
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
11158
11710
  graph,
@@ -11162,7 +11714,7 @@ async function addExternalEndpointEdges(graph, services) {
11162
11714
  );
11163
11715
  nodesAdded += n;
11164
11716
  edgesAdded += e;
11165
- if (!(0, import_types36.passesExtractedFloor)(confidence)) {
11717
+ if (!(0, import_types37.passesExtractedFloor)(confidence)) {
11166
11718
  noteExtractedDropped({
11167
11719
  source: fileNodeId,
11168
11720
  target: ep.infraId,
@@ -11182,7 +11734,7 @@ async function addExternalEndpointEdges(graph, services) {
11182
11734
  source: fileNodeId,
11183
11735
  target: ep.infraId,
11184
11736
  type: edgeType,
11185
- provenance: import_types36.Provenance.EXTRACTED,
11737
+ provenance: import_types37.Provenance.EXTRACTED,
11186
11738
  confidence,
11187
11739
  evidence: ep.evidence
11188
11740
  };
@@ -11205,7 +11757,7 @@ async function addCallEdges(graph, services) {
11205
11757
 
11206
11758
  // src/extract/table-edges.ts
11207
11759
  init_cjs_shims();
11208
- var import_types37 = require("@neat.is/types");
11760
+ var import_types38 = require("@neat.is/types");
11209
11761
  async function addTableEdges(graph, services) {
11210
11762
  let nodesAdded = 0;
11211
11763
  let edgesAdded = 0;
@@ -11219,6 +11771,7 @@ async function addTableEdges(graph, services) {
11219
11771
  refs.push(...sqlalchemyForeignKeys(file, service.dir));
11220
11772
  refs.push(...railsSchemaForeignKeys(file, service.dir));
11221
11773
  refs.push(...laravelMigrationForeignKeys(file, service.dir));
11774
+ refs.push(...gormForeignKeys(file, service.dir));
11222
11775
  modelRefs.push(...railsModelForeignKeys(file, service.dir));
11223
11776
  modelRefs.push(...laravelModelForeignKeys(file, service.dir));
11224
11777
  } catch (err) {
@@ -11232,20 +11785,20 @@ async function addTableEdges(graph, services) {
11232
11785
  }
11233
11786
  refs.push(...modelRefs);
11234
11787
  for (const ref of refs) {
11235
- const childId = (0, import_types37.infraId)("sql-table", ref.childTable);
11236
- const parentId = (0, import_types37.infraId)("sql-table", ref.parentTable);
11788
+ const childId = (0, import_types38.infraId)("sql-table", ref.childTable);
11789
+ const parentId = (0, import_types38.infraId)("sql-table", ref.parentTable);
11237
11790
  if (childId === parentId) continue;
11238
11791
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
11239
11792
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
11240
- const edgeId = (0, import_types37.extractedEdgeId)(childId, parentId, import_types37.EdgeType.REFERENCES);
11793
+ const edgeId = (0, import_types38.extractedEdgeId)(childId, parentId, import_types38.EdgeType.REFERENCES);
11241
11794
  if (graph.hasEdge(edgeId)) continue;
11242
11795
  const edge = {
11243
11796
  id: edgeId,
11244
11797
  source: childId,
11245
11798
  target: parentId,
11246
- type: import_types37.EdgeType.REFERENCES,
11247
- provenance: import_types37.Provenance.EXTRACTED,
11248
- confidence: (0, import_types37.confidenceForExtracted)("structural"),
11799
+ type: import_types38.EdgeType.REFERENCES,
11800
+ provenance: import_types38.Provenance.EXTRACTED,
11801
+ confidence: (0, import_types38.confidenceForExtracted)("structural"),
11249
11802
  evidence: ref.evidence
11250
11803
  };
11251
11804
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -11258,7 +11811,7 @@ function ensureTableNode(graph, id, name) {
11258
11811
  if (graph.hasNode(id)) return 0;
11259
11812
  const node = {
11260
11813
  id,
11261
- type: import_types37.NodeType.InfraNode,
11814
+ type: import_types38.NodeType.InfraNode,
11262
11815
  name,
11263
11816
  provider: "self",
11264
11817
  kind: "sql-table"
@@ -11272,16 +11825,16 @@ init_cjs_shims();
11272
11825
 
11273
11826
  // src/extract/infra/docker-compose.ts
11274
11827
  init_cjs_shims();
11275
- var import_node_path48 = __toESM(require("path"), 1);
11276
- var import_types39 = require("@neat.is/types");
11828
+ var import_node_path49 = __toESM(require("path"), 1);
11829
+ var import_types40 = require("@neat.is/types");
11277
11830
 
11278
11831
  // src/extract/infra/shared.ts
11279
11832
  init_cjs_shims();
11280
- var import_types38 = require("@neat.is/types");
11833
+ var import_types39 = require("@neat.is/types");
11281
11834
  function makeInfraNode(kind, name, provider = "self", extras) {
11282
11835
  return {
11283
- id: (0, import_types38.infraId)(kind, name),
11284
- type: import_types38.NodeType.InfraNode,
11836
+ id: (0, import_types39.infraId)(kind, name),
11837
+ type: import_types39.NodeType.InfraNode,
11285
11838
  name,
11286
11839
  provider,
11287
11840
  kind,
@@ -11325,8 +11878,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
11325
11878
  source: anchorId,
11326
11879
  target: node.id,
11327
11880
  type: edgeType,
11328
- provenance: import_types38.Provenance.EXTRACTED,
11329
- confidence: (0, import_types38.confidenceForExtracted)("structural"),
11881
+ provenance: import_types39.Provenance.EXTRACTED,
11882
+ confidence: (0, import_types39.confidenceForExtracted)("structural"),
11330
11883
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11331
11884
  };
11332
11885
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11343,7 +11896,7 @@ function dependsOnList(value) {
11343
11896
  }
11344
11897
  function serviceNameToServiceNode(name, services) {
11345
11898
  for (const s of services) {
11346
- if (s.node.name === name || import_node_path48.default.basename(s.dir) === name) return s.node.id;
11899
+ if (s.node.name === name || import_node_path49.default.basename(s.dir) === name) return s.node.id;
11347
11900
  }
11348
11901
  return null;
11349
11902
  }
@@ -11352,7 +11905,7 @@ async function addComposeInfra(graph, scanPath, services) {
11352
11905
  let edgesAdded = 0;
11353
11906
  let composePath = null;
11354
11907
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
11355
- const abs = import_node_path48.default.join(scanPath, name);
11908
+ const abs = import_node_path49.default.join(scanPath, name);
11356
11909
  if (await exists(abs)) {
11357
11910
  composePath = abs;
11358
11911
  break;
@@ -11365,13 +11918,13 @@ async function addComposeInfra(graph, scanPath, services) {
11365
11918
  } catch (err) {
11366
11919
  recordExtractionError(
11367
11920
  "infra docker-compose",
11368
- import_node_path48.default.relative(scanPath, composePath),
11921
+ import_node_path49.default.relative(scanPath, composePath),
11369
11922
  err
11370
11923
  );
11371
11924
  return { nodesAdded, edgesAdded };
11372
11925
  }
11373
11926
  if (!compose?.services) return { nodesAdded, edgesAdded };
11374
- const evidenceFile = import_node_path48.default.relative(scanPath, composePath).split(import_node_path48.default.sep).join("/");
11927
+ const evidenceFile = import_node_path49.default.relative(scanPath, composePath).split(import_node_path49.default.sep).join("/");
11375
11928
  const composeNameToNodeId = /* @__PURE__ */ new Map();
11376
11929
  for (const [composeName, svc] of Object.entries(compose.services)) {
11377
11930
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -11393,15 +11946,15 @@ async function addComposeInfra(graph, scanPath, services) {
11393
11946
  for (const dep of dependsOnList(svc.depends_on)) {
11394
11947
  const targetId = composeNameToNodeId.get(dep);
11395
11948
  if (!targetId) continue;
11396
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types39.EdgeType.DEPENDS_ON);
11949
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types40.EdgeType.DEPENDS_ON);
11397
11950
  if (graph.hasEdge(edgeId)) continue;
11398
11951
  const edge = {
11399
11952
  id: edgeId,
11400
11953
  source: sourceId,
11401
11954
  target: targetId,
11402
- type: import_types39.EdgeType.DEPENDS_ON,
11403
- provenance: import_types39.Provenance.EXTRACTED,
11404
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
11955
+ type: import_types40.EdgeType.DEPENDS_ON,
11956
+ provenance: import_types40.Provenance.EXTRACTED,
11957
+ confidence: (0, import_types40.confidenceForExtracted)("structural"),
11405
11958
  evidence: { file: evidenceFile }
11406
11959
  };
11407
11960
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11413,9 +11966,9 @@ async function addComposeInfra(graph, scanPath, services) {
11413
11966
 
11414
11967
  // src/extract/infra/dockerfile.ts
11415
11968
  init_cjs_shims();
11416
- var import_node_path49 = __toESM(require("path"), 1);
11969
+ var import_node_path50 = __toESM(require("path"), 1);
11417
11970
  var import_node_fs18 = require("fs");
11418
- var import_types40 = require("@neat.is/types");
11971
+ var import_types41 = require("@neat.is/types");
11419
11972
  function readDockerfile(content) {
11420
11973
  let image = null;
11421
11974
  const ports = [];
@@ -11444,7 +11997,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11444
11997
  let nodesAdded = 0;
11445
11998
  let edgesAdded = 0;
11446
11999
  for (const service of services) {
11447
- const dockerfilePath = import_node_path49.default.join(service.dir, "Dockerfile");
12000
+ const dockerfilePath = import_node_path50.default.join(service.dir, "Dockerfile");
11448
12001
  if (!await exists(dockerfilePath)) continue;
11449
12002
  let content;
11450
12003
  try {
@@ -11452,7 +12005,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11452
12005
  } catch (err) {
11453
12006
  recordExtractionError(
11454
12007
  "infra dockerfile",
11455
- import_node_path49.default.relative(scanPath, dockerfilePath),
12008
+ import_node_path50.default.relative(scanPath, dockerfilePath),
11456
12009
  err
11457
12010
  );
11458
12011
  continue;
@@ -11464,8 +12017,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11464
12017
  graph.addNode(node.id, node);
11465
12018
  nodesAdded++;
11466
12019
  }
11467
- const relDockerfile = toPosix(import_node_path49.default.relative(service.dir, dockerfilePath));
11468
- const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, dockerfilePath));
12020
+ const relDockerfile = toPosix(import_node_path50.default.relative(service.dir, dockerfilePath));
12021
+ const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, dockerfilePath));
11469
12022
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11470
12023
  graph,
11471
12024
  service.pkg.name,
@@ -11474,15 +12027,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11474
12027
  );
11475
12028
  nodesAdded += fn;
11476
12029
  edgesAdded += fe;
11477
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types40.EdgeType.RUNS_ON);
12030
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types41.EdgeType.RUNS_ON);
11478
12031
  if (!graph.hasEdge(edgeId)) {
11479
12032
  const edge = {
11480
12033
  id: edgeId,
11481
12034
  source: fileNodeId,
11482
12035
  target: node.id,
11483
- type: import_types40.EdgeType.RUNS_ON,
11484
- provenance: import_types40.Provenance.EXTRACTED,
11485
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12036
+ type: import_types41.EdgeType.RUNS_ON,
12037
+ provenance: import_types41.Provenance.EXTRACTED,
12038
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11486
12039
  evidence: {
11487
12040
  file: evidenceFile,
11488
12041
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -11497,15 +12050,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11497
12050
  graph.addNode(portNode.id, portNode);
11498
12051
  nodesAdded++;
11499
12052
  }
11500
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types40.EdgeType.CONNECTS_TO);
12053
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types41.EdgeType.CONNECTS_TO);
11501
12054
  if (graph.hasEdge(portEdgeId)) continue;
11502
12055
  const portEdge = {
11503
12056
  id: portEdgeId,
11504
12057
  source: fileNodeId,
11505
12058
  target: portNode.id,
11506
- type: import_types40.EdgeType.CONNECTS_TO,
11507
- provenance: import_types40.Provenance.EXTRACTED,
11508
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12059
+ type: import_types41.EdgeType.CONNECTS_TO,
12060
+ provenance: import_types41.Provenance.EXTRACTED,
12061
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11509
12062
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
11510
12063
  };
11511
12064
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -11518,8 +12071,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11518
12071
  // src/extract/infra/terraform.ts
11519
12072
  init_cjs_shims();
11520
12073
  var import_node_fs19 = require("fs");
11521
- var import_node_path50 = __toESM(require("path"), 1);
11522
- var import_types41 = require("@neat.is/types");
12074
+ var import_node_path51 = __toESM(require("path"), 1);
12075
+ var import_types42 = require("@neat.is/types");
11523
12076
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
11524
12077
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
11525
12078
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -11529,11 +12082,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
11529
12082
  for (const entry2 of entries) {
11530
12083
  if (entry2.isDirectory()) {
11531
12084
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
11532
- const child = import_node_path50.default.join(start, entry2.name);
12085
+ const child = import_node_path51.default.join(start, entry2.name);
11533
12086
  if (await isPythonVenvDir(child)) continue;
11534
12087
  out.push(...await walkTfFiles(child, depth + 1, max));
11535
12088
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
11536
- out.push(import_node_path50.default.join(start, entry2.name));
12089
+ out.push(import_node_path51.default.join(start, entry2.name));
11537
12090
  }
11538
12091
  }
11539
12092
  return out;
@@ -11565,7 +12118,7 @@ async function addTerraformResources(graph, scanPath) {
11565
12118
  const files = await walkTfFiles(scanPath);
11566
12119
  for (const file of files) {
11567
12120
  const content = await import_node_fs19.promises.readFile(file, "utf8");
11568
- const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, file));
12121
+ const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, file));
11569
12122
  const resources = [];
11570
12123
  const byKey = /* @__PURE__ */ new Map();
11571
12124
  RESOURCE_RE.lastIndex = 0;
@@ -11600,16 +12153,16 @@ async function addTerraformResources(graph, scanPath) {
11600
12153
  if (!target) continue;
11601
12154
  if (seen.has(target.nodeId)) continue;
11602
12155
  seen.add(target.nodeId);
11603
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types41.EdgeType.DEPENDS_ON);
12156
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types42.EdgeType.DEPENDS_ON);
11604
12157
  if (graph.hasEdge(edgeId)) continue;
11605
12158
  const line = lineAt2(content, resource.bodyOffset + ref.index);
11606
12159
  const edge = {
11607
12160
  id: edgeId,
11608
12161
  source: resource.nodeId,
11609
12162
  target: target.nodeId,
11610
- type: import_types41.EdgeType.DEPENDS_ON,
11611
- provenance: import_types41.Provenance.EXTRACTED,
11612
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12163
+ type: import_types42.EdgeType.DEPENDS_ON,
12164
+ provenance: import_types42.Provenance.EXTRACTED,
12165
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
11613
12166
  evidence: { file: evidenceFile, line, snippet: key }
11614
12167
  };
11615
12168
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11623,7 +12176,7 @@ async function addTerraformResources(graph, scanPath) {
11623
12176
  // src/extract/infra/k8s.ts
11624
12177
  init_cjs_shims();
11625
12178
  var import_node_fs20 = require("fs");
11626
- var import_node_path51 = __toESM(require("path"), 1);
12179
+ var import_node_path52 = __toESM(require("path"), 1);
11627
12180
  var import_yaml3 = require("yaml");
11628
12181
  var K8S_KIND_TO_INFRA_KIND = {
11629
12182
  Service: "k8s-service",
@@ -11641,11 +12194,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
11641
12194
  for (const entry2 of entries) {
11642
12195
  if (entry2.isDirectory()) {
11643
12196
  if (IGNORED_DIRS.has(entry2.name)) continue;
11644
- const child = import_node_path51.default.join(start, entry2.name);
12197
+ const child = import_node_path52.default.join(start, entry2.name);
11645
12198
  if (await isPythonVenvDir(child)) continue;
11646
12199
  out.push(...await walkYamlFiles2(child, depth + 1, max));
11647
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path51.default.extname(entry2.name))) {
11648
- out.push(import_node_path51.default.join(start, entry2.name));
12200
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path52.default.extname(entry2.name))) {
12201
+ out.push(import_node_path52.default.join(start, entry2.name));
11649
12202
  }
11650
12203
  }
11651
12204
  return out;
@@ -11679,13 +12232,13 @@ async function addK8sResources(graph, scanPath) {
11679
12232
  // src/extract/infra/cloudflare.ts
11680
12233
  init_cjs_shims();
11681
12234
  var import_node_fs21 = require("fs");
11682
- var import_node_path52 = __toESM(require("path"), 1);
12235
+ var import_node_path53 = __toESM(require("path"), 1);
11683
12236
  var import_smol_toml2 = require("smol-toml");
11684
- var import_types42 = require("@neat.is/types");
12237
+ var import_types43 = require("@neat.is/types");
11685
12238
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
11686
12239
  async function readWranglerConfig(dir) {
11687
12240
  for (const filename of WRANGLER_FILENAMES) {
11688
- const abs = import_node_path52.default.join(dir, filename);
12241
+ const abs = import_node_path53.default.join(dir, filename);
11689
12242
  if (!await exists(abs)) continue;
11690
12243
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
11691
12244
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -11729,8 +12282,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
11729
12282
  source: anchorId,
11730
12283
  target: node.id,
11731
12284
  type: edgeType,
11732
- provenance: import_types42.Provenance.EXTRACTED,
11733
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12285
+ provenance: import_types43.Provenance.EXTRACTED,
12286
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11734
12287
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11735
12288
  };
11736
12289
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11748,11 +12301,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11748
12301
  try {
11749
12302
  read = await readWranglerConfig(service.dir);
11750
12303
  } catch (err) {
11751
- recordExtractionError("infra cloudflare", import_node_path52.default.relative(scanPath, service.dir), err);
12304
+ recordExtractionError("infra cloudflare", import_node_path53.default.relative(scanPath, service.dir), err);
11752
12305
  continue;
11753
12306
  }
11754
12307
  if (!read || !read.config.name) continue;
11755
- const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, import_node_path52.default.join(service.dir, read.relFile)));
12308
+ const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, read.relFile)));
11756
12309
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
11757
12310
  }
11758
12311
  for (const worker of discovered) {
@@ -11764,7 +12317,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11764
12317
  }
11765
12318
  let anchorId = service.node.id;
11766
12319
  if (config.main) {
11767
- const entryRelPath = toPosix(import_node_path52.default.normalize(config.main));
12320
+ const entryRelPath = toPosix(import_node_path53.default.normalize(config.main));
11768
12321
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11769
12322
  graph,
11770
12323
  service.pkg.name,
@@ -11791,15 +12344,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11791
12344
  nodesAdded++;
11792
12345
  }
11793
12346
  if (runtimeNode.id !== anchorId) {
11794
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types42.EdgeType.RUNS_ON);
12347
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types43.EdgeType.RUNS_ON);
11795
12348
  if (!graph.hasEdge(runsOnId)) {
11796
12349
  const edge = {
11797
12350
  id: runsOnId,
11798
12351
  source: anchorId,
11799
12352
  target: runtimeNode.id,
11800
- type: import_types42.EdgeType.RUNS_ON,
11801
- provenance: import_types42.Provenance.EXTRACTED,
11802
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12353
+ type: import_types43.EdgeType.RUNS_ON,
12354
+ provenance: import_types43.Provenance.EXTRACTED,
12355
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11803
12356
  evidence: {
11804
12357
  file: evidenceFile,
11805
12358
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -11813,7 +12366,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11813
12366
  const result = addResourceEdge(
11814
12367
  graph,
11815
12368
  anchorId,
11816
- import_types42.EdgeType.CONNECTS_TO,
12369
+ import_types43.EdgeType.CONNECTS_TO,
11817
12370
  "cloudflare-route",
11818
12371
  route,
11819
12372
  evidenceFile,
@@ -11837,7 +12390,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11837
12390
  const result = addResourceEdge(
11838
12391
  graph,
11839
12392
  anchorId,
11840
- import_types42.EdgeType.DEPENDS_ON,
12393
+ import_types43.EdgeType.DEPENDS_ON,
11841
12394
  group.kind,
11842
12395
  name,
11843
12396
  evidenceFile,
@@ -11851,7 +12404,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11851
12404
  const result = addResourceEdge(
11852
12405
  graph,
11853
12406
  anchorId,
11854
- import_types42.EdgeType.DEPENDS_ON,
12407
+ import_types43.EdgeType.DEPENDS_ON,
11855
12408
  "cloudflare-cron",
11856
12409
  cron,
11857
12410
  evidenceFile,
@@ -11864,7 +12417,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11864
12417
  const result = addResourceEdge(
11865
12418
  graph,
11866
12419
  anchorId,
11867
- import_types42.EdgeType.DEPENDS_ON,
12420
+ import_types43.EdgeType.DEPENDS_ON,
11868
12421
  "cloudflare-env-var",
11869
12422
  varName,
11870
12423
  evidenceFile,
@@ -11877,15 +12430,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11877
12430
  if (!svc.service) continue;
11878
12431
  const target = workerIndex.get(svc.service);
11879
12432
  if (target && target.anchorId !== anchorId) {
11880
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types42.EdgeType.CALLS);
12433
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types43.EdgeType.CALLS);
11881
12434
  if (!graph.hasEdge(edgeId)) {
11882
12435
  const edge = {
11883
12436
  id: edgeId,
11884
12437
  source: anchorId,
11885
12438
  target: target.anchorId,
11886
- type: import_types42.EdgeType.CALLS,
11887
- provenance: import_types42.Provenance.EXTRACTED,
11888
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12439
+ type: import_types43.EdgeType.CALLS,
12440
+ provenance: import_types43.Provenance.EXTRACTED,
12441
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11889
12442
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
11890
12443
  };
11891
12444
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11896,7 +12449,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11896
12449
  const result = addResourceEdge(
11897
12450
  graph,
11898
12451
  anchorId,
11899
- import_types42.EdgeType.DEPENDS_ON,
12452
+ import_types43.EdgeType.DEPENDS_ON,
11900
12453
  "cloudflare-service-binding",
11901
12454
  svc.service,
11902
12455
  evidenceFile,
@@ -11912,12 +12465,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11912
12465
  // src/extract/infra/vercel.ts
11913
12466
  init_cjs_shims();
11914
12467
  var import_node_fs22 = require("fs");
11915
- var import_node_path53 = __toESM(require("path"), 1);
11916
- var import_types43 = require("@neat.is/types");
12468
+ var import_node_path54 = __toESM(require("path"), 1);
12469
+ var import_types44 = require("@neat.is/types");
11917
12470
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
11918
12471
  async function readVercelConfig(dir) {
11919
12472
  for (const filename of VERCEL_CONFIG_FILENAMES) {
11920
- const abs = import_node_path53.default.join(dir, filename);
12473
+ const abs = import_node_path54.default.join(dir, filename);
11921
12474
  if (!await exists(abs)) continue;
11922
12475
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
11923
12476
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -11926,7 +12479,7 @@ async function readVercelConfig(dir) {
11926
12479
  return null;
11927
12480
  }
11928
12481
  async function readLinkedProjectName(dir) {
11929
- const abs = import_node_path53.default.join(dir, ".vercel", "project.json");
12482
+ const abs = import_node_path54.default.join(dir, ".vercel", "project.json");
11930
12483
  if (!await exists(abs)) return void 0;
11931
12484
  const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
11932
12485
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -11944,7 +12497,7 @@ async function addVercelServices(graph, services, scanPath) {
11944
12497
  read = await readVercelConfig(service.dir);
11945
12498
  projectName = await readLinkedProjectName(service.dir);
11946
12499
  } catch (err) {
11947
- recordExtractionError("infra vercel", import_node_path53.default.relative(scanPath, service.dir), err);
12500
+ recordExtractionError("infra vercel", import_node_path54.default.relative(scanPath, service.dir), err);
11948
12501
  continue;
11949
12502
  }
11950
12503
  if (!read && !projectName) continue;
@@ -11960,7 +12513,7 @@ async function addVercelServices(graph, services, scanPath) {
11960
12513
  const anchorId = service.node.id;
11961
12514
  if (!read) continue;
11962
12515
  const { config, relFile, raw } = read;
11963
- const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, relFile)));
12516
+ const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
11964
12517
  const add = (edgeType, kind, name) => {
11965
12518
  if (!name) return;
11966
12519
  const result = emitPlatformResourceEdge(
@@ -11976,12 +12529,12 @@ async function addVercelServices(graph, services, scanPath) {
11976
12529
  nodesAdded += result.nodesAdded;
11977
12530
  edgesAdded += result.edgesAdded;
11978
12531
  };
11979
- add(import_types43.EdgeType.RUNS_ON, "vercel", "vercel");
11980
- for (const cron of config.crons ?? []) add(import_types43.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
11981
- for (const varName of Object.keys(config.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
11982
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12532
+ add(import_types44.EdgeType.RUNS_ON, "vercel", "vercel");
12533
+ for (const cron of config.crons ?? []) add(import_types44.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12534
+ for (const varName of Object.keys(config.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12535
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
11983
12536
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
11984
- add(import_types43.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12537
+ add(import_types44.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
11985
12538
  }
11986
12539
  }
11987
12540
  return { nodesAdded, edgesAdded };
@@ -11990,13 +12543,13 @@ async function addVercelServices(graph, services, scanPath) {
11990
12543
  // src/extract/infra/railway.ts
11991
12544
  init_cjs_shims();
11992
12545
  var import_node_fs23 = require("fs");
11993
- var import_node_path54 = __toESM(require("path"), 1);
12546
+ var import_node_path55 = __toESM(require("path"), 1);
11994
12547
  var import_smol_toml3 = require("smol-toml");
11995
- var import_types44 = require("@neat.is/types");
12548
+ var import_types45 = require("@neat.is/types");
11996
12549
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
11997
12550
  async function readRailwayConfig(dir) {
11998
12551
  for (const filename of RAILWAY_FILENAMES) {
11999
- const abs = import_node_path54.default.join(dir, filename);
12552
+ const abs = import_node_path55.default.join(dir, filename);
12000
12553
  if (!await exists(abs)) continue;
12001
12554
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
12002
12555
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12012,7 +12565,7 @@ async function addRailwayServices(graph, services, scanPath) {
12012
12565
  try {
12013
12566
  read = await readRailwayConfig(service.dir);
12014
12567
  } catch (err) {
12015
- recordExtractionError("infra railway", import_node_path54.default.relative(scanPath, service.dir), err);
12568
+ recordExtractionError("infra railway", import_node_path55.default.relative(scanPath, service.dir), err);
12016
12569
  continue;
12017
12570
  }
12018
12571
  if (!read) continue;
@@ -12022,7 +12575,7 @@ async function addRailwayServices(graph, services, scanPath) {
12022
12575
  }
12023
12576
  const anchorId = service.node.id;
12024
12577
  const { config, relFile, raw } = read;
12025
- const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
12578
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12026
12579
  const add = (edgeType, kind, name) => {
12027
12580
  if (!name) return;
12028
12581
  const result = emitPlatformResourceEdge(
@@ -12038,9 +12591,9 @@ async function addRailwayServices(graph, services, scanPath) {
12038
12591
  nodesAdded += result.nodesAdded;
12039
12592
  edgesAdded += result.edgesAdded;
12040
12593
  };
12041
- add(import_types44.EdgeType.RUNS_ON, "railway", "railway");
12042
- add(import_types44.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12043
- add(import_types44.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12594
+ add(import_types45.EdgeType.RUNS_ON, "railway", "railway");
12595
+ add(import_types45.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12596
+ add(import_types45.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12044
12597
  }
12045
12598
  return { nodesAdded, edgesAdded };
12046
12599
  }
@@ -12048,12 +12601,12 @@ async function addRailwayServices(graph, services, scanPath) {
12048
12601
  // src/extract/infra/supabase.ts
12049
12602
  init_cjs_shims();
12050
12603
  var import_node_fs24 = require("fs");
12051
- var import_node_path55 = __toESM(require("path"), 1);
12604
+ var import_node_path56 = __toESM(require("path"), 1);
12052
12605
  var import_smol_toml4 = require("smol-toml");
12053
- var import_types45 = require("@neat.is/types");
12606
+ var import_types46 = require("@neat.is/types");
12054
12607
  async function readSupabaseConfig(dir) {
12055
- const relFile = import_node_path55.default.join("supabase", "config.toml");
12056
- const abs = import_node_path55.default.join(dir, relFile);
12608
+ const relFile = import_node_path56.default.join("supabase", "config.toml");
12609
+ const abs = import_node_path56.default.join(dir, relFile);
12057
12610
  if (!await exists(abs)) return null;
12058
12611
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
12059
12612
  const config = (0, import_smol_toml4.parse)(raw);
@@ -12067,7 +12620,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12067
12620
  try {
12068
12621
  read = await readSupabaseConfig(service.dir);
12069
12622
  } catch (err) {
12070
- recordExtractionError("infra supabase", import_node_path55.default.relative(scanPath, service.dir), err);
12623
+ recordExtractionError("infra supabase", import_node_path56.default.relative(scanPath, service.dir), err);
12071
12624
  continue;
12072
12625
  }
12073
12626
  if (!read) continue;
@@ -12082,7 +12635,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12082
12635
  });
12083
12636
  }
12084
12637
  const anchorId = service.node.id;
12085
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12638
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12086
12639
  const add = (edgeType, kind, name) => {
12087
12640
  if (!name) return;
12088
12641
  const result = emitPlatformResourceEdge(
@@ -12098,10 +12651,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
12098
12651
  nodesAdded += result.nodesAdded;
12099
12652
  edgesAdded += result.edgesAdded;
12100
12653
  };
12101
- add(import_types45.EdgeType.RUNS_ON, "supabase", "supabase");
12102
- for (const fn of Object.keys(config.functions ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "supabase-function", fn);
12103
- if (config.storage) add(import_types45.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12104
- if (config.auth) add(import_types45.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12654
+ add(import_types46.EdgeType.RUNS_ON, "supabase", "supabase");
12655
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types46.EdgeType.DEPENDS_ON, "supabase-function", fn);
12656
+ if (config.storage) add(import_types46.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12657
+ if (config.auth) add(import_types46.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12105
12658
  }
12106
12659
  return { nodesAdded, edgesAdded };
12107
12660
  }
@@ -12124,14 +12677,14 @@ async function addInfra(graph, scanPath, services) {
12124
12677
 
12125
12678
  // src/extract/zod-shapes.ts
12126
12679
  init_cjs_shims();
12127
- var import_node_path56 = __toESM(require("path"), 1);
12128
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12680
+ var import_node_path57 = __toESM(require("path"), 1);
12681
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
12129
12682
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
12130
- var import_types46 = require("@neat.is/types");
12683
+ var import_types47 = require("@neat.is/types");
12131
12684
  var ZOD_IMPORT_RE = /\bzod\b/;
12132
12685
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12133
12686
  function parserForExt3(ext) {
12134
- const p = new import_tree_sitter15.default();
12687
+ const p = new import_tree_sitter16.default();
12135
12688
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12136
12689
  return p;
12137
12690
  }
@@ -12219,7 +12772,7 @@ function topLevelSchemas(root) {
12219
12772
  }
12220
12773
  function zodShapesFromFile(file, serviceDir) {
12221
12774
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12222
- const tree = parseSource3(parserForExt3(import_node_path56.default.extname(file.path)), file.content);
12775
+ const tree = parseSource3(parserForExt3(import_node_path57.default.extname(file.path)), file.content);
12223
12776
  const out = [];
12224
12777
  const seen = /* @__PURE__ */ new Set();
12225
12778
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -12233,11 +12786,11 @@ function zodShapesFromFile(file, serviceDir) {
12233
12786
  seen.add(name);
12234
12787
  const line = call.startPosition.row + 1;
12235
12788
  out.push({
12236
- infraId: (0, import_types46.infraId)("zod-schema", name),
12789
+ infraId: (0, import_types47.infraId)("zod-schema", name),
12237
12790
  name,
12238
12791
  fields,
12239
12792
  evidence: {
12240
- file: import_node_path56.default.relative(serviceDir, file.path),
12793
+ file: import_node_path57.default.relative(serviceDir, file.path),
12241
12794
  line,
12242
12795
  snippet: snippet(file.content, line)
12243
12796
  }
@@ -12268,7 +12821,7 @@ async function addZodShapes(graph, services) {
12268
12821
  if (!graph.hasNode(shape.infraId)) {
12269
12822
  const node = {
12270
12823
  id: shape.infraId,
12271
- type: import_types46.NodeType.InfraNode,
12824
+ type: import_types47.NodeType.InfraNode,
12272
12825
  name: shape.name,
12273
12826
  provider: "self",
12274
12827
  kind: "zod-schema"
@@ -12278,14 +12831,14 @@ async function addZodShapes(graph, services) {
12278
12831
  }
12279
12832
  if (shape.fields.length > 0) {
12280
12833
  const node = graph.getNodeAttributes(shape.infraId);
12281
- if (node.type === import_types46.NodeType.InfraNode) {
12834
+ if (node.type === import_types47.NodeType.InfraNode) {
12282
12835
  graph.replaceNodeAttributes(shape.infraId, {
12283
12836
  ...node,
12284
12837
  columns: foldColumns(
12285
12838
  node.columns,
12286
12839
  shape.fields,
12287
- import_types46.Provenance.EXTRACTED,
12288
- (0, import_types46.confidenceForExtracted)("structural")
12840
+ import_types47.Provenance.EXTRACTED,
12841
+ (0, import_types47.confidenceForExtracted)("structural")
12289
12842
  )
12290
12843
  });
12291
12844
  }
@@ -12299,15 +12852,15 @@ async function addZodShapes(graph, services) {
12299
12852
  );
12300
12853
  nodesAdded += n;
12301
12854
  edgesAdded += e;
12302
- const edgeId = (0, import_types46.extractedEdgeId)(fileNodeId, shape.infraId, import_types46.EdgeType.CONTAINS);
12855
+ const edgeId = (0, import_types47.extractedEdgeId)(fileNodeId, shape.infraId, import_types47.EdgeType.CONTAINS);
12303
12856
  if (!graph.hasEdge(edgeId)) {
12304
12857
  const edge = {
12305
12858
  id: edgeId,
12306
12859
  source: fileNodeId,
12307
12860
  target: shape.infraId,
12308
- type: import_types46.EdgeType.CONTAINS,
12309
- provenance: import_types46.Provenance.EXTRACTED,
12310
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
12861
+ type: import_types47.EdgeType.CONTAINS,
12862
+ provenance: import_types47.Provenance.EXTRACTED,
12863
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
12311
12864
  evidence: shape.evidence
12312
12865
  };
12313
12866
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -12321,7 +12874,7 @@ async function addZodShapes(graph, services) {
12321
12874
 
12322
12875
  // src/extract/firestore-rules.ts
12323
12876
  init_cjs_shims();
12324
- var import_types47 = require("@neat.is/types");
12877
+ var import_types48 = require("@neat.is/types");
12325
12878
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
12326
12879
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
12327
12880
  function stripComments(src) {
@@ -12461,7 +13014,7 @@ async function addFirestoreRules(graph, services) {
12461
13014
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
12462
13015
  graph.forEachNode((id, attrs) => {
12463
13016
  const node = attrs;
12464
- if (node.type !== import_types47.NodeType.InfraNode) return;
13017
+ if (node.type !== import_types48.NodeType.InfraNode) return;
12465
13018
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
12466
13019
  const fields = guards.get(collectionKeyFromName(node.name));
12467
13020
  if (!fields || fields.size === 0) return;
@@ -12474,17 +13027,17 @@ async function addFirestoreRules(graph, services) {
12474
13027
  }
12475
13028
 
12476
13029
  // src/extract/index.ts
12477
- var import_node_path58 = __toESM(require("path"), 1);
13030
+ var import_node_path59 = __toESM(require("path"), 1);
12478
13031
 
12479
13032
  // src/extract/retire.ts
12480
13033
  init_cjs_shims();
12481
13034
  var import_node_fs25 = require("fs");
12482
- var import_node_path57 = __toESM(require("path"), 1);
12483
- var import_types48 = require("@neat.is/types");
13035
+ var import_node_path58 = __toESM(require("path"), 1);
13036
+ var import_types49 = require("@neat.is/types");
12484
13037
  function dropOrphanedFileNodes(graph) {
12485
13038
  const orphans = [];
12486
13039
  graph.forEachNode((id, attrs) => {
12487
- if (attrs.type !== import_types48.NodeType.FileNode) return;
13040
+ if (attrs.type !== import_types49.NodeType.FileNode) return;
12488
13041
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
12489
13042
  orphans.push(id);
12490
13043
  }
@@ -12497,14 +13050,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
12497
13050
  const bases = [scanPath, ...serviceDirs];
12498
13051
  graph.forEachEdge((id, attrs) => {
12499
13052
  const edge = attrs;
12500
- if (edge.provenance !== import_types48.Provenance.EXTRACTED) return;
13053
+ if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
12501
13054
  const evidenceFile = edge.evidence?.file;
12502
13055
  if (!evidenceFile) return;
12503
- if (import_node_path57.default.isAbsolute(evidenceFile)) {
13056
+ if (import_node_path58.default.isAbsolute(evidenceFile)) {
12504
13057
  if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
12505
13058
  return;
12506
13059
  }
12507
- const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path57.default.join(base, evidenceFile)));
13060
+ const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path58.default.join(base, evidenceFile)));
12508
13061
  if (!found) toDrop.push(id);
12509
13062
  });
12510
13063
  for (const id of toDrop) graph.dropEdge(id);
@@ -12561,7 +13114,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12561
13114
  }
12562
13115
  const droppedEntries = drainDroppedExtracted();
12563
13116
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
12564
- const rejectedPath = import_node_path58.default.join(import_node_path58.default.dirname(opts.errorsPath), "rejected.ndjson");
13117
+ const rejectedPath = import_node_path59.default.join(import_node_path59.default.dirname(opts.errorsPath), "rejected.ndjson");
12565
13118
  try {
12566
13119
  await writeRejectedExtracted(droppedEntries, rejectedPath);
12567
13120
  } catch (err) {
@@ -12596,8 +13149,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12596
13149
  // src/persist.ts
12597
13150
  init_cjs_shims();
12598
13151
  var import_node_fs26 = require("fs");
12599
- var import_node_path59 = __toESM(require("path"), 1);
12600
- var import_types49 = require("@neat.is/types");
13152
+ var import_node_path60 = __toESM(require("path"), 1);
13153
+ var import_types50 = require("@neat.is/types");
12601
13154
  var SCHEMA_VERSION = 7;
12602
13155
  function migrateV1ToV2(payload) {
12603
13156
  const nodes = payload.graph.nodes;
@@ -12621,7 +13174,7 @@ function migrateV5ToV6(payload) {
12621
13174
  if (Array.isArray(nodes)) {
12622
13175
  for (const node of nodes) {
12623
13176
  const attrs = node.attributes;
12624
- if (!attrs || attrs.type !== import_types49.NodeType.InfraNode) continue;
13177
+ if (!attrs || attrs.type !== import_types50.NodeType.InfraNode) continue;
12625
13178
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
12626
13179
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
12627
13180
  }
@@ -12637,12 +13190,12 @@ function migrateV2ToV3(payload) {
12637
13190
  for (const edge of edges) {
12638
13191
  const attrs = edge.attributes;
12639
13192
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
12640
- attrs.provenance = import_types49.Provenance.OBSERVED;
13193
+ attrs.provenance = import_types50.Provenance.OBSERVED;
12641
13194
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
12642
13195
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
12643
13196
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
12644
13197
  if (type && source && target) {
12645
- const newId = (0, import_types49.observedEdgeId)(source, target, type);
13198
+ const newId = (0, import_types50.observedEdgeId)(source, target, type);
12646
13199
  attrs.id = newId;
12647
13200
  if (edge.key) edge.key = newId;
12648
13201
  }
@@ -12651,7 +13204,7 @@ function migrateV2ToV3(payload) {
12651
13204
  return { ...payload, schemaVersion: 3 };
12652
13205
  }
12653
13206
  async function ensureDir(filePath) {
12654
- await import_node_fs26.promises.mkdir(import_node_path59.default.dirname(filePath), { recursive: true });
13207
+ await import_node_fs26.promises.mkdir(import_node_path60.default.dirname(filePath), { recursive: true });
12655
13208
  }
12656
13209
  async function saveGraphToDisk(graph, outPath) {
12657
13210
  await ensureDir(outPath);
@@ -12741,23 +13294,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
12741
13294
 
12742
13295
  // src/projects.ts
12743
13296
  init_cjs_shims();
12744
- var import_node_path60 = __toESM(require("path"), 1);
13297
+ var import_node_path61 = __toESM(require("path"), 1);
12745
13298
  function pathsForProject(project, baseDir) {
12746
13299
  if (project === DEFAULT_PROJECT) {
12747
13300
  return {
12748
- snapshotPath: import_node_path60.default.join(baseDir, "graph.json"),
12749
- errorsPath: import_node_path60.default.join(baseDir, "errors.ndjson"),
12750
- staleEventsPath: import_node_path60.default.join(baseDir, "stale-events.ndjson"),
12751
- embeddingsCachePath: import_node_path60.default.join(baseDir, "embeddings.json"),
12752
- policyViolationsPath: import_node_path60.default.join(baseDir, "policy-violations.ndjson")
13301
+ snapshotPath: import_node_path61.default.join(baseDir, "graph.json"),
13302
+ errorsPath: import_node_path61.default.join(baseDir, "errors.ndjson"),
13303
+ staleEventsPath: import_node_path61.default.join(baseDir, "stale-events.ndjson"),
13304
+ embeddingsCachePath: import_node_path61.default.join(baseDir, "embeddings.json"),
13305
+ policyViolationsPath: import_node_path61.default.join(baseDir, "policy-violations.ndjson")
12753
13306
  };
12754
13307
  }
12755
13308
  return {
12756
- snapshotPath: import_node_path60.default.join(baseDir, `${project}.json`),
12757
- errorsPath: import_node_path60.default.join(baseDir, `errors.${project}.ndjson`),
12758
- staleEventsPath: import_node_path60.default.join(baseDir, `stale-events.${project}.ndjson`),
12759
- embeddingsCachePath: import_node_path60.default.join(baseDir, `embeddings.${project}.json`),
12760
- policyViolationsPath: import_node_path60.default.join(baseDir, `policy-violations.${project}.ndjson`)
13309
+ snapshotPath: import_node_path61.default.join(baseDir, `${project}.json`),
13310
+ errorsPath: import_node_path61.default.join(baseDir, `errors.${project}.ndjson`),
13311
+ staleEventsPath: import_node_path61.default.join(baseDir, `stale-events.${project}.ndjson`),
13312
+ embeddingsCachePath: import_node_path61.default.join(baseDir, `embeddings.${project}.json`),
13313
+ policyViolationsPath: import_node_path61.default.join(baseDir, `policy-violations.${project}.ndjson`)
12761
13314
  };
12762
13315
  }
12763
13316
  var Projects = class {
@@ -12795,19 +13348,19 @@ var Projects = class {
12795
13348
  init_cjs_shims();
12796
13349
  var import_fastify2 = __toESM(require("fastify"), 1);
12797
13350
  var import_cors = __toESM(require("@fastify/cors"), 1);
12798
- var import_types79 = require("@neat.is/types");
13351
+ var import_types80 = require("@neat.is/types");
12799
13352
 
12800
13353
  // src/extend/index.ts
12801
13354
  init_cjs_shims();
12802
13355
  var import_node_fs28 = require("fs");
12803
- var import_node_path62 = __toESM(require("path"), 1);
13356
+ var import_node_path63 = __toESM(require("path"), 1);
12804
13357
  var import_node_os2 = __toESM(require("os"), 1);
12805
13358
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
12806
13359
 
12807
13360
  // src/installers/package-manager.ts
12808
13361
  init_cjs_shims();
12809
13362
  var import_node_fs27 = require("fs");
12810
- var import_node_path61 = __toESM(require("path"), 1);
13363
+ var import_node_path62 = __toESM(require("path"), 1);
12811
13364
  var import_node_child_process = require("child_process");
12812
13365
  var LOCKFILE_PRIORITY = [
12813
13366
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -12829,22 +13382,22 @@ async function exists2(p) {
12829
13382
  }
12830
13383
  }
12831
13384
  async function detectPackageManager(serviceDir) {
12832
- let dir = import_node_path61.default.resolve(serviceDir);
13385
+ let dir = import_node_path62.default.resolve(serviceDir);
12833
13386
  const stops = /* @__PURE__ */ new Set();
12834
13387
  for (let i = 0; i < 64; i++) {
12835
13388
  if (stops.has(dir)) break;
12836
13389
  stops.add(dir);
12837
13390
  for (const candidate of LOCKFILE_PRIORITY) {
12838
- const lockPath = import_node_path61.default.join(dir, candidate.lockfile);
13391
+ const lockPath = import_node_path62.default.join(dir, candidate.lockfile);
12839
13392
  if (await exists2(lockPath)) {
12840
13393
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
12841
13394
  }
12842
13395
  }
12843
- const parent = import_node_path61.default.dirname(dir);
13396
+ const parent = import_node_path62.default.dirname(dir);
12844
13397
  if (parent === dir) break;
12845
13398
  dir = parent;
12846
13399
  }
12847
- return { pm: "npm", cwd: import_node_path61.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
13400
+ return { pm: "npm", cwd: import_node_path62.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
12848
13401
  }
12849
13402
  async function runPackageManagerInstall(cmd) {
12850
13403
  return new Promise((resolve) => {
@@ -12893,7 +13446,7 @@ async function fileExists2(p) {
12893
13446
  }
12894
13447
  }
12895
13448
  async function readPackageJson(scanPath) {
12896
- const pkgPath = import_node_path62.default.join(scanPath, "package.json");
13449
+ const pkgPath = import_node_path63.default.join(scanPath, "package.json");
12897
13450
  const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
12898
13451
  return JSON.parse(raw);
12899
13452
  }
@@ -12907,27 +13460,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
12907
13460
  ]);
12908
13461
  async function findHookFiles(scanPath) {
12909
13462
  const found = [];
12910
- const walk8 = async (dir) => {
13463
+ const walk9 = async (dir) => {
12911
13464
  const entries = await import_node_fs28.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
12912
13465
  for (const entry2 of entries) {
12913
13466
  if (entry2.isDirectory()) {
12914
13467
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
12915
- await walk8(import_node_path62.default.join(dir, entry2.name));
13468
+ await walk9(import_node_path63.default.join(dir, entry2.name));
12916
13469
  } else if (entry2.isFile()) {
12917
13470
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
12918
- const rel = import_node_path62.default.relative(scanPath, import_node_path62.default.join(dir, entry2.name));
12919
- found.push(rel.split(import_node_path62.default.sep).join("/"));
13471
+ const rel = import_node_path63.default.relative(scanPath, import_node_path63.default.join(dir, entry2.name));
13472
+ found.push(rel.split(import_node_path63.default.sep).join("/"));
12920
13473
  }
12921
13474
  }
12922
13475
  }
12923
13476
  };
12924
- await walk8(scanPath);
13477
+ await walk9(scanPath);
12925
13478
  return found.sort();
12926
13479
  }
12927
13480
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
12928
13481
  let fallback = null;
12929
13482
  for (const file of hookFiles) {
12930
- const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(scanPath, file), "utf8");
13483
+ const content = await import_node_fs28.promises.readFile(import_node_path63.default.join(scanPath, file), "utf8");
12931
13484
  const patched = splicedContent(content, snippet2);
12932
13485
  if (patched !== null) return { file, content, patched };
12933
13486
  if (fallback === null) fallback = { file, content };
@@ -12935,11 +13488,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
12935
13488
  return { file: fallback.file, content: fallback.content, patched: null };
12936
13489
  }
12937
13490
  function extendLogPath() {
12938
- return process.env.NEAT_EXTEND_LOG ?? import_node_path62.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
13491
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path63.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
12939
13492
  }
12940
13493
  async function appendExtendLog(entry2) {
12941
13494
  const logPath = extendLogPath();
12942
- await import_node_fs28.promises.mkdir(import_node_path62.default.dirname(logPath), { recursive: true });
13495
+ await import_node_fs28.promises.mkdir(import_node_path63.default.dirname(logPath), { recursive: true });
12943
13496
  await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
12944
13497
  }
12945
13498
  function splicedContent(fileContent, snippet2) {
@@ -12998,7 +13551,7 @@ function lookupInstrumentation(library, installedVersion) {
12998
13551
  }
12999
13552
  async function describeProjectInstrumentation(ctx) {
13000
13553
  const hookFiles = await findHookFiles(ctx.scanPath);
13001
- const envNeat = await fileExists2(import_node_path62.default.join(ctx.scanPath, ".env.neat"));
13554
+ const envNeat = await fileExists2(import_node_path63.default.join(ctx.scanPath, ".env.neat"));
13002
13555
  const registryInstrPackages = new Set(
13003
13556
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
13004
13557
  );
@@ -13020,7 +13573,7 @@ async function applyExtension(ctx, args, options) {
13020
13573
  );
13021
13574
  }
13022
13575
  for (const file of hookFiles) {
13023
- const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(ctx.scanPath, file), "utf8");
13576
+ const content = await import_node_fs28.promises.readFile(import_node_path63.default.join(ctx.scanPath, file), "utf8");
13024
13577
  if (content.includes(args.registration_snippet)) {
13025
13578
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
13026
13579
  }
@@ -13032,10 +13585,10 @@ async function applyExtension(ctx, args, options) {
13032
13585
  );
13033
13586
  }
13034
13587
  const primaryFile = primary.file;
13035
- const primaryPath = import_node_path62.default.join(ctx.scanPath, primaryFile);
13588
+ const primaryPath = import_node_path63.default.join(ctx.scanPath, primaryFile);
13036
13589
  const filesTouched = [];
13037
13590
  const depsAdded = [];
13038
- const pkgPath = import_node_path62.default.join(ctx.scanPath, "package.json");
13591
+ const pkgPath = import_node_path63.default.join(ctx.scanPath, "package.json");
13039
13592
  const pkg = await readPackageJson(ctx.scanPath);
13040
13593
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
13041
13594
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -13074,7 +13627,7 @@ async function dryRunExtension(ctx, args) {
13074
13627
  };
13075
13628
  }
13076
13629
  for (const file of hookFiles) {
13077
- const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(ctx.scanPath, file), "utf8");
13630
+ const content = await import_node_fs28.promises.readFile(import_node_path63.default.join(ctx.scanPath, file), "utf8");
13078
13631
  if (content.includes(args.registration_snippet)) {
13079
13632
  return {
13080
13633
  library: args.library,
@@ -13115,7 +13668,7 @@ async function rollbackExtension(ctx, args) {
13115
13668
  if (!match) {
13116
13669
  return { undone: false, message: "no apply found for library" };
13117
13670
  }
13118
- const pkgPath = import_node_path62.default.join(ctx.scanPath, "package.json");
13671
+ const pkgPath = import_node_path63.default.join(ctx.scanPath, "package.json");
13119
13672
  if (await fileExists2(pkgPath)) {
13120
13673
  const pkg = await readPackageJson(ctx.scanPath);
13121
13674
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -13126,7 +13679,7 @@ async function rollbackExtension(ctx, args) {
13126
13679
  }
13127
13680
  const hookFiles = await findHookFiles(ctx.scanPath);
13128
13681
  for (const file of hookFiles) {
13129
- const filePath = import_node_path62.default.join(ctx.scanPath, file);
13682
+ const filePath = import_node_path63.default.join(ctx.scanPath, file);
13130
13683
  const content = await import_node_fs28.promises.readFile(filePath, "utf8");
13131
13684
  if (content.includes(match.registration_snippet)) {
13132
13685
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -13142,39 +13695,39 @@ async function rollbackExtension(ctx, args) {
13142
13695
 
13143
13696
  // src/divergences.ts
13144
13697
  init_cjs_shims();
13145
- var import_types50 = require("@neat.is/types");
13698
+ var import_types51 = require("@neat.is/types");
13146
13699
  function bucketKey(source, target, type) {
13147
13700
  return `${type}|${source}|${target}`;
13148
13701
  }
13149
13702
  function bucketSourceFor(graph, edge) {
13150
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) return edge.source;
13151
- const parsed = (0, import_types50.parseFileId)(edge.source);
13703
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) return edge.source;
13704
+ const parsed = (0, import_types51.parseFileId)(edge.source);
13152
13705
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
13153
13706
  const target = graph.getNodeAttributes(edge.target);
13154
- if (target.type !== import_types50.NodeType.DatabaseNode) return edge.source;
13155
- return (0, import_types50.serviceId)(parsed.service);
13707
+ if (target.type !== import_types51.NodeType.DatabaseNode) return edge.source;
13708
+ return (0, import_types51.serviceId)(parsed.service);
13156
13709
  }
13157
13710
  function bucketEdges(graph) {
13158
13711
  const buckets2 = /* @__PURE__ */ new Map();
13159
13712
  graph.forEachEdge((id, attrs) => {
13160
13713
  const e = attrs;
13161
- const parsed = (0, import_types50.parseEdgeId)(id);
13714
+ const parsed = (0, import_types51.parseEdgeId)(id);
13162
13715
  const provenance = parsed?.provenance ?? e.provenance;
13163
13716
  const source = bucketSourceFor(graph, e);
13164
13717
  const key = bucketKey(source, e.target, e.type);
13165
13718
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
13166
13719
  switch (provenance) {
13167
- case import_types50.Provenance.EXTRACTED:
13720
+ case import_types51.Provenance.EXTRACTED:
13168
13721
  cur.extracted = e;
13169
13722
  break;
13170
- case import_types50.Provenance.OBSERVED:
13723
+ case import_types51.Provenance.OBSERVED:
13171
13724
  cur.observed = e;
13172
13725
  break;
13173
- case import_types50.Provenance.INFERRED:
13726
+ case import_types51.Provenance.INFERRED:
13174
13727
  cur.inferred = e;
13175
13728
  break;
13176
13729
  default:
13177
- if (e.provenance === import_types50.Provenance.STALE) cur.stale = e;
13730
+ if (e.provenance === import_types51.Provenance.STALE) cur.stale = e;
13178
13731
  }
13179
13732
  buckets2.set(key, cur);
13180
13733
  });
@@ -13183,22 +13736,22 @@ function bucketEdges(graph) {
13183
13736
  function nodeIsFrontier(graph, nodeId) {
13184
13737
  if (!graph.hasNode(nodeId)) return false;
13185
13738
  const attrs = graph.getNodeAttributes(nodeId);
13186
- return attrs.type === import_types50.NodeType.FrontierNode;
13739
+ return attrs.type === import_types51.NodeType.FrontierNode;
13187
13740
  }
13188
13741
  function nodeIsWebsocketChannel(graph, nodeId) {
13189
13742
  if (!graph.hasNode(nodeId)) return false;
13190
13743
  const attrs = graph.getNodeAttributes(nodeId);
13191
- return attrs.type === import_types50.NodeType.WebSocketChannelNode;
13744
+ return attrs.type === import_types51.NodeType.WebSocketChannelNode;
13192
13745
  }
13193
13746
  function nodeIsServerAction(graph, nodeId) {
13194
13747
  if (!graph.hasNode(nodeId)) return false;
13195
13748
  const attrs = graph.getNodeAttributes(nodeId);
13196
- return attrs.type === import_types50.NodeType.ServerActionNode;
13749
+ return attrs.type === import_types51.NodeType.ServerActionNode;
13197
13750
  }
13198
13751
  function nodeIsSymbol(graph, nodeId) {
13199
13752
  if (!graph.hasNode(nodeId)) return false;
13200
13753
  const attrs = graph.getNodeAttributes(nodeId);
13201
- return attrs.type === import_types50.NodeType.SymbolNode;
13754
+ return attrs.type === import_types51.NodeType.SymbolNode;
13202
13755
  }
13203
13756
  function clampConfidence(n) {
13204
13757
  if (!Number.isFinite(n)) return 0;
@@ -13218,14 +13771,14 @@ function gradedConfidence(edge) {
13218
13771
  return clampConfidence(confidenceForEdge(edge));
13219
13772
  }
13220
13773
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
13221
- import_types50.EdgeType.CALLS,
13222
- import_types50.EdgeType.CONNECTS_TO,
13223
- import_types50.EdgeType.PUBLISHES_TO,
13224
- import_types50.EdgeType.CONSUMES_FROM
13774
+ import_types51.EdgeType.CALLS,
13775
+ import_types51.EdgeType.CONNECTS_TO,
13776
+ import_types51.EdgeType.PUBLISHES_TO,
13777
+ import_types51.EdgeType.CONSUMES_FROM
13225
13778
  ]);
13226
13779
  function detectMissingDivergences(graph, bucket) {
13227
13780
  const out = [];
13228
- if (bucket.type === import_types50.EdgeType.CONTAINS) return out;
13781
+ if (bucket.type === import_types51.EdgeType.CONTAINS) return out;
13229
13782
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
13230
13783
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
13231
13784
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -13267,7 +13820,7 @@ function declaredHostFor(svc) {
13267
13820
  function hasExtractedConfiguredBy(graph, svcId) {
13268
13821
  for (const edgeId of graph.outboundEdges(svcId)) {
13269
13822
  const e = graph.getEdgeAttributes(edgeId);
13270
- if (e.type === import_types50.EdgeType.CONFIGURED_BY && e.provenance === import_types50.Provenance.EXTRACTED) {
13823
+ if (e.type === import_types51.EdgeType.CONFIGURED_BY && e.provenance === import_types51.Provenance.EXTRACTED) {
13271
13824
  return true;
13272
13825
  }
13273
13826
  }
@@ -13280,10 +13833,10 @@ function detectHostMismatch(graph, svcId, svc) {
13280
13833
  const out = [];
13281
13834
  for (const edgeId of graph.outboundEdges(svcId)) {
13282
13835
  const edge = graph.getEdgeAttributes(edgeId);
13283
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13284
- if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
13836
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) continue;
13837
+ if (edge.provenance !== import_types51.Provenance.OBSERVED) continue;
13285
13838
  const target = graph.getNodeAttributes(edge.target);
13286
- if (target.type !== import_types50.NodeType.DatabaseNode) continue;
13839
+ if (target.type !== import_types51.NodeType.DatabaseNode) continue;
13287
13840
  const observedHost = target.host?.trim();
13288
13841
  if (!observedHost) continue;
13289
13842
  if (observedHost === declaredHost) continue;
@@ -13305,10 +13858,10 @@ function detectCompatDivergences(graph, svcId, svc) {
13305
13858
  const deps = svc.dependencies ?? {};
13306
13859
  for (const edgeId of graph.outboundEdges(svcId)) {
13307
13860
  const edge = graph.getEdgeAttributes(edgeId);
13308
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13309
- if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
13861
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) continue;
13862
+ if (edge.provenance !== import_types51.Provenance.OBSERVED) continue;
13310
13863
  const target = graph.getNodeAttributes(edge.target);
13311
- if (target.type !== import_types50.NodeType.DatabaseNode) continue;
13864
+ if (target.type !== import_types51.NodeType.DatabaseNode) continue;
13312
13865
  for (const pair of compatPairs()) {
13313
13866
  if (pair.engine !== target.engine) continue;
13314
13867
  const declared = deps[pair.driver];
@@ -13405,7 +13958,7 @@ function suppressHostMismatchHalves(all) {
13405
13958
  for (const d of all) {
13406
13959
  if (d.type !== "host-mismatch") continue;
13407
13960
  observedHalf.add(`${d.source}->${d.target}`);
13408
- declaredHalf.add((0, import_types50.databaseId)(d.extractedHost));
13961
+ declaredHalf.add((0, import_types51.databaseId)(d.extractedHost));
13409
13962
  }
13410
13963
  if (observedHalf.size === 0) return all;
13411
13964
  return all.filter((d) => {
@@ -13424,13 +13977,13 @@ function computeDivergences(graph, opts = {}) {
13424
13977
  }
13425
13978
  graph.forEachNode((nodeId, attrs) => {
13426
13979
  const n = attrs;
13427
- if (n.type === import_types50.NodeType.ServiceNode) {
13980
+ if (n.type === import_types51.NodeType.ServiceNode) {
13428
13981
  const svc = n;
13429
13982
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
13430
13983
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
13431
13984
  return;
13432
13985
  }
13433
- if (n.type === import_types50.NodeType.InfraNode && n.kind === "sql-table") {
13986
+ if (n.type === import_types51.NodeType.InfraNode && n.kind === "sql-table") {
13434
13987
  for (const d of detectColumnDrift(n)) all.push(d);
13435
13988
  }
13436
13989
  });
@@ -13466,7 +14019,7 @@ function computeDivergences(graph, opts = {}) {
13466
14019
  const bc = "column" in b && b.column ? b.column : "";
13467
14020
  return ac.localeCompare(bc);
13468
14021
  });
13469
- return import_types50.DivergenceResultSchema.parse({
14022
+ return import_types51.DivergenceResultSchema.parse({
13470
14023
  divergences: filtered,
13471
14024
  totalAffected: filtered.length,
13472
14025
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13599,26 +14152,26 @@ function canonicalJson(value) {
13599
14152
  init_cjs_shims();
13600
14153
  var import_node_fs30 = require("fs");
13601
14154
  var import_node_os3 = __toESM(require("os"), 1);
13602
- var import_node_path63 = __toESM(require("path"), 1);
13603
- var import_types51 = require("@neat.is/types");
14155
+ var import_node_path64 = __toESM(require("path"), 1);
14156
+ var import_types52 = require("@neat.is/types");
13604
14157
  var LOCK_TIMEOUT_MS = 5e3;
13605
14158
  var LOCK_RETRY_MS = 50;
13606
14159
  function neatHome() {
13607
14160
  const override = process.env.NEAT_HOME;
13608
- if (override && override.length > 0) return import_node_path63.default.resolve(override);
13609
- return import_node_path63.default.join(import_node_os3.default.homedir(), ".neat");
14161
+ if (override && override.length > 0) return import_node_path64.default.resolve(override);
14162
+ return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
13610
14163
  }
13611
14164
  function registryPath() {
13612
- return import_node_path63.default.join(neatHome(), "projects.json");
14165
+ return import_node_path64.default.join(neatHome(), "projects.json");
13613
14166
  }
13614
14167
  function registryLockPath() {
13615
- return import_node_path63.default.join(neatHome(), "projects.json.lock");
14168
+ return import_node_path64.default.join(neatHome(), "projects.json.lock");
13616
14169
  }
13617
14170
  function daemonPidPath() {
13618
- return import_node_path63.default.join(neatHome(), "neatd.pid");
14171
+ return import_node_path64.default.join(neatHome(), "neatd.pid");
13619
14172
  }
13620
14173
  function daemonsDir() {
13621
- return import_node_path63.default.join(neatHome(), "daemons");
14174
+ return import_node_path64.default.join(neatHome(), "daemons");
13622
14175
  }
13623
14176
  function isFiniteInt(v) {
13624
14177
  return typeof v === "number" && Number.isFinite(v);
@@ -13659,7 +14212,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
13659
14212
  const out = [];
13660
14213
  for (const name of names) {
13661
14214
  if (!name.endsWith(".json")) continue;
13662
- const file = import_node_path63.default.join(dir, name);
14215
+ const file = import_node_path64.default.join(dir, name);
13663
14216
  let raw;
13664
14217
  try {
13665
14218
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -13736,7 +14289,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
13736
14289
  }
13737
14290
  }
13738
14291
  async function writeAtomically(target, contents) {
13739
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(target), { recursive: true });
14292
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(target), { recursive: true });
13740
14293
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
13741
14294
  const fd = await import_node_fs30.promises.open(tmp, "w");
13742
14295
  try {
@@ -13749,7 +14302,7 @@ async function writeAtomically(target, contents) {
13749
14302
  }
13750
14303
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
13751
14304
  const deadline = Date.now() + timeoutMs;
13752
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(lockPath), { recursive: true });
14305
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(lockPath), { recursive: true });
13753
14306
  let probedHolder = false;
13754
14307
  while (true) {
13755
14308
  try {
@@ -13802,10 +14355,10 @@ async function readRegistry() {
13802
14355
  throw err;
13803
14356
  }
13804
14357
  const parsed = JSON.parse(raw);
13805
- return import_types51.RegistryFileSchema.parse(parsed);
14358
+ return import_types52.RegistryFileSchema.parse(parsed);
13806
14359
  }
13807
14360
  async function writeRegistry(reg) {
13808
- const validated = import_types51.RegistryFileSchema.parse(reg);
14361
+ const validated = import_types52.RegistryFileSchema.parse(reg);
13809
14362
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
13810
14363
  }
13811
14364
  async function getProject(name) {
@@ -13950,7 +14503,7 @@ init_auth();
13950
14503
  // src/connectors-config.ts
13951
14504
  init_cjs_shims();
13952
14505
  var import_node_os4 = __toESM(require("os"), 1);
13953
- var import_node_path64 = __toESM(require("path"), 1);
14506
+ var import_node_path65 = __toESM(require("path"), 1);
13954
14507
  var import_node_fs31 = require("fs");
13955
14508
  var CONNECTORS_CONFIG_VERSION = 1;
13956
14509
  var EnvRefUnsetError = class extends Error {
@@ -13965,11 +14518,11 @@ var EnvRefUnsetError = class extends Error {
13965
14518
  };
13966
14519
  function neatHome2() {
13967
14520
  const override = process.env.NEAT_HOME;
13968
- if (override && override.length > 0) return import_node_path64.default.resolve(override);
13969
- return import_node_path64.default.join(import_node_os4.default.homedir(), ".neat");
14521
+ if (override && override.length > 0) return import_node_path65.default.resolve(override);
14522
+ return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
13970
14523
  }
13971
14524
  function connectorsConfigPath(home = neatHome2()) {
13972
- return import_node_path64.default.join(home, "connectors.json");
14525
+ return import_node_path65.default.join(home, "connectors.json");
13973
14526
  }
13974
14527
  var MODE_MASK_LOOSER_THAN_0600 = 63;
13975
14528
  async function warnIfModeLooserThan0600(file) {
@@ -14156,15 +14709,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
14156
14709
 
14157
14710
  // src/connectors/index.ts
14158
14711
  init_cjs_shims();
14159
- var import_types52 = require("@neat.is/types");
14712
+ var import_types53 = require("@neat.is/types");
14160
14713
  var NO_ENV = "unknown";
14161
14714
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
14162
14715
  if (!graph.hasNode(targetNodeId)) return void 0;
14163
14716
  const sites = [];
14164
14717
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
14165
14718
  const edge = graph.getEdgeAttributes(edgeId);
14166
- if (edge.provenance !== import_types52.Provenance.EXTRACTED) continue;
14167
- const parsed = (0, import_types52.parseFileId)(edge.source);
14719
+ if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
14720
+ const parsed = (0, import_types53.parseFileId)(edge.source);
14168
14721
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
14169
14722
  const site = { relPath: edge.evidence.file };
14170
14723
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -14175,7 +14728,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
14175
14728
  function routeCallSiteFor(graph, targetNodeId) {
14176
14729
  if (!graph.hasNode(targetNodeId)) return void 0;
14177
14730
  const attrs = graph.getNodeAttributes(targetNodeId);
14178
- if (attrs.type !== import_types52.NodeType.RouteNode || !attrs.path) return void 0;
14731
+ if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
14179
14732
  const site = { relPath: attrs.path };
14180
14733
  if (attrs.line !== void 0) site.line = attrs.line;
14181
14734
  return site;
@@ -14656,10 +15209,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
14656
15209
  // src/connectors/supabase/map.ts
14657
15210
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
14658
15211
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
14659
- function targetFromRestPath(path69) {
14660
- const rpcMatch = REST_RPC_PATH_RE.exec(path69);
15212
+ function targetFromRestPath(path70) {
15213
+ const rpcMatch = REST_RPC_PATH_RE.exec(path70);
14661
15214
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
14662
- const tableMatch = REST_TABLE_PATH_RE.exec(path69);
15215
+ const tableMatch = REST_TABLE_PATH_RE.exec(path70);
14663
15216
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
14664
15217
  return null;
14665
15218
  }
@@ -14770,23 +15323,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
14770
15323
 
14771
15324
  // src/connectors/supabase/resolve.ts
14772
15325
  init_cjs_shims();
14773
- var import_types54 = require("@neat.is/types");
15326
+ var import_types55 = require("@neat.is/types");
14774
15327
  function createSupabaseResolveTarget(graph, config) {
14775
15328
  return (signal, _ctx) => {
14776
15329
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
14777
15330
  return null;
14778
15331
  }
14779
- const subResourceId = (0, import_types54.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15332
+ const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
14780
15333
  if (graph.hasNode(subResourceId)) {
14781
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15334
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14782
15335
  }
14783
- const bareResourceId = (0, import_types54.infraId)(signal.targetKind, signal.targetName);
15336
+ const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
14784
15337
  if (graph.hasNode(bareResourceId)) {
14785
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15338
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14786
15339
  }
14787
- const projectLevelId = (0, import_types54.infraId)("supabase", config.nodeRef);
15340
+ const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
14788
15341
  if (graph.hasNode(projectLevelId)) {
14789
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15342
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14790
15343
  }
14791
15344
  return null;
14792
15345
  };
@@ -14879,7 +15432,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
14879
15432
 
14880
15433
  // src/connectors/railway/index.ts
14881
15434
  init_cjs_shims();
14882
- var import_types58 = require("@neat.is/types");
15435
+ var import_types59 = require("@neat.is/types");
14883
15436
 
14884
15437
  // src/connectors/railway/client.ts
14885
15438
  init_cjs_shims();
@@ -15030,7 +15583,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
15030
15583
  const out = [];
15031
15584
  graph.forEachNode((_id, attrs) => {
15032
15585
  const node = attrs;
15033
- if (node.type !== import_types58.NodeType.RouteNode) return;
15586
+ if (node.type !== import_types59.NodeType.RouteNode) return;
15034
15587
  const route = attrs;
15035
15588
  if (route.service !== serviceName) return;
15036
15589
  out.push({
@@ -15134,12 +15687,12 @@ function createRailwayResolveTarget(config) {
15134
15687
  const serviceName = config.serviceNameById[config.serviceId];
15135
15688
  if (!serviceName) return null;
15136
15689
  if (signal.targetKind === ROUTE_TARGET_KIND) {
15137
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types58.EdgeType.CALLS };
15690
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
15138
15691
  }
15139
15692
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
15140
15693
  const peerName = config.serviceNameById[signal.targetName];
15141
15694
  if (!peerName) return null;
15142
- return { targetNodeId: (0, import_types58.serviceId)(peerName), serviceName, edgeType: import_types58.EdgeType.CONNECTS_TO };
15695
+ return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
15143
15696
  }
15144
15697
  return null;
15145
15698
  };
@@ -15263,9 +15816,9 @@ function parseFirebaseTargetName(targetName) {
15263
15816
  const secondSep = rest.indexOf(FIELD_SEP);
15264
15817
  if (secondSep === -1) return null;
15265
15818
  const method = rest.slice(0, secondSep);
15266
- const path69 = rest.slice(secondSep + 1);
15267
- if (!resourceName || !method || !path69) return null;
15268
- return { resourceName, method, path: path69 };
15819
+ const path70 = rest.slice(secondSep + 1);
15820
+ if (!resourceName || !method || !path70) return null;
15821
+ return { resourceName, method, path: path70 };
15269
15822
  }
15270
15823
  function resourceNameFor(type, labels) {
15271
15824
  if (!labels) return null;
@@ -15303,14 +15856,14 @@ function mapLogEntryToSignal(entry2) {
15303
15856
  if (!req2) return null;
15304
15857
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
15305
15858
  const method = req2.requestMethod.toUpperCase();
15306
- const path69 = pathFromRequestUrl(req2.requestUrl);
15307
- if (path69 === null) return null;
15859
+ const path70 = pathFromRequestUrl(req2.requestUrl);
15860
+ if (path70 === null) return null;
15308
15861
  const timestamp = entry2.timestamp;
15309
15862
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15310
15863
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
15311
15864
  return {
15312
15865
  targetKind: resourceType,
15313
- targetName: packFirebaseTargetName({ resourceName, method, path: path69 }),
15866
+ targetName: packFirebaseTargetName({ resourceName, method, path: path70 }),
15314
15867
  callCount: 1,
15315
15868
  errorCount: isError ? 1 : 0,
15316
15869
  lastObservedIso: timestamp
@@ -15327,7 +15880,7 @@ function mapLogEntriesToSignals(entries) {
15327
15880
 
15328
15881
  // src/connectors/firebase/resolve.ts
15329
15882
  init_cjs_shims();
15330
- var import_types59 = require("@neat.is/types");
15883
+ var import_types60 = require("@neat.is/types");
15331
15884
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
15332
15885
  switch (resourceType) {
15333
15886
  case "cloud_function":
@@ -15342,7 +15895,7 @@ function routeEntriesFor(graph, serviceName) {
15342
15895
  const entries = [];
15343
15896
  graph.forEachNode((_id, attrs) => {
15344
15897
  const node = attrs;
15345
- if (node.type !== import_types59.NodeType.RouteNode) return;
15898
+ if (node.type !== import_types60.NodeType.RouteNode) return;
15346
15899
  const route = attrs;
15347
15900
  if (route.service !== serviceName) return;
15348
15901
  entries.push({
@@ -15374,7 +15927,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
15374
15927
  return {
15375
15928
  targetNodeId: match.routeNodeId,
15376
15929
  serviceName,
15377
- edgeType: import_types59.EdgeType.CALLS
15930
+ edgeType: import_types60.EdgeType.CALLS
15378
15931
  };
15379
15932
  };
15380
15933
  }
@@ -15401,7 +15954,7 @@ init_cjs_shims();
15401
15954
 
15402
15955
  // src/connectors/cloudflare/connector.ts
15403
15956
  init_cjs_shims();
15404
- var import_types61 = require("@neat.is/types");
15957
+ var import_types62 = require("@neat.is/types");
15405
15958
 
15406
15959
  // src/connectors/cloudflare/client.ts
15407
15960
  init_cjs_shims();
@@ -15517,7 +16070,7 @@ function mapEventToSignal(event) {
15517
16070
  if (Number.isNaN(observedAt.getTime())) return null;
15518
16071
  const statusCode = metadata?.statusCode;
15519
16072
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
15520
- const path69 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
16073
+ const path70 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15521
16074
  return {
15522
16075
  targetKind: CLOUDFLARE_TARGET_KIND,
15523
16076
  targetName: scriptName,
@@ -15525,7 +16078,7 @@ function mapEventToSignal(event) {
15525
16078
  errorCount: isError ? 1 : 0,
15526
16079
  lastObservedIso: observedAt.toISOString(),
15527
16080
  method,
15528
- ...path69 ? { path: path69 } : {},
16081
+ ...path70 ? { path: path70 } : {},
15529
16082
  ...typeof statusCode === "number" ? { statusCode } : {},
15530
16083
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
15531
16084
  };
@@ -15565,19 +16118,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
15565
16118
  graph.forEachNode((id, attrs) => {
15566
16119
  if (found) return;
15567
16120
  const a = attrs;
15568
- if (a.type === import_types61.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
16121
+ if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15569
16122
  found = id;
15570
16123
  }
15571
16124
  });
15572
16125
  return found;
15573
16126
  }
15574
- function findMatchingRouteNode(graph, serviceName, method, path69) {
15575
- const normalizedPath = normalizePathTemplate(path69);
16127
+ function findMatchingRouteNode(graph, serviceName, method, path70) {
16128
+ const normalizedPath = normalizePathTemplate(path70);
15576
16129
  let found = null;
15577
16130
  graph.forEachNode((id, attrs) => {
15578
16131
  if (found) return;
15579
16132
  const a = attrs;
15580
- if (a.type !== import_types61.NodeType.RouteNode || a.service !== serviceName) return;
16133
+ if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
15581
16134
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
15582
16135
  const routeMethod = (a.method ?? "").toUpperCase();
15583
16136
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -15589,18 +16142,18 @@ function createCloudflareResolveTarget(config, graph) {
15589
16142
  return (signal) => {
15590
16143
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
15591
16144
  const scriptName = signal.targetName;
15592
- const { method, path: path69 } = signal;
16145
+ const { method, path: path70 } = signal;
15593
16146
  const resolveRouteGrain = (serviceName, wholeFileId) => {
15594
- if (!method || !path69) return wholeFileId;
15595
- return findMatchingRouteNode(graph, serviceName, method, path69) ?? wholeFileId;
16147
+ if (!method || !path70) return wholeFileId;
16148
+ return findMatchingRouteNode(graph, serviceName, method, path70) ?? wholeFileId;
15596
16149
  };
15597
16150
  const mapping = config.workers?.[scriptName];
15598
16151
  if (mapping) {
15599
- const wholeFileId = (0, import_types61.fileId)(mapping.service, mapping.entryFile);
16152
+ const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
15600
16153
  return {
15601
16154
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
15602
16155
  serviceName: mapping.service,
15603
- edgeType: import_types61.EdgeType.CALLS
16156
+ edgeType: import_types62.EdgeType.CALLS
15604
16157
  };
15605
16158
  }
15606
16159
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -15609,13 +16162,13 @@ function createCloudflareResolveTarget(config, graph) {
15609
16162
  return {
15610
16163
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
15611
16164
  serviceName: fileNode.service,
15612
- edgeType: import_types61.EdgeType.CALLS
16165
+ edgeType: import_types62.EdgeType.CALLS
15613
16166
  };
15614
16167
  }
15615
16168
  return {
15616
- targetNodeId: (0, import_types61.infraId)("cloudflare-worker", scriptName),
16169
+ targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
15617
16170
  serviceName: scriptName,
15618
- edgeType: import_types61.EdgeType.CALLS,
16171
+ edgeType: import_types62.EdgeType.CALLS,
15619
16172
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
15620
16173
  };
15621
16174
  };
@@ -15811,14 +16364,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
15811
16364
 
15812
16365
  // src/connectors/neon/resolve.ts
15813
16366
  init_cjs_shims();
15814
- var import_types65 = require("@neat.is/types");
16367
+ var import_types66 = require("@neat.is/types");
15815
16368
  function createNeonResolveTarget(config) {
15816
16369
  return (signal) => {
15817
16370
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
15818
16371
  return {
15819
- targetNodeId: (0, import_types65.infraId)("sql-table", signal.targetName),
16372
+ targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
15820
16373
  serviceName: config.serviceName,
15821
- edgeType: import_types65.EdgeType.CALLS,
16374
+ edgeType: import_types66.EdgeType.CALLS,
15822
16375
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
15823
16376
  };
15824
16377
  };
@@ -15944,9 +16497,9 @@ function parseCloudRunTargetName(targetName) {
15944
16497
  const secondSep = rest.indexOf(FIELD_SEP2);
15945
16498
  if (secondSep === -1) return null;
15946
16499
  const method = rest.slice(0, secondSep);
15947
- const path69 = rest.slice(secondSep + 1);
15948
- if (!serviceName || !method || !path69) return null;
15949
- return { serviceName, method, path: path69 };
16500
+ const path70 = rest.slice(secondSep + 1);
16501
+ if (!serviceName || !method || !path70) return null;
16502
+ return { serviceName, method, path: path70 };
15950
16503
  }
15951
16504
 
15952
16505
  // src/connectors/cloud-run/map.ts
@@ -15975,14 +16528,14 @@ function mapLogEntryToSignal2(entry2) {
15975
16528
  if (!req2) return null;
15976
16529
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
15977
16530
  const method = req2.requestMethod.toUpperCase();
15978
- const path69 = pathFromRequestUrl2(req2.requestUrl);
15979
- if (path69 === null) return null;
16531
+ const path70 = pathFromRequestUrl2(req2.requestUrl);
16532
+ if (path70 === null) return null;
15980
16533
  const timestamp = entry2.timestamp;
15981
16534
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15982
16535
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
15983
16536
  return {
15984
16537
  targetKind: CLOUD_RUN_TARGET_KIND,
15985
- targetName: packCloudRunTargetName({ serviceName, method, path: path69 }),
16538
+ targetName: packCloudRunTargetName({ serviceName, method, path: path70 }),
15986
16539
  callCount: 1,
15987
16540
  errorCount: isError ? 1 : 0,
15988
16541
  lastObservedIso: timestamp
@@ -15999,14 +16552,14 @@ function mapLogEntriesToSignals2(entries) {
15999
16552
 
16000
16553
  // src/connectors/cloud-run/resolve.ts
16001
16554
  init_cjs_shims();
16002
- var import_types69 = require("@neat.is/types");
16555
+ var import_types70 = require("@neat.is/types");
16003
16556
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
16004
16557
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
16005
16558
  let found = null;
16006
16559
  graph.forEachNode((_id, attrs) => {
16007
16560
  if (found) return;
16008
16561
  const node = attrs;
16009
- if (node.type !== import_types69.NodeType.RouteNode) return;
16562
+ if (node.type !== import_types70.NodeType.RouteNode) return;
16010
16563
  const route = attrs;
16011
16564
  if (route.service !== serviceName || !route.pathTemplate) return;
16012
16565
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -16021,23 +16574,23 @@ function createCloudRunResolveTarget(graph, config) {
16021
16574
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
16022
16575
  const identity = parseCloudRunTargetName(signal.targetName);
16023
16576
  if (!identity) return null;
16024
- const { serviceName: gcpServiceName, method, path: path69 } = identity;
16577
+ const { serviceName: gcpServiceName, method, path: path70 } = identity;
16025
16578
  const mappedService = config.serviceMap?.[gcpServiceName];
16026
16579
  if (mappedService) {
16027
16580
  const routeNodeId = findMatchingRouteNode2(
16028
16581
  graph,
16029
16582
  mappedService,
16030
16583
  method,
16031
- normalizePathTemplate(path69)
16584
+ normalizePathTemplate(path70)
16032
16585
  );
16033
16586
  if (routeNodeId) {
16034
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types69.EdgeType.CALLS };
16587
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
16035
16588
  }
16036
16589
  }
16037
16590
  return {
16038
- targetNodeId: (0, import_types69.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16591
+ targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16039
16592
  serviceName: mappedService ?? gcpServiceName,
16040
- edgeType: import_types69.EdgeType.CALLS,
16593
+ edgeType: import_types70.EdgeType.CALLS,
16041
16594
  ensureInfraNode: {
16042
16595
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
16043
16596
  name: gcpServiceName,
@@ -16078,7 +16631,7 @@ function createCloudRunConnector(graph, config = {}) {
16078
16631
 
16079
16632
  // src/connectors/render/index.ts
16080
16633
  init_cjs_shims();
16081
- var import_types72 = require("@neat.is/types");
16634
+ var import_types73 = require("@neat.is/types");
16082
16635
 
16083
16636
  // src/connectors/render/types.ts
16084
16637
  init_cjs_shims();
@@ -16156,7 +16709,7 @@ function buildRenderRouteIndex(graph, serviceName) {
16156
16709
  const out = [];
16157
16710
  graph.forEachNode((_id, attrs) => {
16158
16711
  const node = attrs;
16159
- if (node.type !== import_types72.NodeType.RouteNode) return;
16712
+ if (node.type !== import_types73.NodeType.RouteNode) return;
16160
16713
  const route = attrs;
16161
16714
  if (route.service !== serviceName) return;
16162
16715
  out.push({
@@ -16241,7 +16794,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
16241
16794
  function createRenderResolveTarget(config) {
16242
16795
  return (signal) => {
16243
16796
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
16244
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types72.EdgeType.CALLS };
16797
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
16245
16798
  }
16246
16799
  return null;
16247
16800
  };
@@ -16379,21 +16932,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
16379
16932
 
16380
16933
  // src/connectors/planetscale/resolve.ts
16381
16934
  init_cjs_shims();
16382
- var import_types76 = require("@neat.is/types");
16935
+ var import_types77 = require("@neat.is/types");
16383
16936
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
16384
16937
  function createPlanetscaleResolveTarget(graph, config) {
16385
16938
  const databaseName = `${config.organization}/${config.database}`;
16386
16939
  return (signal, _ctx) => {
16387
16940
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16388
- const tableId = (0, import_types76.infraId)("sql-table", signal.targetName);
16941
+ const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
16389
16942
  if (graph.hasNode(tableId)) {
16390
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types76.EdgeType.CALLS };
16943
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
16391
16944
  }
16392
- const providerId = (0, import_types76.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16945
+ const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16393
16946
  return {
16394
16947
  targetNodeId: providerId,
16395
16948
  serviceName: config.serviceName,
16396
- edgeType: import_types76.EdgeType.CALLS,
16949
+ edgeType: import_types77.EdgeType.CALLS,
16397
16950
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
16398
16951
  };
16399
16952
  };
@@ -17063,11 +17616,11 @@ function registerRoutes(scope, ctx) {
17063
17616
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
17064
17617
  const parsed = [];
17065
17618
  for (const c of candidates) {
17066
- const r = import_types79.DivergenceTypeSchema.safeParse(c);
17619
+ const r = import_types80.DivergenceTypeSchema.safeParse(c);
17067
17620
  if (!r.success) {
17068
17621
  return reply.code(400).send({
17069
17622
  error: `unknown divergence type "${c}"`,
17070
- allowed: import_types79.DivergenceTypeSchema.options
17623
+ allowed: import_types80.DivergenceTypeSchema.options
17071
17624
  });
17072
17625
  }
17073
17626
  parsed.push(r.data);
@@ -17376,7 +17929,7 @@ function registerRoutes(scope, ctx) {
17376
17929
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
17377
17930
  let violations = await log.readAll();
17378
17931
  if (req2.query.severity) {
17379
- const sev = import_types79.PolicySeveritySchema.safeParse(req2.query.severity);
17932
+ const sev = import_types80.PolicySeveritySchema.safeParse(req2.query.severity);
17380
17933
  if (!sev.success) {
17381
17934
  return reply.code(400).send({
17382
17935
  error: "invalid severity",
@@ -17415,7 +17968,7 @@ function registerRoutes(scope, ctx) {
17415
17968
  scope.post("/policies/check", async (req2, reply) => {
17416
17969
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
17417
17970
  if (!proj) return;
17418
- const parsed = import_types79.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
17971
+ const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
17419
17972
  if (!parsed.success) {
17420
17973
  return reply.code(400).send({
17421
17974
  error: "invalid /policies/check body",
@@ -17737,7 +18290,7 @@ init_auth();
17737
18290
  // src/unrouted.ts
17738
18291
  init_cjs_shims();
17739
18292
  var import_node_fs32 = require("fs");
17740
- var import_node_path65 = __toESM(require("path"), 1);
18293
+ var import_node_path66 = __toESM(require("path"), 1);
17741
18294
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
17742
18295
  return {
17743
18296
  timestamp: now.toISOString(),
@@ -17747,34 +18300,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
17747
18300
  };
17748
18301
  }
17749
18302
  async function appendUnroutedSpan(neatHome4, record) {
17750
- const target = import_node_path65.default.join(neatHome4, "errors.ndjson");
18303
+ const target = import_node_path66.default.join(neatHome4, "errors.ndjson");
17751
18304
  await import_node_fs32.promises.mkdir(neatHome4, { recursive: true });
17752
18305
  await import_node_fs32.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
17753
18306
  }
17754
18307
  function unroutedErrorsPath(neatHome4) {
17755
- return import_node_path65.default.join(neatHome4, "errors.ndjson");
18308
+ return import_node_path66.default.join(neatHome4, "errors.ndjson");
17756
18309
  }
17757
18310
 
17758
18311
  // src/daemon.ts
17759
- var import_types80 = require("@neat.is/types");
18312
+ var import_types81 = require("@neat.is/types");
17760
18313
  function daemonJsonPath(scanPath) {
17761
- return import_node_path66.default.join(scanPath, "neat-out", "daemon.json");
18314
+ return import_node_path67.default.join(scanPath, "neat-out", "daemon.json");
17762
18315
  }
17763
18316
  function daemonsDiscoveryDir(home) {
17764
18317
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
17765
- return import_node_path66.default.join(base, "daemons");
18318
+ return import_node_path67.default.join(base, "daemons");
17766
18319
  }
17767
18320
  function daemonDiscoveryPath(project, home) {
17768
- return import_node_path66.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
18321
+ return import_node_path67.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
17769
18322
  }
17770
18323
  function sanitizeDiscoveryName(project) {
17771
18324
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
17772
18325
  }
17773
18326
  function neatHomeFromEnv() {
17774
18327
  const env = process.env.NEAT_HOME;
17775
- if (env && env.length > 0) return import_node_path66.default.resolve(env);
18328
+ if (env && env.length > 0) return import_node_path67.default.resolve(env);
17776
18329
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17777
- return import_node_path66.default.join(home, ".neat");
18330
+ return import_node_path67.default.join(home, ".neat");
17778
18331
  }
17779
18332
  function resolveNeatVersion() {
17780
18333
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -17843,11 +18396,11 @@ function teardownSlot(slot) {
17843
18396
  }
17844
18397
  }
17845
18398
  function neatHomeFor(opts) {
17846
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path66.default.resolve(opts.neatHome);
18399
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path67.default.resolve(opts.neatHome);
17847
18400
  const env = process.env.NEAT_HOME;
17848
- if (env && env.length > 0) return import_node_path66.default.resolve(env);
18401
+ if (env && env.length > 0) return import_node_path67.default.resolve(env);
17849
18402
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17850
- return import_node_path66.default.join(home, ".neat");
18403
+ return import_node_path67.default.join(home, ".neat");
17851
18404
  }
17852
18405
  function routeSpanToProject(serviceName, projects) {
17853
18406
  if (!serviceName) return DEFAULT_PROJECT;
@@ -17895,11 +18448,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
17895
18448
  if (!serviceName) return true;
17896
18449
  if (serviceNameMatchesProject(serviceName, project)) return true;
17897
18450
  return graph.someNode(
17898
- (_id, attrs) => attrs.type === import_types80.NodeType.ServiceNode && attrs.name === serviceName
18451
+ (_id, attrs) => attrs.type === import_types81.NodeType.ServiceNode && attrs.name === serviceName
17899
18452
  );
17900
18453
  }
17901
18454
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
17902
- const paths = pathsForProject(entry2.name, import_node_path66.default.join(entry2.path, "neat-out"));
18455
+ const paths = pathsForProject(entry2.name, import_node_path67.default.join(entry2.path, "neat-out"));
17903
18456
  try {
17904
18457
  const stat = await import_node_fs33.promises.stat(entry2.path);
17905
18458
  if (!stat.isDirectory()) {
@@ -18015,7 +18568,7 @@ async function startDaemon(opts = {}) {
18015
18568
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
18016
18569
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
18017
18570
  const singleProject = projectArg;
18018
- const singleProjectPath = singleProject && projectPathArg ? import_node_path66.default.resolve(projectPathArg) : null;
18571
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path67.default.resolve(projectPathArg) : null;
18019
18572
  if (singleProject && !singleProjectPath) {
18020
18573
  throw new Error(
18021
18574
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -18030,7 +18583,7 @@ async function startDaemon(opts = {}) {
18030
18583
  );
18031
18584
  }
18032
18585
  }
18033
- const pidPath = import_node_path66.default.join(home, "neatd.pid");
18586
+ const pidPath = import_node_path67.default.join(home, "neatd.pid");
18034
18587
  await writeAtomically(pidPath, `${process.pid}
18035
18588
  `);
18036
18589
  const slots = /* @__PURE__ */ new Map();
@@ -18442,8 +18995,8 @@ async function startDaemon(opts = {}) {
18442
18995
  let registryWatcher = null;
18443
18996
  let reloadTimer = null;
18444
18997
  if (!singleProject) try {
18445
- const regDir = import_node_path66.default.dirname(regPath);
18446
- const regBase = import_node_path66.default.basename(regPath);
18998
+ const regDir = import_node_path67.default.dirname(regPath);
18999
+ const regBase = import_node_path67.default.basename(regPath);
18447
19000
  registryWatcher = (0, import_node_fs33.watch)(regDir, (_eventType, filename) => {
18448
19001
  if (filename !== null && filename !== regBase) return;
18449
19002
  if (reloadTimer) clearTimeout(reloadTimer);
@@ -18518,7 +19071,7 @@ init_cjs_shims();
18518
19071
  var import_node_child_process2 = require("child_process");
18519
19072
  var import_node_fs34 = require("fs");
18520
19073
  var import_node_net = __toESM(require("net"), 1);
18521
- var import_node_path67 = __toESM(require("path"), 1);
19074
+ var import_node_path68 = __toESM(require("path"), 1);
18522
19075
  var DEFAULT_WEB_PORT = 6328;
18523
19076
  var DEFAULT_REST_PORT = 8080;
18524
19077
  function asValidPort(value) {
@@ -18527,11 +19080,11 @@ function asValidPort(value) {
18527
19080
  }
18528
19081
  function projectRoot() {
18529
19082
  const fromEnv = process.env.NEAT_SCAN_PATH;
18530
- return import_node_path67.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
19083
+ return import_node_path68.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
18531
19084
  }
18532
19085
  async function readDaemonPorts(root) {
18533
19086
  try {
18534
- const raw = await import_node_fs34.promises.readFile(import_node_path67.default.join(root, "neat-out", "daemon.json"), "utf8");
19087
+ const raw = await import_node_fs34.promises.readFile(import_node_path68.default.join(root, "neat-out", "daemon.json"), "utf8");
18535
19088
  const parsed = JSON.parse(raw);
18536
19089
  const ports = parsed?.ports ?? {};
18537
19090
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -18576,10 +19129,10 @@ function resolveWebPackageDir() {
18576
19129
  eval("require")
18577
19130
  );
18578
19131
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
18579
- return import_node_path67.default.dirname(pkgJsonPath);
19132
+ return import_node_path68.default.dirname(pkgJsonPath);
18580
19133
  }
18581
19134
  function resolveStandaloneServerEntry(webDir) {
18582
- return import_node_path67.default.join(webDir, ".next/standalone/packages/web/server.js");
19135
+ return import_node_path68.default.join(webDir, ".next/standalone/packages/web/server.js");
18583
19136
  }
18584
19137
  async function pickInternalPort() {
18585
19138
  return new Promise((resolve, reject) => {
@@ -18632,7 +19185,7 @@ async function spawnWebUI(restPort, opts = {}) {
18632
19185
  NEAT_API_URL: apiUrl
18633
19186
  };
18634
19187
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
18635
- cwd: import_node_path67.default.dirname(serverEntry),
19188
+ cwd: import_node_path68.default.dirname(serverEntry),
18636
19189
  env,
18637
19190
  stdio: ["ignore", "inherit", "inherit"],
18638
19191
  detached: false
@@ -18808,14 +19361,14 @@ function localVersion() {
18808
19361
  }
18809
19362
  function neatHome3() {
18810
19363
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
18811
- return import_node_path68.default.resolve(process.env.NEAT_HOME);
19364
+ return import_node_path69.default.resolve(process.env.NEAT_HOME);
18812
19365
  }
18813
19366
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
18814
- return import_node_path68.default.join(home, ".neat");
19367
+ return import_node_path69.default.join(home, ".neat");
18815
19368
  }
18816
19369
  async function readPid() {
18817
19370
  try {
18818
- const raw = await import_node_fs35.promises.readFile(import_node_path68.default.join(neatHome3(), "neatd.pid"), "utf8");
19371
+ const raw = await import_node_fs35.promises.readFile(import_node_path69.default.join(neatHome3(), "neatd.pid"), "utf8");
18819
19372
  const n = Number.parseInt(raw.trim(), 10);
18820
19373
  return Number.isFinite(n) ? n : null;
18821
19374
  } catch {