@neat.is/core 0.7.6 → 0.7.8

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/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path67 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path67) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path67)) {
63
+ const path68 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path68) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path68)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -342,7 +342,7 @@ function pickEnv(spanAttrs, resourceAttrs) {
342
342
  return ENV_FALLBACK;
343
343
  }
344
344
  function normalizeDbSystem(attrs) {
345
- const raw = attrs["db.system"];
345
+ const raw = attrs["db.system"] ?? attrs["db.system.name"];
346
346
  if (typeof raw !== "string") return void 0;
347
347
  return raw === "mongoose" ? "mongodb" : raw;
348
348
  }
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
414
414
  const v = attrs[key];
415
415
  if (typeof v === "string" && v.length > 0) {
416
416
  const q = v.indexOf("?");
417
- const path67 = q === -1 ? v : v.slice(0, q);
418
- if (path67.length > 0) return path67;
417
+ const path68 = q === -1 ? v : v.slice(0, q);
418
+ if (path68.length > 0) return path68;
419
419
  }
420
420
  }
421
421
  return void 0;
@@ -434,6 +434,9 @@ function parseOtlpRequest(body) {
434
434
  for (const ss of rs.scopeSpans ?? []) {
435
435
  for (const span of ss.spans ?? []) {
436
436
  const attrs = attrsToRecord(span.attributes);
437
+ const dbSqlText = typeof attrs["db.statement"] === "string" ? attrs["db.statement"] : typeof attrs["db.query.text"] === "string" ? attrs["db.query.text"] : void 0;
438
+ const dbSystemName = normalizeDbSystem(attrs);
439
+ 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;
437
440
  const parsed = {
438
441
  service,
439
442
  resourceServiceNamePresent,
@@ -448,11 +451,11 @@ function parseOtlpRequest(body) {
448
451
  durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
449
452
  env: pickEnv(attrs, resourceAttrs),
450
453
  attributes: attrs,
451
- dbSystem: normalizeDbSystem(attrs),
454
+ dbSystem: dbSystemName,
452
455
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
453
456
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
454
- dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
455
- dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
457
+ dbTable: directDbTable ?? (dbSqlText ? tableFromSqlStatement(dbSqlText) ?? void 0 : void 0),
458
+ dbColumns: dbSqlText ? columnsFromSqlStatement(dbSqlText) : void 0,
456
459
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
457
460
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
458
461
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -1314,19 +1317,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1314
1317
  function longestIncomingWalk(graph, start, maxDepth) {
1315
1318
  let best = { path: [start], edges: [] };
1316
1319
  const visited = /* @__PURE__ */ new Set([start]);
1317
- function step(node, path67, edges) {
1318
- if (path67.length > best.path.length) {
1319
- best = { path: [...path67], edges: [...edges] };
1320
+ function step(node, path68, edges) {
1321
+ if (path68.length > best.path.length) {
1322
+ best = { path: [...path68], edges: [...edges] };
1320
1323
  }
1321
- if (path67.length - 1 >= maxDepth) return;
1324
+ if (path68.length - 1 >= maxDepth) return;
1322
1325
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1323
1326
  for (const [srcId, edge] of incoming) {
1324
1327
  if (visited.has(srcId)) continue;
1325
1328
  visited.add(srcId);
1326
- path67.push(srcId);
1329
+ path68.push(srcId);
1327
1330
  edges.push(edge);
1328
- step(srcId, path67, edges);
1329
- path67.pop();
1331
+ step(srcId, path68, edges);
1332
+ path68.pop();
1330
1333
  edges.pop();
1331
1334
  visited.delete(srcId);
1332
1335
  }
@@ -1334,11 +1337,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1334
1337
  step(start, [start], []);
1335
1338
  return best;
1336
1339
  }
1337
- function databaseRootCauseShape(graph, origin, walk8) {
1340
+ function databaseRootCauseShape(graph, origin, walk9) {
1338
1341
  const targetDb = origin;
1339
1342
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1340
1343
  if (candidatePairs.length === 0) return null;
1341
- for (const id of walk8.path) {
1344
+ for (const id of walk9.path) {
1342
1345
  const owner = resolveOwningService(graph, id);
1343
1346
  if (!owner) continue;
1344
1347
  const { id: serviceId9, svc } = owner;
@@ -1365,8 +1368,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
1365
1368
  }
1366
1369
  return null;
1367
1370
  }
1368
- function serviceRootCauseShape(graph, _origin, walk8) {
1369
- for (const id of walk8.path) {
1371
+ function serviceRootCauseShape(graph, _origin, walk9) {
1372
+ for (const id of walk9.path) {
1370
1373
  const owner = resolveOwningService(graph, id);
1371
1374
  if (!owner) continue;
1372
1375
  const { id: serviceId9, svc } = owner;
@@ -1402,15 +1405,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
1402
1405
  }
1403
1406
  return null;
1404
1407
  }
1405
- function fileRootCauseShape(graph, origin, walk8) {
1408
+ function fileRootCauseShape(graph, origin, walk9) {
1406
1409
  const owner = resolveOwningService(graph, origin.id);
1407
1410
  if (!owner) return null;
1408
- return serviceRootCauseShape(graph, owner.svc, walk8);
1411
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1409
1412
  }
1410
- function symbolRootCauseShape(graph, origin, walk8) {
1413
+ function symbolRootCauseShape(graph, origin, walk9) {
1411
1414
  const owner = resolveOwningService(graph, origin.id);
1412
1415
  if (!owner) return null;
1413
- return serviceRootCauseShape(graph, owner.svc, walk8);
1416
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1414
1417
  }
1415
1418
  var rootCauseShapes = {
1416
1419
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1423,16 +1426,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1423
1426
  const origin = graph.getNodeAttributes(errorNodeId);
1424
1427
  const shape = rootCauseShapes[origin.type];
1425
1428
  if (shape) {
1426
- const walk8 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1427
- const match = shape(graph, origin, walk8);
1429
+ const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1430
+ const match = shape(graph, origin, walk9);
1428
1431
  if (match) {
1429
1432
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1430
1433
  return import_types.RootCauseResultSchema.parse({
1431
1434
  rootCauseNode: match.rootCauseNode,
1432
1435
  rootCauseReason: reason,
1433
- traversalPath: walk8.path,
1434
- edgeProvenances: walk8.edges.map((e) => e.provenance),
1435
- confidence: confidenceFromMix(walk8.edges),
1436
+ traversalPath: walk9.path,
1437
+ edgeProvenances: walk9.edges.map((e) => e.provenance),
1438
+ confidence: confidenceFromMix(walk9.edges),
1436
1439
  fixRecommendation: match.fixRecommendation
1437
1440
  });
1438
1441
  }
@@ -1533,26 +1536,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
1533
1536
  return best;
1534
1537
  }
1535
1538
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1536
- const path67 = [originServiceId];
1539
+ const path68 = [originServiceId];
1537
1540
  const edges = [];
1538
1541
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1539
1542
  let current = originServiceId;
1540
1543
  for (let depth = 0; depth < maxDepth; depth++) {
1541
1544
  const hop = dominantFailingCall(graph, current, visited);
1542
1545
  if (!hop) break;
1543
- path67.push(hop.nextService);
1546
+ path68.push(hop.nextService);
1544
1547
  edges.push(hop.edge);
1545
1548
  visited.add(hop.nextService);
1546
1549
  current = hop.nextService;
1547
1550
  }
1548
1551
  if (edges.length === 0) return null;
1549
- return { path: path67, edges, culprit: current };
1552
+ return { path: path68, edges, culprit: current };
1550
1553
  }
1551
1554
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1552
1555
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1553
1556
  if (!chain) return null;
1554
1557
  const culprit = chain.culprit;
1555
- const path67 = [...chain.path];
1558
+ const path68 = [...chain.path];
1556
1559
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1557
1560
  const baseConfidence = confidenceFromMix(chain.edges);
1558
1561
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1560,14 +1563,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1560
1563
  if (loc) {
1561
1564
  let rootCauseNode = culprit;
1562
1565
  if (loc.fileNode) {
1563
- path67.push(loc.fileNode);
1566
+ path68.push(loc.fileNode);
1564
1567
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1565
1568
  rootCauseNode = loc.fileNode;
1566
1569
  }
1567
1570
  return import_types.RootCauseResultSchema.parse({
1568
1571
  rootCauseNode,
1569
1572
  rootCauseReason: loc.rootCauseReason,
1570
- traversalPath: path67,
1573
+ traversalPath: path68,
1571
1574
  edgeProvenances,
1572
1575
  confidence,
1573
1576
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1579,7 +1582,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1579
1582
  return import_types.RootCauseResultSchema.parse({
1580
1583
  rootCauseNode: culprit,
1581
1584
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1582
- traversalPath: path67,
1585
+ traversalPath: path68,
1583
1586
  edgeProvenances,
1584
1587
  confidence,
1585
1588
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2514,14 +2517,14 @@ function buildServiceHostIndex(services) {
2514
2517
  }
2515
2518
  async function walkSourceFiles(dir) {
2516
2519
  const out = [];
2517
- async function walk8(current) {
2520
+ async function walk9(current) {
2518
2521
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2519
2522
  for (const entry of entries) {
2520
2523
  const full = import_node_path5.default.join(current, entry.name);
2521
2524
  if (entry.isDirectory()) {
2522
2525
  if (IGNORED_DIRS.has(entry.name)) continue;
2523
2526
  if (await isPythonVenvDir(full)) continue;
2524
- await walk8(full);
2527
+ await walk9(full);
2525
2528
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2526
2529
  // would attribute our instrumentation imports to the user's service.
2527
2530
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2529,7 +2532,7 @@ async function walkSourceFiles(dir) {
2529
2532
  }
2530
2533
  }
2531
2534
  }
2532
- await walk8(dir);
2535
+ await walk9(dir);
2533
2536
  return out;
2534
2537
  }
2535
2538
  async function loadSourceFiles(dir) {
@@ -3042,8 +3045,9 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
3042
3045
  "all"
3043
3046
  ]);
3044
3047
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
3048
+ var NET_HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
3045
3049
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
3046
- function ginRoutesFromSource(source, parser) {
3050
+ function goRouterRoutesFromSource(source, parser, framework) {
3047
3051
  const tree = parseSource2(parser, source);
3048
3052
  const prefixes = /* @__PURE__ */ new Map();
3049
3053
  const out = [];
@@ -3053,10 +3057,12 @@ function ginRoutesFromSource(source, parser) {
3053
3057
  const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
3054
3058
  if (name && value?.type === "call_expression") {
3055
3059
  const fn2 = value.childForFieldName("function");
3056
- const field = fn2?.childForFieldName("field")?.text;
3057
- const first2 = value.childForFieldName("arguments")?.namedChild(0);
3058
- if (field === "Group" && first2?.type === "interpreted_string_literal") {
3059
- prefixes.set(name, first2.text.slice(1, -1));
3060
+ if (fn2?.childForFieldName("field")?.text === "Group") {
3061
+ const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
3062
+ if (leaf2 !== null) {
3063
+ const parent = fn2.childForFieldName("operand")?.text ?? "";
3064
+ prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
3065
+ }
3060
3066
  }
3061
3067
  }
3062
3068
  return;
@@ -3067,18 +3073,127 @@ function ginRoutesFromSource(source, parser) {
3067
3073
  const method = fn.childForFieldName("field")?.text?.toUpperCase();
3068
3074
  if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
3069
3075
  const receiver = fn.childForFieldName("operand")?.text ?? "";
3070
- const first = node.childForFieldName("arguments")?.namedChild(0);
3071
- if (first?.type !== "interpreted_string_literal") return;
3072
- const leaf = first.text.slice(1, -1);
3076
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
3077
+ if (leaf === null) return;
3073
3078
  out.push({
3074
- method: method === "ALL" ? "ALL" : method,
3079
+ method,
3075
3080
  pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
3076
3081
  line: node.startPosition.row + 1,
3077
- framework: "gin"
3082
+ framework
3078
3083
  });
3079
3084
  });
3080
3085
  return out;
3081
3086
  }
3087
+ function goStringLiteral(node) {
3088
+ if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
3089
+ return node.text.slice(1, -1);
3090
+ }
3091
+ return null;
3092
+ }
3093
+ function ginRoutesFromSource(source, parser) {
3094
+ return goRouterRoutesFromSource(source, parser, "gin");
3095
+ }
3096
+ function echoRoutesFromSource(source, parser) {
3097
+ return goRouterRoutesFromSource(source, parser, "echo");
3098
+ }
3099
+ function fiberRoutesFromSource(source, parser) {
3100
+ return goRouterRoutesFromSource(source, parser, "fiber");
3101
+ }
3102
+ function chiRoutesFromSource(source, parser) {
3103
+ const tree = parseSource2(parser, source);
3104
+ const out = [];
3105
+ chiWalk(tree.rootNode, "", out);
3106
+ return out;
3107
+ }
3108
+ function stripChiRegex(path68) {
3109
+ return path68.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3110
+ }
3111
+ function chiWalk(node, prefix, out) {
3112
+ for (let i = 0; i < node.namedChildCount; i++) {
3113
+ const child = node.namedChild(i);
3114
+ if (child) chiHandle(child, prefix, out);
3115
+ }
3116
+ }
3117
+ function chiHandle(node, prefix, out) {
3118
+ if (node.type === "call_expression") {
3119
+ const fn = node.childForFieldName("function");
3120
+ if (fn?.type === "selector_expression") {
3121
+ const field = fn.childForFieldName("field")?.text;
3122
+ const args = node.childForFieldName("arguments");
3123
+ if (field === "Route") {
3124
+ const leaf = goStringLiteral(args?.namedChild(0));
3125
+ const closure = args?.namedChild(1);
3126
+ if (leaf !== null && closure?.type === "func_literal") {
3127
+ const body = closure.childForFieldName("body");
3128
+ if (body) chiWalk(body, prefix + leaf, out);
3129
+ }
3130
+ return;
3131
+ }
3132
+ if (field === "Group") {
3133
+ const closure = args?.namedChild(0);
3134
+ if (closure?.type === "func_literal") {
3135
+ const body = closure.childForFieldName("body");
3136
+ if (body) chiWalk(body, prefix, out);
3137
+ }
3138
+ return;
3139
+ }
3140
+ if (field === "Mount") {
3141
+ return;
3142
+ }
3143
+ if (field && ROUTER_METHODS.has(field.toLowerCase())) {
3144
+ const leaf = goStringLiteral(args?.namedChild(0));
3145
+ if (leaf !== null) {
3146
+ out.push({
3147
+ method: field.toUpperCase(),
3148
+ pathTemplate: canonicalizeTemplate(stripChiRegex(prefix + leaf)),
3149
+ line: node.startPosition.row + 1,
3150
+ framework: "chi"
3151
+ });
3152
+ }
3153
+ return;
3154
+ }
3155
+ }
3156
+ }
3157
+ chiWalk(node, prefix, out);
3158
+ }
3159
+ function netHttpRoutesFromSource(source, parser) {
3160
+ const tree = parseSource2(parser, source);
3161
+ if (!goImportsNetHttp(tree.rootNode)) return [];
3162
+ const out = [];
3163
+ walk(tree.rootNode, (node) => {
3164
+ if (node.type !== "call_expression") return;
3165
+ const fn = node.childForFieldName("function");
3166
+ if (fn?.type !== "selector_expression") return;
3167
+ const field = fn.childForFieldName("field")?.text;
3168
+ if (field !== "HandleFunc" && field !== "Handle") return;
3169
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
3170
+ if (leaf === null) return;
3171
+ const sp = leaf.indexOf(" ");
3172
+ if (sp < 0) return;
3173
+ const method = leaf.slice(0, sp);
3174
+ const rest = leaf.slice(sp + 1);
3175
+ if (!NET_HTTP_METHODS.has(method)) return;
3176
+ if (!rest.startsWith("/")) return;
3177
+ out.push({
3178
+ method,
3179
+ pathTemplate: canonicalizeTemplate(rest),
3180
+ line: node.startPosition.row + 1,
3181
+ framework: "net/http"
3182
+ });
3183
+ });
3184
+ return out;
3185
+ }
3186
+ function goImportsNetHttp(root) {
3187
+ let found = false;
3188
+ walk(root, (node) => {
3189
+ if (found || node.type !== "import_spec") return;
3190
+ for (let i = 0; i < node.namedChildCount; i++) {
3191
+ const child = node.namedChild(i);
3192
+ if (goStringLiteral(child) === "net/http") found = true;
3193
+ }
3194
+ });
3195
+ return found;
3196
+ }
3082
3197
  var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
3083
3198
  var NESTJS_METHODS = /* @__PURE__ */ new Map([
3084
3199
  ["Get", "GET"],
@@ -3663,9 +3778,9 @@ function rubyRocketRoute(args) {
3663
3778
  if (!pair || pair.type !== "pair") continue;
3664
3779
  const k = pair.childForFieldName("key");
3665
3780
  if (k?.type !== "string") continue;
3666
- const path67 = rubyLiteral(k);
3667
- if (path67 === null) continue;
3668
- return { path: path67, target: rubyLiteral(pair.childForFieldName("value")) };
3781
+ const path68 = rubyLiteral(k);
3782
+ if (path68 === null) continue;
3783
+ return { path: path68, target: rubyLiteral(pair.childForFieldName("value")) };
3669
3784
  }
3670
3785
  return null;
3671
3786
  }
@@ -4342,9 +4457,13 @@ async function addRoutes(graph, services) {
4342
4457
  const hasFlask = deps["flask"] !== void 0;
4343
4458
  const hasDjango = deps["django"] !== void 0;
4344
4459
  const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
4460
+ const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
4461
+ const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
4462
+ const hasChi = deps["github.com/go-chi/chi/v5"] !== void 0 || deps["github.com/go-chi/chi"] !== void 0;
4463
+ const isGoService = service.node.language === "go";
4345
4464
  const hasRails = deps["rails"] !== void 0;
4346
4465
  const hasLaravel = deps["laravel/framework"] !== void 0;
4347
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
4466
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
4348
4467
  continue;
4349
4468
  const files = await loadSourceFiles(service.dir);
4350
4469
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4368,7 +4487,12 @@ async function addRoutes(graph, services) {
4368
4487
  } else if (isRb) {
4369
4488
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4370
4489
  } else if (isGo) {
4371
- routes = hasGin ? ginRoutesFromSource(file.content, goParser) : [];
4490
+ if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4491
+ else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
4492
+ else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
4493
+ else if (hasChi) routes = chiRoutesFromSource(file.content, goParser);
4494
+ else routes = [];
4495
+ routes = routes.concat(netHttpRoutesFromSource(file.content, goParser));
4372
4496
  } else if (isPy) {
4373
4497
  routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
4374
4498
  if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
@@ -5978,6 +6102,13 @@ function parseGoMod(source) {
5978
6102
  }
5979
6103
  return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
5980
6104
  }
6105
+ function goFramework(deps) {
6106
+ if (deps["github.com/gin-gonic/gin"]) return "gin";
6107
+ if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
6108
+ if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
6109
+ if (deps["github.com/go-chi/chi/v5"] || deps["github.com/go-chi/chi"]) return "chi";
6110
+ return void 0;
6111
+ }
5981
6112
  async function discoverGoService(scanPath, dir) {
5982
6113
  let raw;
5983
6114
  try {
@@ -5989,6 +6120,7 @@ async function discoverGoService(scanPath, dir) {
5989
6120
  if (!mod) return null;
5990
6121
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
5991
6122
  const pkg = { name, dependencies: mod.dependencies };
6123
+ const framework = goFramework(mod.dependencies);
5992
6124
  const node = {
5993
6125
  id: (0, import_types9.serviceId)(name),
5994
6126
  type: import_types9.NodeType.ServiceNode,
@@ -5996,7 +6128,7 @@ async function discoverGoService(scanPath, dir) {
5996
6128
  language: "go",
5997
6129
  dependencies: mod.dependencies,
5998
6130
  repoPath: import_node_path10.default.relative(scanPath, dir),
5999
- ...mod.dependencies["github.com/gin-gonic/gin"] ? { framework: "gin" } : {}
6131
+ ...framework ? { framework } : {}
6000
6132
  };
6001
6133
  return { pkg, dir, node };
6002
6134
  }
@@ -6896,7 +7028,7 @@ async function addSymbolEdges(graph, services) {
6896
7028
  return best;
6897
7029
  };
6898
7030
  const requests = [];
6899
- const walk8 = (node) => {
7031
+ const walk9 = (node) => {
6900
7032
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
6901
7033
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
6902
7034
  if (self && self.kind === "class") {
@@ -6942,10 +7074,10 @@ async function addSymbolEdges(graph, services) {
6942
7074
  }
6943
7075
  for (let i = 0; i < node.namedChildCount; i++) {
6944
7076
  const child = node.namedChild(i);
6945
- if (child) walk8(child);
7077
+ if (child) walk9(child);
6946
7078
  }
6947
7079
  };
6948
- walk8(root);
7080
+ walk9(root);
6949
7081
  for (const req of requests) {
6950
7082
  const targetSid = resolveTarget(req.targetName, req.wantKind);
6951
7083
  if (!targetSid) continue;
@@ -7958,20 +8090,20 @@ var import_node_path28 = __toESM(require("path"), 1);
7958
8090
  var import_types18 = require("@neat.is/types");
7959
8091
  async function walkConfigFiles(dir) {
7960
8092
  const out = [];
7961
- async function walk8(current) {
8093
+ async function walk9(current) {
7962
8094
  const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true });
7963
8095
  for (const entry of entries) {
7964
8096
  const full = import_node_path28.default.join(current, entry.name);
7965
8097
  if (entry.isDirectory()) {
7966
8098
  if (IGNORED_DIRS.has(entry.name)) continue;
7967
8099
  if (await isPythonVenvDir(full)) continue;
7968
- await walk8(full);
8100
+ await walk9(full);
7969
8101
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
7970
8102
  out.push(full);
7971
8103
  }
7972
8104
  }
7973
8105
  }
7974
- await walk8(dir);
8106
+ await walk9(dir);
7975
8107
  return out;
7976
8108
  }
7977
8109
  async function addConfigNodes(graph, services, scanPath) {
@@ -8061,20 +8193,20 @@ function grpcMethodsFromProto(content, fqPackage) {
8061
8193
  }
8062
8194
  async function walkProtoFiles(dir) {
8063
8195
  const out = [];
8064
- async function walk8(current) {
8196
+ async function walk9(current) {
8065
8197
  const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8066
8198
  for (const entry of entries) {
8067
8199
  const full = import_node_path29.default.join(current, entry.name);
8068
8200
  if (entry.isDirectory()) {
8069
8201
  if (IGNORED_DIRS.has(entry.name)) continue;
8070
8202
  if (await isPythonVenvDir(full)) continue;
8071
- await walk8(full);
8203
+ await walk9(full);
8072
8204
  } else if (entry.isFile() && import_node_path29.default.extname(entry.name) === PROTO_EXTENSION) {
8073
8205
  out.push(full);
8074
8206
  }
8075
8207
  }
8076
8208
  }
8077
- await walk8(dir);
8209
+ await walk9(dir);
8078
8210
  return out;
8079
8211
  }
8080
8212
  async function addGrpcMethods(graph, services) {
@@ -8142,7 +8274,7 @@ async function addGrpcMethods(graph, services) {
8142
8274
 
8143
8275
  // src/extract/calls/index.ts
8144
8276
  init_cjs_shims();
8145
- var import_types36 = require("@neat.is/types");
8277
+ var import_types37 = require("@neat.is/types");
8146
8278
 
8147
8279
  // src/extract/calls/http.ts
8148
8280
  init_cjs_shims();
@@ -8896,7 +9028,7 @@ function isFirestoreClientFactory(node) {
8896
9028
  }
8897
9029
  function firestoreClientVars(root) {
8898
9030
  const vars = /* @__PURE__ */ new Set();
8899
- const walk8 = (node) => {
9031
+ const walk9 = (node) => {
8900
9032
  if (node.type === "variable_declarator") {
8901
9033
  const name = node.childForFieldName("name");
8902
9034
  let value = node.childForFieldName("value");
@@ -8905,9 +9037,9 @@ function firestoreClientVars(root) {
8905
9037
  vars.add(name.text);
8906
9038
  }
8907
9039
  }
8908
- for (const c of namedChildren(node)) walk8(c);
9040
+ for (const c of namedChildren(node)) walk9(c);
8909
9041
  };
8910
- walk8(root);
9042
+ walk9(root);
8911
9043
  return vars;
8912
9044
  }
8913
9045
  function isClientExpr(node, clientVars) {
@@ -9062,7 +9194,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9062
9194
  }
9063
9195
  s.add(field);
9064
9196
  };
9065
- const walk8 = (node) => {
9197
+ const walk9 = (node) => {
9066
9198
  if (node.type === "call_expression") {
9067
9199
  const fn = node.childForFieldName("function");
9068
9200
  const line = node.startPosition.row + 1;
@@ -9102,9 +9234,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9102
9234
  }
9103
9235
  }
9104
9236
  }
9105
- for (const c of namedChildren(node)) walk8(c);
9237
+ for (const c of namedChildren(node)) walk9(c);
9106
9238
  };
9107
- walk8(tree.rootNode);
9239
+ walk9(tree.rootNode);
9108
9240
  const out = [];
9109
9241
  for (const [collPath, line] of collLine) {
9110
9242
  const byField = writes.get(collPath);
@@ -9899,7 +10031,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9899
10031
  const tree = parseSource3(parserForExt2(import_node_path41.default.extname(file.path)), file.content);
9900
10032
  const out = [];
9901
10033
  const seen = /* @__PURE__ */ new Set();
9902
- const walk8 = (node) => {
10034
+ const walk9 = (node) => {
9903
10035
  if (node.type === "call_expression") {
9904
10036
  const fn = node.childForFieldName("function");
9905
10037
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9927,9 +10059,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9927
10059
  }
9928
10060
  }
9929
10061
  }
9930
- for (const c of namedChildren4(node)) walk8(c);
10062
+ for (const c of namedChildren4(node)) walk9(c);
9931
10063
  };
9932
- walk8(tree.rootNode);
10064
+ walk9(tree.rootNode);
9933
10065
  return out;
9934
10066
  }
9935
10067
  function enclosingVarName(call) {
@@ -9951,7 +10083,7 @@ function enclosingVarName(call) {
9951
10083
  function collectDrizzleTables(root) {
9952
10084
  const tables = [];
9953
10085
  const varToTable = /* @__PURE__ */ new Map();
9954
- const walk8 = (node) => {
10086
+ const walk9 = (node) => {
9955
10087
  if (node.type === "call_expression") {
9956
10088
  const fn = node.childForFieldName("function");
9957
10089
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9966,9 +10098,9 @@ function collectDrizzleTables(root) {
9966
10098
  }
9967
10099
  }
9968
10100
  }
9969
- for (const c of namedChildren4(node)) walk8(c);
10101
+ for (const c of namedChildren4(node)) walk9(c);
9970
10102
  };
9971
- walk8(root);
10103
+ walk9(root);
9972
10104
  return { tables, varToTable };
9973
10105
  }
9974
10106
  function referencesTargetVar(call) {
@@ -9991,7 +10123,7 @@ function drizzleForeignKeys(file, serviceDir) {
9991
10123
  const seen = /* @__PURE__ */ new Set();
9992
10124
  for (const table of tables) {
9993
10125
  if (!table.object) continue;
9994
- const walk8 = (node) => {
10126
+ const walk9 = (node) => {
9995
10127
  if (node.type === "call_expression") {
9996
10128
  const targetVar = referencesTargetVar(node);
9997
10129
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -10012,9 +10144,9 @@ function drizzleForeignKeys(file, serviceDir) {
10012
10144
  }
10013
10145
  }
10014
10146
  }
10015
- for (const c of namedChildren4(node)) walk8(c);
10147
+ for (const c of namedChildren4(node)) walk9(c);
10016
10148
  };
10017
- walk8(table.object);
10149
+ walk9(table.object);
10018
10150
  }
10019
10151
  return out;
10020
10152
  }
@@ -11094,15 +11226,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
11094
11226
  return out;
11095
11227
  }
11096
11228
 
11229
+ // src/extract/calls/gorm.ts
11230
+ init_cjs_shims();
11231
+ var import_node_path48 = __toESM(require("path"), 1);
11232
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
11233
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
11234
+ var import_types36 = require("@neat.is/types");
11235
+ var GORM_IMPORT_RE = /gorm\.io\/gorm/;
11236
+ var PARSE_CHUNK11 = 16384;
11237
+ function makeGoParser3() {
11238
+ const p = new import_tree_sitter15.default();
11239
+ p.setLanguage(import_tree_sitter_go4.default);
11240
+ return p;
11241
+ }
11242
+ function parseSource10(parser, source) {
11243
+ return parser.parse(
11244
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
11245
+ );
11246
+ }
11247
+ function walk8(node, visit) {
11248
+ visit(node);
11249
+ for (let i = 0; i < node.namedChildCount; i++) {
11250
+ const c = node.namedChild(i);
11251
+ if (c) walk8(c, visit);
11252
+ }
11253
+ }
11254
+ var COMMON_INITIALISMS = [
11255
+ "ASCII",
11256
+ "HTTPS",
11257
+ "UTF8",
11258
+ "XSRF",
11259
+ "HTML",
11260
+ "HTTP",
11261
+ "JSON",
11262
+ "UUID",
11263
+ "XMPP",
11264
+ "ACL",
11265
+ "API",
11266
+ "CPU",
11267
+ "CSS",
11268
+ "DNS",
11269
+ "EOF",
11270
+ "GUID",
11271
+ "LHS",
11272
+ "QPS",
11273
+ "RAM",
11274
+ "RHS",
11275
+ "RPC",
11276
+ "SLA",
11277
+ "SQL",
11278
+ "SSH",
11279
+ "TCP",
11280
+ "TLS",
11281
+ "TTL",
11282
+ "UDP",
11283
+ "UID",
11284
+ "URI",
11285
+ "URL",
11286
+ "UID",
11287
+ "XSS",
11288
+ "ID",
11289
+ "IP",
11290
+ "UI",
11291
+ "VM",
11292
+ "XML"
11293
+ ].sort((a, b) => b.length - a.length);
11294
+ function titleCase(word) {
11295
+ return word.charAt(0) + word.slice(1).toLowerCase();
11296
+ }
11297
+ function replaceInitialisms(name) {
11298
+ let out = "";
11299
+ let i = 0;
11300
+ while (i < name.length) {
11301
+ let matched = false;
11302
+ for (const init of COMMON_INITIALISMS) {
11303
+ if (name.startsWith(init, i)) {
11304
+ out += titleCase(init);
11305
+ i += init.length;
11306
+ matched = true;
11307
+ break;
11308
+ }
11309
+ }
11310
+ if (!matched) {
11311
+ out += name[i];
11312
+ i++;
11313
+ }
11314
+ }
11315
+ return out;
11316
+ }
11317
+ var isUpper = (c) => c >= "A" && c <= "Z";
11318
+ var isDigit = (c) => c >= "0" && c <= "9";
11319
+ function toDBName(name) {
11320
+ if (name === "") return "";
11321
+ const value = replaceInitialisms(name);
11322
+ if (value.length === 1) return value.toLowerCase();
11323
+ let buf = "";
11324
+ let lastCase = false;
11325
+ let curCase = isUpper(value[0]);
11326
+ for (let i = 0; i < value.length - 1; i++) {
11327
+ const v = value[i];
11328
+ const nextCase = isUpper(value[i + 1]);
11329
+ const nextNumber = isDigit(value[i + 1]);
11330
+ if (curCase) {
11331
+ if (lastCase && (nextCase || nextNumber)) {
11332
+ buf += v.toLowerCase();
11333
+ } else {
11334
+ if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
11335
+ buf += v.toLowerCase();
11336
+ }
11337
+ } else {
11338
+ buf += v;
11339
+ }
11340
+ lastCase = curCase;
11341
+ curCase = nextCase;
11342
+ }
11343
+ const last = value[value.length - 1];
11344
+ if (curCase) {
11345
+ if (!lastCase && value.length > 1) buf += "_";
11346
+ buf += last.toLowerCase();
11347
+ } else {
11348
+ buf += last;
11349
+ }
11350
+ return buf;
11351
+ }
11352
+ var UNCOUNTABLE = /* @__PURE__ */ new Set([
11353
+ "equipment",
11354
+ "information",
11355
+ "rice",
11356
+ "money",
11357
+ "species",
11358
+ "series",
11359
+ "fish",
11360
+ "sheep",
11361
+ "jeans",
11362
+ "police"
11363
+ ]);
11364
+ var IRREGULAR = [
11365
+ ["person", "people"],
11366
+ ["man", "men"],
11367
+ ["child", "children"],
11368
+ ["sex", "sexes"],
11369
+ ["move", "moves"]
11370
+ ];
11371
+ var PLURAL_RULES = [
11372
+ [/(quiz)$/i, "$1zes"],
11373
+ [/^(ox)$/i, "$1en"],
11374
+ [/([ml])ouse$/i, "$1ice"],
11375
+ [/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
11376
+ [/(x|ch|ss|sh)$/i, "$1es"],
11377
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
11378
+ [/(hive)$/i, "$1s"],
11379
+ [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
11380
+ [/sis$/i, "ses"],
11381
+ [/([ti])um$/i, "$1a"],
11382
+ [/([ti])a$/i, "$1a"],
11383
+ [/(buffal|tomat)o$/i, "$1oes"],
11384
+ [/(bu)s$/i, "$1ses"],
11385
+ [/(alias|status)$/i, "$1es"],
11386
+ [/(octop|vir)i$/i, "$1i"],
11387
+ [/(octop|vir)us$/i, "$1i"],
11388
+ [/(ax|test)is$/i, "$1es"],
11389
+ [/s$/i, "s"]
11390
+ ];
11391
+ function pluralize3(word) {
11392
+ if (word === "") return word;
11393
+ const lower = word.toLowerCase();
11394
+ for (const u of UNCOUNTABLE) {
11395
+ if (lower === u || lower.endsWith("_" + u)) return word;
11396
+ }
11397
+ for (const [sing, plur] of IRREGULAR) {
11398
+ const re = new RegExp(sing + "$", "i");
11399
+ if (re.test(word)) return word.replace(re, plur);
11400
+ }
11401
+ for (const [re, rep] of PLURAL_RULES) {
11402
+ if (re.test(word)) return word.replace(re, rep);
11403
+ }
11404
+ return word + "s";
11405
+ }
11406
+ function deriveTableName(structName) {
11407
+ return pluralize3(toDBName(structName));
11408
+ }
11409
+ function stringLiteralValue(node) {
11410
+ if (!node) return null;
11411
+ if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
11412
+ const t = node.text;
11413
+ return t.length >= 2 ? t.slice(1, -1) : "";
11414
+ }
11415
+ return null;
11416
+ }
11417
+ function parseGormTag(tagNode) {
11418
+ const tag = {};
11419
+ if (!tagNode) return tag;
11420
+ let inner = tagNode.text;
11421
+ if (inner.length >= 2) inner = inner.slice(1, -1);
11422
+ if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
11423
+ const m = inner.match(/gorm:"([^"]*)"/);
11424
+ if (!m) return tag;
11425
+ for (const part of m[1].split(";")) {
11426
+ if (part === "") continue;
11427
+ const idx = part.indexOf(":");
11428
+ const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
11429
+ const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
11430
+ if (key === "-") tag.skip = true;
11431
+ else if (key === "column") tag.column = value;
11432
+ else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
11433
+ else if (key === "foreignkey") tag.foreignKey = value;
11434
+ else if (key === "many2many") tag.many2many = value;
11435
+ else if (key === "embedded") tag.embedded = true;
11436
+ else if (key === "embeddedprefix") tag.embeddedPrefix = value;
11437
+ }
11438
+ return tag;
11439
+ }
11440
+ function unwrapType(typeNode) {
11441
+ let isSlice = false;
11442
+ let isPointer = false;
11443
+ let n = typeNode;
11444
+ while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
11445
+ if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
11446
+ if (n.type === "pointer_type") isPointer = true;
11447
+ n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
11448
+ }
11449
+ if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11450
+ if (n.type === "type_identifier") {
11451
+ return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
11452
+ }
11453
+ if (n.type === "qualified_type") {
11454
+ const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
11455
+ const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
11456
+ return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
11457
+ }
11458
+ return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11459
+ }
11460
+ function readField(fieldDecl) {
11461
+ const names = [];
11462
+ let tagNode = null;
11463
+ for (let i = 0; i < fieldDecl.namedChildCount; i++) {
11464
+ const c = fieldDecl.namedChild(i);
11465
+ if (!c) continue;
11466
+ if (c.type === "field_identifier") names.push(c.text);
11467
+ else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
11468
+ }
11469
+ const typeNode = fieldDecl.childForFieldName("type");
11470
+ const t = unwrapType(typeNode);
11471
+ return {
11472
+ names,
11473
+ typeName: t.name,
11474
+ qualifier: t.qualifier,
11475
+ isSlice: t.isSlice,
11476
+ isPointer: t.isPointer,
11477
+ isQualified: t.isQualified,
11478
+ tag: parseGormTag(tagNode),
11479
+ line: fieldDecl.startPosition.row + 1
11480
+ };
11481
+ }
11482
+ function collectStructs(tree) {
11483
+ const structs = /* @__PURE__ */ new Map();
11484
+ walk8(tree.rootNode, (node) => {
11485
+ if (node.type !== "type_spec") return;
11486
+ const nameNode = node.childForFieldName("name");
11487
+ const typeNode = node.childForFieldName("type");
11488
+ if (!nameNode || typeNode?.type !== "struct_type") return;
11489
+ const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
11490
+ const fields = [];
11491
+ if (list && list.type === "field_declaration_list") {
11492
+ for (let i = 0; i < list.namedChildCount; i++) {
11493
+ const fd = list.namedChild(i);
11494
+ if (fd?.type === "field_declaration") fields.push(readField(fd));
11495
+ }
11496
+ }
11497
+ structs.set(nameNode.text, {
11498
+ name: nameNode.text,
11499
+ fields,
11500
+ line: node.startPosition.row + 1
11501
+ });
11502
+ });
11503
+ return structs;
11504
+ }
11505
+ var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
11506
+ "AutoMigrate",
11507
+ "Model",
11508
+ "Create",
11509
+ "Find",
11510
+ "First",
11511
+ "Take",
11512
+ "Last",
11513
+ "Save",
11514
+ "Delete",
11515
+ "Where",
11516
+ "FirstOrCreate",
11517
+ "FirstOrInit"
11518
+ ]);
11519
+ function compositeStructName(arg) {
11520
+ let n = arg;
11521
+ if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
11522
+ if (!n || n.type !== "composite_literal") return null;
11523
+ const typeNode = n.childForFieldName("type");
11524
+ if (!typeNode) return null;
11525
+ if (typeNode.type === "type_identifier") return typeNode.text;
11526
+ if (typeNode.type === "qualified_type") {
11527
+ return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
11528
+ }
11529
+ return null;
11530
+ }
11531
+ function collectCallModels(tree) {
11532
+ const models = /* @__PURE__ */ new Set();
11533
+ walk8(tree.rootNode, (node) => {
11534
+ if (node.type !== "call_expression") return;
11535
+ const fn = node.childForFieldName("function");
11536
+ if (fn?.type !== "selector_expression") return;
11537
+ const method = fn.childForFieldName("field")?.text;
11538
+ if (!method || !GORM_MODEL_METHODS.has(method)) return;
11539
+ const args = node.childForFieldName("arguments");
11540
+ if (!args) return;
11541
+ for (let i = 0; i < args.namedChildCount; i++) {
11542
+ const arg = args.namedChild(i);
11543
+ if (!arg) continue;
11544
+ const name = compositeStructName(arg);
11545
+ if (name) models.add(name);
11546
+ }
11547
+ });
11548
+ return models;
11549
+ }
11550
+ function collectTableNameOverrides(tree) {
11551
+ const overrides = /* @__PURE__ */ new Map();
11552
+ const declarers = /* @__PURE__ */ new Set();
11553
+ walk8(tree.rootNode, (node) => {
11554
+ if (node.type !== "method_declaration") return;
11555
+ if (node.childForFieldName("name")?.text !== "TableName") return;
11556
+ const receiver = node.childForFieldName("receiver");
11557
+ if (!receiver) return;
11558
+ let recvType = null;
11559
+ for (let i = 0; i < receiver.namedChildCount; i++) {
11560
+ const pd = receiver.namedChild(i);
11561
+ if (pd?.type !== "parameter_declaration") continue;
11562
+ const t = unwrapType(pd.childForFieldName("type"));
11563
+ recvType = t.name;
11564
+ }
11565
+ if (!recvType) return;
11566
+ declarers.add(recvType);
11567
+ const body = node.childForFieldName("body");
11568
+ if (!body) return;
11569
+ let literal = null;
11570
+ walk8(body, (n) => {
11571
+ if (literal !== null) return;
11572
+ if (n.type !== "return_statement") return;
11573
+ const exprList = n.namedChild(0);
11574
+ const first = exprList?.namedChild(0) ?? exprList;
11575
+ const v = stringLiteralValue(first);
11576
+ if (v) literal = v;
11577
+ });
11578
+ if (literal !== null) overrides.set(recvType, literal);
11579
+ });
11580
+ return { overrides, declarers };
11581
+ }
11582
+ function isRelationField(field, structs) {
11583
+ if (field.names.length === 0) return false;
11584
+ if (field.isQualified) return false;
11585
+ if (!field.typeName) return false;
11586
+ return structs.has(field.typeName);
11587
+ }
11588
+ function isGormModelEmbed(field) {
11589
+ return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
11590
+ }
11591
+ function analyze(tree) {
11592
+ const structs = collectStructs(tree);
11593
+ const { overrides, declarers } = collectTableNameOverrides(tree);
11594
+ const callModels = collectCallModels(tree);
11595
+ const models = /* @__PURE__ */ new Set();
11596
+ for (const [name, info] of structs) {
11597
+ if (info.fields.some(isGormModelEmbed)) models.add(name);
11598
+ }
11599
+ for (const name of callModels) if (structs.has(name)) models.add(name);
11600
+ for (const name of declarers) if (structs.has(name)) models.add(name);
11601
+ let grew = true;
11602
+ while (grew) {
11603
+ grew = false;
11604
+ for (const name of Array.from(models)) {
11605
+ const info = structs.get(name);
11606
+ if (!info) continue;
11607
+ for (const field of info.fields) {
11608
+ if (!isRelationField(field, structs)) continue;
11609
+ const target = field.typeName;
11610
+ if (!models.has(target) && structs.has(target)) {
11611
+ models.add(target);
11612
+ grew = true;
11613
+ }
11614
+ }
11615
+ }
11616
+ }
11617
+ const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
11618
+ return { structs, models, tableFor };
11619
+ }
11620
+ function collectColumns(struct, structs, seen, prefix, out, emitted) {
11621
+ if (seen.has(struct.name)) return;
11622
+ seen.add(struct.name);
11623
+ const add = (col) => {
11624
+ const full = prefix + col;
11625
+ if (!emitted.has(full)) {
11626
+ emitted.add(full);
11627
+ out.push(full);
11628
+ }
11629
+ };
11630
+ for (const field of struct.fields) {
11631
+ if (field.tag.skip) continue;
11632
+ if (field.names.length === 0) {
11633
+ if (isGormModelEmbed(field)) {
11634
+ add("id");
11635
+ add("created_at");
11636
+ add("updated_at");
11637
+ add("deleted_at");
11638
+ } else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
11639
+ collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
11640
+ }
11641
+ continue;
11642
+ }
11643
+ if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
11644
+ collectColumns(
11645
+ structs.get(field.typeName),
11646
+ structs,
11647
+ seen,
11648
+ prefix + (field.tag.embeddedPrefix ?? ""),
11649
+ out,
11650
+ emitted
11651
+ );
11652
+ continue;
11653
+ }
11654
+ if (isRelationField(field, structs)) continue;
11655
+ if (field.names.length === 1 && field.tag.column) {
11656
+ add(field.tag.column);
11657
+ } else {
11658
+ for (const n of field.names) add(toDBName(n));
11659
+ }
11660
+ }
11661
+ seen.delete(struct.name);
11662
+ }
11663
+ function gormEndpointsFromFile(file, serviceDir) {
11664
+ if (import_node_path48.default.extname(file.path) !== ".go") return [];
11665
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11666
+ const tree = parseSource10(makeGoParser3(), file.content);
11667
+ const { structs, models, tableFor } = analyze(tree);
11668
+ const out = [];
11669
+ const seenTables = /* @__PURE__ */ new Set();
11670
+ for (const name of models) {
11671
+ const struct = structs.get(name);
11672
+ if (!struct) continue;
11673
+ const table = tableFor(name);
11674
+ if (seenTables.has(table)) continue;
11675
+ seenTables.add(table);
11676
+ const columns = [];
11677
+ collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
11678
+ out.push({
11679
+ infraId: (0, import_types36.infraId)("sql-table", table),
11680
+ name: table,
11681
+ kind: "sql-table",
11682
+ edgeType: "CALLS",
11683
+ confidenceKind: "structural",
11684
+ ...columns.length > 0 ? { columns } : {},
11685
+ evidence: {
11686
+ file: toPosix(import_node_path48.default.relative(serviceDir, file.path)),
11687
+ line: struct.line,
11688
+ snippet: snippet(file.content, struct.line)
11689
+ }
11690
+ });
11691
+ }
11692
+ return out;
11693
+ }
11694
+ function gormForeignKeys(file, serviceDir) {
11695
+ if (import_node_path48.default.extname(file.path) !== ".go") return [];
11696
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11697
+ const tree = parseSource10(makeGoParser3(), file.content);
11698
+ const { structs, models, tableFor } = analyze(tree);
11699
+ const out = [];
11700
+ const seen = /* @__PURE__ */ new Set();
11701
+ const emit = (childTable, parentTable, line) => {
11702
+ if (!childTable || !parentTable || childTable === parentTable) return;
11703
+ const key = `${childTable}->${parentTable}`;
11704
+ if (seen.has(key)) return;
11705
+ seen.add(key);
11706
+ out.push({
11707
+ childTable,
11708
+ parentTable,
11709
+ evidence: {
11710
+ file: toPosix(import_node_path48.default.relative(serviceDir, file.path)),
11711
+ line,
11712
+ snippet: snippet(file.content, line)
11713
+ }
11714
+ });
11715
+ };
11716
+ for (const name of models) {
11717
+ const struct = structs.get(name);
11718
+ if (!struct) continue;
11719
+ const thisTable = tableFor(name);
11720
+ const scalarNames = new Set(
11721
+ struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
11722
+ );
11723
+ for (const field of struct.fields) {
11724
+ if (field.tag.skip) continue;
11725
+ if (!isRelationField(field, structs)) continue;
11726
+ const relTable = tableFor(field.typeName);
11727
+ if (field.tag.many2many) {
11728
+ emit(field.tag.many2many, thisTable, field.line);
11729
+ emit(field.tag.many2many, relTable, field.line);
11730
+ continue;
11731
+ }
11732
+ if (field.isSlice) {
11733
+ emit(relTable, thisTable, field.line);
11734
+ continue;
11735
+ }
11736
+ const convFk = field.names[0] + "ID";
11737
+ const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
11738
+ if (belongsTo) emit(thisTable, relTable, field.line);
11739
+ else emit(relTable, thisTable, field.line);
11740
+ }
11741
+ }
11742
+ return out;
11743
+ }
11744
+
11097
11745
  // src/extract/calls/index.ts
11098
11746
  function edgeTypeFromEndpoint(ep) {
11099
11747
  switch (ep.edgeType) {
11100
11748
  case "PUBLISHES_TO":
11101
- return import_types36.EdgeType.PUBLISHES_TO;
11749
+ return import_types37.EdgeType.PUBLISHES_TO;
11102
11750
  case "CONSUMES_FROM":
11103
- return import_types36.EdgeType.CONSUMES_FROM;
11751
+ return import_types37.EdgeType.CONSUMES_FROM;
11104
11752
  default:
11105
- return import_types36.EdgeType.CALLS;
11753
+ return import_types37.EdgeType.CALLS;
11106
11754
  }
11107
11755
  }
11108
11756
  function isAwsKind(kind) {
@@ -11135,6 +11783,11 @@ async function addExternalEndpointEdges(graph, services) {
11135
11783
  } catch (err) {
11136
11784
  recordExtractionError("go SQL call extraction", file.path, err);
11137
11785
  }
11786
+ try {
11787
+ endpoints.push(...gormEndpointsFromFile(file, service.dir));
11788
+ } catch (err) {
11789
+ recordExtractionError("gorm data-axis extraction", file.path, err);
11790
+ }
11138
11791
  try {
11139
11792
  endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
11140
11793
  endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
@@ -11157,7 +11810,7 @@ async function addExternalEndpointEdges(graph, services) {
11157
11810
  if (!graph.hasNode(ep.infraId)) {
11158
11811
  const node = {
11159
11812
  id: ep.infraId,
11160
- type: import_types36.NodeType.InfraNode,
11813
+ type: import_types37.NodeType.InfraNode,
11161
11814
  name: ep.name,
11162
11815
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
11163
11816
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -11170,21 +11823,21 @@ async function addExternalEndpointEdges(graph, services) {
11170
11823
  }
11171
11824
  if (ep.columns && ep.columns.length > 0) {
11172
11825
  const node = graph.getNodeAttributes(ep.infraId);
11173
- if (node.type === import_types36.NodeType.InfraNode) {
11826
+ if (node.type === import_types37.NodeType.InfraNode) {
11174
11827
  graph.replaceNodeAttributes(ep.infraId, {
11175
11828
  ...node,
11176
11829
  columns: foldColumns(
11177
11830
  node.columns,
11178
11831
  ep.columns,
11179
- import_types36.Provenance.EXTRACTED,
11180
- (0, import_types36.confidenceForExtracted)(ep.confidenceKind)
11832
+ import_types37.Provenance.EXTRACTED,
11833
+ (0, import_types37.confidenceForExtracted)(ep.confidenceKind)
11181
11834
  )
11182
11835
  });
11183
11836
  }
11184
11837
  }
11185
11838
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
11186
11839
  const node = graph.getNodeAttributes(ep.infraId);
11187
- if (node.type === import_types36.NodeType.InfraNode) {
11840
+ if (node.type === import_types37.NodeType.InfraNode) {
11188
11841
  graph.replaceNodeAttributes(ep.infraId, {
11189
11842
  ...node,
11190
11843
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -11192,7 +11845,7 @@ async function addExternalEndpointEdges(graph, services) {
11192
11845
  }
11193
11846
  }
11194
11847
  const edgeType = edgeTypeFromEndpoint(ep);
11195
- const confidence = (0, import_types36.confidenceForExtracted)(ep.confidenceKind);
11848
+ const confidence = (0, import_types37.confidenceForExtracted)(ep.confidenceKind);
11196
11849
  const relFile = toPosix(ep.evidence.file);
11197
11850
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
11198
11851
  graph,
@@ -11202,7 +11855,7 @@ async function addExternalEndpointEdges(graph, services) {
11202
11855
  );
11203
11856
  nodesAdded += n;
11204
11857
  edgesAdded += e;
11205
- if (!(0, import_types36.passesExtractedFloor)(confidence)) {
11858
+ if (!(0, import_types37.passesExtractedFloor)(confidence)) {
11206
11859
  noteExtractedDropped({
11207
11860
  source: fileNodeId,
11208
11861
  target: ep.infraId,
@@ -11222,7 +11875,7 @@ async function addExternalEndpointEdges(graph, services) {
11222
11875
  source: fileNodeId,
11223
11876
  target: ep.infraId,
11224
11877
  type: edgeType,
11225
- provenance: import_types36.Provenance.EXTRACTED,
11878
+ provenance: import_types37.Provenance.EXTRACTED,
11226
11879
  confidence,
11227
11880
  evidence: ep.evidence
11228
11881
  };
@@ -11245,7 +11898,7 @@ async function addCallEdges(graph, services) {
11245
11898
 
11246
11899
  // src/extract/table-edges.ts
11247
11900
  init_cjs_shims();
11248
- var import_types37 = require("@neat.is/types");
11901
+ var import_types38 = require("@neat.is/types");
11249
11902
  async function addTableEdges(graph, services) {
11250
11903
  let nodesAdded = 0;
11251
11904
  let edgesAdded = 0;
@@ -11259,6 +11912,7 @@ async function addTableEdges(graph, services) {
11259
11912
  refs.push(...sqlalchemyForeignKeys(file, service.dir));
11260
11913
  refs.push(...railsSchemaForeignKeys(file, service.dir));
11261
11914
  refs.push(...laravelMigrationForeignKeys(file, service.dir));
11915
+ refs.push(...gormForeignKeys(file, service.dir));
11262
11916
  modelRefs.push(...railsModelForeignKeys(file, service.dir));
11263
11917
  modelRefs.push(...laravelModelForeignKeys(file, service.dir));
11264
11918
  } catch (err) {
@@ -11272,20 +11926,20 @@ async function addTableEdges(graph, services) {
11272
11926
  }
11273
11927
  refs.push(...modelRefs);
11274
11928
  for (const ref of refs) {
11275
- const childId = (0, import_types37.infraId)("sql-table", ref.childTable);
11276
- const parentId = (0, import_types37.infraId)("sql-table", ref.parentTable);
11929
+ const childId = (0, import_types38.infraId)("sql-table", ref.childTable);
11930
+ const parentId = (0, import_types38.infraId)("sql-table", ref.parentTable);
11277
11931
  if (childId === parentId) continue;
11278
11932
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
11279
11933
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
11280
- const edgeId = (0, import_types37.extractedEdgeId)(childId, parentId, import_types37.EdgeType.REFERENCES);
11934
+ const edgeId = (0, import_types38.extractedEdgeId)(childId, parentId, import_types38.EdgeType.REFERENCES);
11281
11935
  if (graph.hasEdge(edgeId)) continue;
11282
11936
  const edge = {
11283
11937
  id: edgeId,
11284
11938
  source: childId,
11285
11939
  target: parentId,
11286
- type: import_types37.EdgeType.REFERENCES,
11287
- provenance: import_types37.Provenance.EXTRACTED,
11288
- confidence: (0, import_types37.confidenceForExtracted)("structural"),
11940
+ type: import_types38.EdgeType.REFERENCES,
11941
+ provenance: import_types38.Provenance.EXTRACTED,
11942
+ confidence: (0, import_types38.confidenceForExtracted)("structural"),
11289
11943
  evidence: ref.evidence
11290
11944
  };
11291
11945
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -11298,7 +11952,7 @@ function ensureTableNode(graph, id, name) {
11298
11952
  if (graph.hasNode(id)) return 0;
11299
11953
  const node = {
11300
11954
  id,
11301
- type: import_types37.NodeType.InfraNode,
11955
+ type: import_types38.NodeType.InfraNode,
11302
11956
  name,
11303
11957
  provider: "self",
11304
11958
  kind: "sql-table"
@@ -11312,16 +11966,16 @@ init_cjs_shims();
11312
11966
 
11313
11967
  // src/extract/infra/docker-compose.ts
11314
11968
  init_cjs_shims();
11315
- var import_node_path48 = __toESM(require("path"), 1);
11316
- var import_types39 = require("@neat.is/types");
11969
+ var import_node_path49 = __toESM(require("path"), 1);
11970
+ var import_types40 = require("@neat.is/types");
11317
11971
 
11318
11972
  // src/extract/infra/shared.ts
11319
11973
  init_cjs_shims();
11320
- var import_types38 = require("@neat.is/types");
11974
+ var import_types39 = require("@neat.is/types");
11321
11975
  function makeInfraNode(kind, name, provider = "self", extras) {
11322
11976
  return {
11323
- id: (0, import_types38.infraId)(kind, name),
11324
- type: import_types38.NodeType.InfraNode,
11977
+ id: (0, import_types39.infraId)(kind, name),
11978
+ type: import_types39.NodeType.InfraNode,
11325
11979
  name,
11326
11980
  provider,
11327
11981
  kind,
@@ -11365,8 +12019,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
11365
12019
  source: anchorId,
11366
12020
  target: node.id,
11367
12021
  type: edgeType,
11368
- provenance: import_types38.Provenance.EXTRACTED,
11369
- confidence: (0, import_types38.confidenceForExtracted)("structural"),
12022
+ provenance: import_types39.Provenance.EXTRACTED,
12023
+ confidence: (0, import_types39.confidenceForExtracted)("structural"),
11370
12024
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11371
12025
  };
11372
12026
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11383,7 +12037,7 @@ function dependsOnList(value) {
11383
12037
  }
11384
12038
  function serviceNameToServiceNode(name, services) {
11385
12039
  for (const s of services) {
11386
- if (s.node.name === name || import_node_path48.default.basename(s.dir) === name) return s.node.id;
12040
+ if (s.node.name === name || import_node_path49.default.basename(s.dir) === name) return s.node.id;
11387
12041
  }
11388
12042
  return null;
11389
12043
  }
@@ -11392,7 +12046,7 @@ async function addComposeInfra(graph, scanPath, services) {
11392
12046
  let edgesAdded = 0;
11393
12047
  let composePath = null;
11394
12048
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
11395
- const abs = import_node_path48.default.join(scanPath, name);
12049
+ const abs = import_node_path49.default.join(scanPath, name);
11396
12050
  if (await exists(abs)) {
11397
12051
  composePath = abs;
11398
12052
  break;
@@ -11405,13 +12059,13 @@ async function addComposeInfra(graph, scanPath, services) {
11405
12059
  } catch (err) {
11406
12060
  recordExtractionError(
11407
12061
  "infra docker-compose",
11408
- import_node_path48.default.relative(scanPath, composePath),
12062
+ import_node_path49.default.relative(scanPath, composePath),
11409
12063
  err
11410
12064
  );
11411
12065
  return { nodesAdded, edgesAdded };
11412
12066
  }
11413
12067
  if (!compose?.services) return { nodesAdded, edgesAdded };
11414
- const evidenceFile = import_node_path48.default.relative(scanPath, composePath).split(import_node_path48.default.sep).join("/");
12068
+ const evidenceFile = import_node_path49.default.relative(scanPath, composePath).split(import_node_path49.default.sep).join("/");
11415
12069
  const composeNameToNodeId = /* @__PURE__ */ new Map();
11416
12070
  for (const [composeName, svc] of Object.entries(compose.services)) {
11417
12071
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -11433,15 +12087,15 @@ async function addComposeInfra(graph, scanPath, services) {
11433
12087
  for (const dep of dependsOnList(svc.depends_on)) {
11434
12088
  const targetId = composeNameToNodeId.get(dep);
11435
12089
  if (!targetId) continue;
11436
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types39.EdgeType.DEPENDS_ON);
12090
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types40.EdgeType.DEPENDS_ON);
11437
12091
  if (graph.hasEdge(edgeId)) continue;
11438
12092
  const edge = {
11439
12093
  id: edgeId,
11440
12094
  source: sourceId,
11441
12095
  target: targetId,
11442
- type: import_types39.EdgeType.DEPENDS_ON,
11443
- provenance: import_types39.Provenance.EXTRACTED,
11444
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
12096
+ type: import_types40.EdgeType.DEPENDS_ON,
12097
+ provenance: import_types40.Provenance.EXTRACTED,
12098
+ confidence: (0, import_types40.confidenceForExtracted)("structural"),
11445
12099
  evidence: { file: evidenceFile }
11446
12100
  };
11447
12101
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11453,9 +12107,9 @@ async function addComposeInfra(graph, scanPath, services) {
11453
12107
 
11454
12108
  // src/extract/infra/dockerfile.ts
11455
12109
  init_cjs_shims();
11456
- var import_node_path49 = __toESM(require("path"), 1);
12110
+ var import_node_path50 = __toESM(require("path"), 1);
11457
12111
  var import_node_fs18 = require("fs");
11458
- var import_types40 = require("@neat.is/types");
12112
+ var import_types41 = require("@neat.is/types");
11459
12113
  function readDockerfile(content) {
11460
12114
  let image = null;
11461
12115
  const ports = [];
@@ -11484,7 +12138,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11484
12138
  let nodesAdded = 0;
11485
12139
  let edgesAdded = 0;
11486
12140
  for (const service of services) {
11487
- const dockerfilePath = import_node_path49.default.join(service.dir, "Dockerfile");
12141
+ const dockerfilePath = import_node_path50.default.join(service.dir, "Dockerfile");
11488
12142
  if (!await exists(dockerfilePath)) continue;
11489
12143
  let content;
11490
12144
  try {
@@ -11492,7 +12146,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11492
12146
  } catch (err) {
11493
12147
  recordExtractionError(
11494
12148
  "infra dockerfile",
11495
- import_node_path49.default.relative(scanPath, dockerfilePath),
12149
+ import_node_path50.default.relative(scanPath, dockerfilePath),
11496
12150
  err
11497
12151
  );
11498
12152
  continue;
@@ -11504,8 +12158,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11504
12158
  graph.addNode(node.id, node);
11505
12159
  nodesAdded++;
11506
12160
  }
11507
- const relDockerfile = toPosix(import_node_path49.default.relative(service.dir, dockerfilePath));
11508
- const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, dockerfilePath));
12161
+ const relDockerfile = toPosix(import_node_path50.default.relative(service.dir, dockerfilePath));
12162
+ const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, dockerfilePath));
11509
12163
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11510
12164
  graph,
11511
12165
  service.pkg.name,
@@ -11514,15 +12168,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11514
12168
  );
11515
12169
  nodesAdded += fn;
11516
12170
  edgesAdded += fe;
11517
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types40.EdgeType.RUNS_ON);
12171
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types41.EdgeType.RUNS_ON);
11518
12172
  if (!graph.hasEdge(edgeId)) {
11519
12173
  const edge = {
11520
12174
  id: edgeId,
11521
12175
  source: fileNodeId,
11522
12176
  target: node.id,
11523
- type: import_types40.EdgeType.RUNS_ON,
11524
- provenance: import_types40.Provenance.EXTRACTED,
11525
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12177
+ type: import_types41.EdgeType.RUNS_ON,
12178
+ provenance: import_types41.Provenance.EXTRACTED,
12179
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11526
12180
  evidence: {
11527
12181
  file: evidenceFile,
11528
12182
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -11537,15 +12191,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11537
12191
  graph.addNode(portNode.id, portNode);
11538
12192
  nodesAdded++;
11539
12193
  }
11540
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types40.EdgeType.CONNECTS_TO);
12194
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types41.EdgeType.CONNECTS_TO);
11541
12195
  if (graph.hasEdge(portEdgeId)) continue;
11542
12196
  const portEdge = {
11543
12197
  id: portEdgeId,
11544
12198
  source: fileNodeId,
11545
12199
  target: portNode.id,
11546
- type: import_types40.EdgeType.CONNECTS_TO,
11547
- provenance: import_types40.Provenance.EXTRACTED,
11548
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12200
+ type: import_types41.EdgeType.CONNECTS_TO,
12201
+ provenance: import_types41.Provenance.EXTRACTED,
12202
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11549
12203
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
11550
12204
  };
11551
12205
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -11558,8 +12212,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11558
12212
  // src/extract/infra/terraform.ts
11559
12213
  init_cjs_shims();
11560
12214
  var import_node_fs19 = require("fs");
11561
- var import_node_path50 = __toESM(require("path"), 1);
11562
- var import_types41 = require("@neat.is/types");
12215
+ var import_node_path51 = __toESM(require("path"), 1);
12216
+ var import_types42 = require("@neat.is/types");
11563
12217
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
11564
12218
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
11565
12219
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -11569,11 +12223,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
11569
12223
  for (const entry of entries) {
11570
12224
  if (entry.isDirectory()) {
11571
12225
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
11572
- const child = import_node_path50.default.join(start, entry.name);
12226
+ const child = import_node_path51.default.join(start, entry.name);
11573
12227
  if (await isPythonVenvDir(child)) continue;
11574
12228
  out.push(...await walkTfFiles(child, depth + 1, max));
11575
12229
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
11576
- out.push(import_node_path50.default.join(start, entry.name));
12230
+ out.push(import_node_path51.default.join(start, entry.name));
11577
12231
  }
11578
12232
  }
11579
12233
  return out;
@@ -11605,7 +12259,7 @@ async function addTerraformResources(graph, scanPath) {
11605
12259
  const files = await walkTfFiles(scanPath);
11606
12260
  for (const file of files) {
11607
12261
  const content = await import_node_fs19.promises.readFile(file, "utf8");
11608
- const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, file));
12262
+ const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, file));
11609
12263
  const resources = [];
11610
12264
  const byKey = /* @__PURE__ */ new Map();
11611
12265
  RESOURCE_RE.lastIndex = 0;
@@ -11640,16 +12294,16 @@ async function addTerraformResources(graph, scanPath) {
11640
12294
  if (!target) continue;
11641
12295
  if (seen.has(target.nodeId)) continue;
11642
12296
  seen.add(target.nodeId);
11643
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types41.EdgeType.DEPENDS_ON);
12297
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types42.EdgeType.DEPENDS_ON);
11644
12298
  if (graph.hasEdge(edgeId)) continue;
11645
12299
  const line = lineAt2(content, resource.bodyOffset + ref.index);
11646
12300
  const edge = {
11647
12301
  id: edgeId,
11648
12302
  source: resource.nodeId,
11649
12303
  target: target.nodeId,
11650
- type: import_types41.EdgeType.DEPENDS_ON,
11651
- provenance: import_types41.Provenance.EXTRACTED,
11652
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12304
+ type: import_types42.EdgeType.DEPENDS_ON,
12305
+ provenance: import_types42.Provenance.EXTRACTED,
12306
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
11653
12307
  evidence: { file: evidenceFile, line, snippet: key }
11654
12308
  };
11655
12309
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11663,7 +12317,7 @@ async function addTerraformResources(graph, scanPath) {
11663
12317
  // src/extract/infra/k8s.ts
11664
12318
  init_cjs_shims();
11665
12319
  var import_node_fs20 = require("fs");
11666
- var import_node_path51 = __toESM(require("path"), 1);
12320
+ var import_node_path52 = __toESM(require("path"), 1);
11667
12321
  var import_yaml3 = require("yaml");
11668
12322
  var K8S_KIND_TO_INFRA_KIND = {
11669
12323
  Service: "k8s-service",
@@ -11681,11 +12335,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
11681
12335
  for (const entry of entries) {
11682
12336
  if (entry.isDirectory()) {
11683
12337
  if (IGNORED_DIRS.has(entry.name)) continue;
11684
- const child = import_node_path51.default.join(start, entry.name);
12338
+ const child = import_node_path52.default.join(start, entry.name);
11685
12339
  if (await isPythonVenvDir(child)) continue;
11686
12340
  out.push(...await walkYamlFiles2(child, depth + 1, max));
11687
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path51.default.extname(entry.name))) {
11688
- out.push(import_node_path51.default.join(start, entry.name));
12341
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path52.default.extname(entry.name))) {
12342
+ out.push(import_node_path52.default.join(start, entry.name));
11689
12343
  }
11690
12344
  }
11691
12345
  return out;
@@ -11719,13 +12373,13 @@ async function addK8sResources(graph, scanPath) {
11719
12373
  // src/extract/infra/cloudflare.ts
11720
12374
  init_cjs_shims();
11721
12375
  var import_node_fs21 = require("fs");
11722
- var import_node_path52 = __toESM(require("path"), 1);
12376
+ var import_node_path53 = __toESM(require("path"), 1);
11723
12377
  var import_smol_toml2 = require("smol-toml");
11724
- var import_types42 = require("@neat.is/types");
12378
+ var import_types43 = require("@neat.is/types");
11725
12379
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
11726
12380
  async function readWranglerConfig(dir) {
11727
12381
  for (const filename of WRANGLER_FILENAMES) {
11728
- const abs = import_node_path52.default.join(dir, filename);
12382
+ const abs = import_node_path53.default.join(dir, filename);
11729
12383
  if (!await exists(abs)) continue;
11730
12384
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
11731
12385
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -11769,8 +12423,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
11769
12423
  source: anchorId,
11770
12424
  target: node.id,
11771
12425
  type: edgeType,
11772
- provenance: import_types42.Provenance.EXTRACTED,
11773
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12426
+ provenance: import_types43.Provenance.EXTRACTED,
12427
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11774
12428
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11775
12429
  };
11776
12430
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11788,11 +12442,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11788
12442
  try {
11789
12443
  read = await readWranglerConfig(service.dir);
11790
12444
  } catch (err) {
11791
- recordExtractionError("infra cloudflare", import_node_path52.default.relative(scanPath, service.dir), err);
12445
+ recordExtractionError("infra cloudflare", import_node_path53.default.relative(scanPath, service.dir), err);
11792
12446
  continue;
11793
12447
  }
11794
12448
  if (!read || !read.config.name) continue;
11795
- const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, import_node_path52.default.join(service.dir, read.relFile)));
12449
+ const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, read.relFile)));
11796
12450
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
11797
12451
  }
11798
12452
  for (const worker of discovered) {
@@ -11804,7 +12458,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11804
12458
  }
11805
12459
  let anchorId = service.node.id;
11806
12460
  if (config.main) {
11807
- const entryRelPath = toPosix(import_node_path52.default.normalize(config.main));
12461
+ const entryRelPath = toPosix(import_node_path53.default.normalize(config.main));
11808
12462
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11809
12463
  graph,
11810
12464
  service.pkg.name,
@@ -11831,15 +12485,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11831
12485
  nodesAdded++;
11832
12486
  }
11833
12487
  if (runtimeNode.id !== anchorId) {
11834
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types42.EdgeType.RUNS_ON);
12488
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types43.EdgeType.RUNS_ON);
11835
12489
  if (!graph.hasEdge(runsOnId)) {
11836
12490
  const edge = {
11837
12491
  id: runsOnId,
11838
12492
  source: anchorId,
11839
12493
  target: runtimeNode.id,
11840
- type: import_types42.EdgeType.RUNS_ON,
11841
- provenance: import_types42.Provenance.EXTRACTED,
11842
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12494
+ type: import_types43.EdgeType.RUNS_ON,
12495
+ provenance: import_types43.Provenance.EXTRACTED,
12496
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11843
12497
  evidence: {
11844
12498
  file: evidenceFile,
11845
12499
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -11853,7 +12507,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11853
12507
  const result = addResourceEdge(
11854
12508
  graph,
11855
12509
  anchorId,
11856
- import_types42.EdgeType.CONNECTS_TO,
12510
+ import_types43.EdgeType.CONNECTS_TO,
11857
12511
  "cloudflare-route",
11858
12512
  route,
11859
12513
  evidenceFile,
@@ -11877,7 +12531,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11877
12531
  const result = addResourceEdge(
11878
12532
  graph,
11879
12533
  anchorId,
11880
- import_types42.EdgeType.DEPENDS_ON,
12534
+ import_types43.EdgeType.DEPENDS_ON,
11881
12535
  group.kind,
11882
12536
  name,
11883
12537
  evidenceFile,
@@ -11891,7 +12545,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11891
12545
  const result = addResourceEdge(
11892
12546
  graph,
11893
12547
  anchorId,
11894
- import_types42.EdgeType.DEPENDS_ON,
12548
+ import_types43.EdgeType.DEPENDS_ON,
11895
12549
  "cloudflare-cron",
11896
12550
  cron,
11897
12551
  evidenceFile,
@@ -11904,7 +12558,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11904
12558
  const result = addResourceEdge(
11905
12559
  graph,
11906
12560
  anchorId,
11907
- import_types42.EdgeType.DEPENDS_ON,
12561
+ import_types43.EdgeType.DEPENDS_ON,
11908
12562
  "cloudflare-env-var",
11909
12563
  varName,
11910
12564
  evidenceFile,
@@ -11917,15 +12571,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11917
12571
  if (!svc.service) continue;
11918
12572
  const target = workerIndex.get(svc.service);
11919
12573
  if (target && target.anchorId !== anchorId) {
11920
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types42.EdgeType.CALLS);
12574
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types43.EdgeType.CALLS);
11921
12575
  if (!graph.hasEdge(edgeId)) {
11922
12576
  const edge = {
11923
12577
  id: edgeId,
11924
12578
  source: anchorId,
11925
12579
  target: target.anchorId,
11926
- type: import_types42.EdgeType.CALLS,
11927
- provenance: import_types42.Provenance.EXTRACTED,
11928
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12580
+ type: import_types43.EdgeType.CALLS,
12581
+ provenance: import_types43.Provenance.EXTRACTED,
12582
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11929
12583
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
11930
12584
  };
11931
12585
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11936,7 +12590,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11936
12590
  const result = addResourceEdge(
11937
12591
  graph,
11938
12592
  anchorId,
11939
- import_types42.EdgeType.DEPENDS_ON,
12593
+ import_types43.EdgeType.DEPENDS_ON,
11940
12594
  "cloudflare-service-binding",
11941
12595
  svc.service,
11942
12596
  evidenceFile,
@@ -11952,12 +12606,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11952
12606
  // src/extract/infra/vercel.ts
11953
12607
  init_cjs_shims();
11954
12608
  var import_node_fs22 = require("fs");
11955
- var import_node_path53 = __toESM(require("path"), 1);
11956
- var import_types43 = require("@neat.is/types");
12609
+ var import_node_path54 = __toESM(require("path"), 1);
12610
+ var import_types44 = require("@neat.is/types");
11957
12611
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
11958
12612
  async function readVercelConfig(dir) {
11959
12613
  for (const filename of VERCEL_CONFIG_FILENAMES) {
11960
- const abs = import_node_path53.default.join(dir, filename);
12614
+ const abs = import_node_path54.default.join(dir, filename);
11961
12615
  if (!await exists(abs)) continue;
11962
12616
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
11963
12617
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -11966,7 +12620,7 @@ async function readVercelConfig(dir) {
11966
12620
  return null;
11967
12621
  }
11968
12622
  async function readLinkedProjectName(dir) {
11969
- const abs = import_node_path53.default.join(dir, ".vercel", "project.json");
12623
+ const abs = import_node_path54.default.join(dir, ".vercel", "project.json");
11970
12624
  if (!await exists(abs)) return void 0;
11971
12625
  const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
11972
12626
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -11984,7 +12638,7 @@ async function addVercelServices(graph, services, scanPath) {
11984
12638
  read = await readVercelConfig(service.dir);
11985
12639
  projectName = await readLinkedProjectName(service.dir);
11986
12640
  } catch (err) {
11987
- recordExtractionError("infra vercel", import_node_path53.default.relative(scanPath, service.dir), err);
12641
+ recordExtractionError("infra vercel", import_node_path54.default.relative(scanPath, service.dir), err);
11988
12642
  continue;
11989
12643
  }
11990
12644
  if (!read && !projectName) continue;
@@ -12000,7 +12654,7 @@ async function addVercelServices(graph, services, scanPath) {
12000
12654
  const anchorId = service.node.id;
12001
12655
  if (!read) continue;
12002
12656
  const { config, relFile, raw } = read;
12003
- const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, relFile)));
12657
+ const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
12004
12658
  const add = (edgeType, kind, name) => {
12005
12659
  if (!name) return;
12006
12660
  const result = emitPlatformResourceEdge(
@@ -12016,12 +12670,12 @@ async function addVercelServices(graph, services, scanPath) {
12016
12670
  nodesAdded += result.nodesAdded;
12017
12671
  edgesAdded += result.edgesAdded;
12018
12672
  };
12019
- add(import_types43.EdgeType.RUNS_ON, "vercel", "vercel");
12020
- for (const cron of config.crons ?? []) add(import_types43.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12021
- for (const varName of Object.keys(config.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12022
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12673
+ add(import_types44.EdgeType.RUNS_ON, "vercel", "vercel");
12674
+ for (const cron of config.crons ?? []) add(import_types44.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12675
+ for (const varName of Object.keys(config.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12676
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12023
12677
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
12024
- add(import_types43.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12678
+ add(import_types44.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12025
12679
  }
12026
12680
  }
12027
12681
  return { nodesAdded, edgesAdded };
@@ -12030,13 +12684,13 @@ async function addVercelServices(graph, services, scanPath) {
12030
12684
  // src/extract/infra/railway.ts
12031
12685
  init_cjs_shims();
12032
12686
  var import_node_fs23 = require("fs");
12033
- var import_node_path54 = __toESM(require("path"), 1);
12687
+ var import_node_path55 = __toESM(require("path"), 1);
12034
12688
  var import_smol_toml3 = require("smol-toml");
12035
- var import_types44 = require("@neat.is/types");
12689
+ var import_types45 = require("@neat.is/types");
12036
12690
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
12037
12691
  async function readRailwayConfig(dir) {
12038
12692
  for (const filename of RAILWAY_FILENAMES) {
12039
- const abs = import_node_path54.default.join(dir, filename);
12693
+ const abs = import_node_path55.default.join(dir, filename);
12040
12694
  if (!await exists(abs)) continue;
12041
12695
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
12042
12696
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12052,7 +12706,7 @@ async function addRailwayServices(graph, services, scanPath) {
12052
12706
  try {
12053
12707
  read = await readRailwayConfig(service.dir);
12054
12708
  } catch (err) {
12055
- recordExtractionError("infra railway", import_node_path54.default.relative(scanPath, service.dir), err);
12709
+ recordExtractionError("infra railway", import_node_path55.default.relative(scanPath, service.dir), err);
12056
12710
  continue;
12057
12711
  }
12058
12712
  if (!read) continue;
@@ -12062,7 +12716,7 @@ async function addRailwayServices(graph, services, scanPath) {
12062
12716
  }
12063
12717
  const anchorId = service.node.id;
12064
12718
  const { config, relFile, raw } = read;
12065
- const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
12719
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12066
12720
  const add = (edgeType, kind, name) => {
12067
12721
  if (!name) return;
12068
12722
  const result = emitPlatformResourceEdge(
@@ -12078,9 +12732,9 @@ async function addRailwayServices(graph, services, scanPath) {
12078
12732
  nodesAdded += result.nodesAdded;
12079
12733
  edgesAdded += result.edgesAdded;
12080
12734
  };
12081
- add(import_types44.EdgeType.RUNS_ON, "railway", "railway");
12082
- add(import_types44.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12083
- add(import_types44.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12735
+ add(import_types45.EdgeType.RUNS_ON, "railway", "railway");
12736
+ add(import_types45.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12737
+ add(import_types45.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12084
12738
  }
12085
12739
  return { nodesAdded, edgesAdded };
12086
12740
  }
@@ -12088,12 +12742,12 @@ async function addRailwayServices(graph, services, scanPath) {
12088
12742
  // src/extract/infra/supabase.ts
12089
12743
  init_cjs_shims();
12090
12744
  var import_node_fs24 = require("fs");
12091
- var import_node_path55 = __toESM(require("path"), 1);
12745
+ var import_node_path56 = __toESM(require("path"), 1);
12092
12746
  var import_smol_toml4 = require("smol-toml");
12093
- var import_types45 = require("@neat.is/types");
12747
+ var import_types46 = require("@neat.is/types");
12094
12748
  async function readSupabaseConfig(dir) {
12095
- const relFile = import_node_path55.default.join("supabase", "config.toml");
12096
- const abs = import_node_path55.default.join(dir, relFile);
12749
+ const relFile = import_node_path56.default.join("supabase", "config.toml");
12750
+ const abs = import_node_path56.default.join(dir, relFile);
12097
12751
  if (!await exists(abs)) return null;
12098
12752
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
12099
12753
  const config = (0, import_smol_toml4.parse)(raw);
@@ -12107,7 +12761,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12107
12761
  try {
12108
12762
  read = await readSupabaseConfig(service.dir);
12109
12763
  } catch (err) {
12110
- recordExtractionError("infra supabase", import_node_path55.default.relative(scanPath, service.dir), err);
12764
+ recordExtractionError("infra supabase", import_node_path56.default.relative(scanPath, service.dir), err);
12111
12765
  continue;
12112
12766
  }
12113
12767
  if (!read) continue;
@@ -12122,7 +12776,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12122
12776
  });
12123
12777
  }
12124
12778
  const anchorId = service.node.id;
12125
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12779
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12126
12780
  const add = (edgeType, kind, name) => {
12127
12781
  if (!name) return;
12128
12782
  const result = emitPlatformResourceEdge(
@@ -12138,10 +12792,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
12138
12792
  nodesAdded += result.nodesAdded;
12139
12793
  edgesAdded += result.edgesAdded;
12140
12794
  };
12141
- add(import_types45.EdgeType.RUNS_ON, "supabase", "supabase");
12142
- for (const fn of Object.keys(config.functions ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "supabase-function", fn);
12143
- if (config.storage) add(import_types45.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12144
- if (config.auth) add(import_types45.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12795
+ add(import_types46.EdgeType.RUNS_ON, "supabase", "supabase");
12796
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types46.EdgeType.DEPENDS_ON, "supabase-function", fn);
12797
+ if (config.storage) add(import_types46.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12798
+ if (config.auth) add(import_types46.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12145
12799
  }
12146
12800
  return { nodesAdded, edgesAdded };
12147
12801
  }
@@ -12164,14 +12818,14 @@ async function addInfra(graph, scanPath, services) {
12164
12818
 
12165
12819
  // src/extract/zod-shapes.ts
12166
12820
  init_cjs_shims();
12167
- var import_node_path56 = __toESM(require("path"), 1);
12168
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12821
+ var import_node_path57 = __toESM(require("path"), 1);
12822
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
12169
12823
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
12170
- var import_types46 = require("@neat.is/types");
12824
+ var import_types47 = require("@neat.is/types");
12171
12825
  var ZOD_IMPORT_RE = /\bzod\b/;
12172
12826
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12173
12827
  function parserForExt3(ext) {
12174
- const p = new import_tree_sitter15.default();
12828
+ const p = new import_tree_sitter16.default();
12175
12829
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12176
12830
  return p;
12177
12831
  }
@@ -12259,7 +12913,7 @@ function topLevelSchemas(root) {
12259
12913
  }
12260
12914
  function zodShapesFromFile(file, serviceDir) {
12261
12915
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12262
- const tree = parseSource3(parserForExt3(import_node_path56.default.extname(file.path)), file.content);
12916
+ const tree = parseSource3(parserForExt3(import_node_path57.default.extname(file.path)), file.content);
12263
12917
  const out = [];
12264
12918
  const seen = /* @__PURE__ */ new Set();
12265
12919
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -12273,11 +12927,11 @@ function zodShapesFromFile(file, serviceDir) {
12273
12927
  seen.add(name);
12274
12928
  const line = call.startPosition.row + 1;
12275
12929
  out.push({
12276
- infraId: (0, import_types46.infraId)("zod-schema", name),
12930
+ infraId: (0, import_types47.infraId)("zod-schema", name),
12277
12931
  name,
12278
12932
  fields,
12279
12933
  evidence: {
12280
- file: import_node_path56.default.relative(serviceDir, file.path),
12934
+ file: import_node_path57.default.relative(serviceDir, file.path),
12281
12935
  line,
12282
12936
  snippet: snippet(file.content, line)
12283
12937
  }
@@ -12308,7 +12962,7 @@ async function addZodShapes(graph, services) {
12308
12962
  if (!graph.hasNode(shape.infraId)) {
12309
12963
  const node = {
12310
12964
  id: shape.infraId,
12311
- type: import_types46.NodeType.InfraNode,
12965
+ type: import_types47.NodeType.InfraNode,
12312
12966
  name: shape.name,
12313
12967
  provider: "self",
12314
12968
  kind: "zod-schema"
@@ -12318,14 +12972,14 @@ async function addZodShapes(graph, services) {
12318
12972
  }
12319
12973
  if (shape.fields.length > 0) {
12320
12974
  const node = graph.getNodeAttributes(shape.infraId);
12321
- if (node.type === import_types46.NodeType.InfraNode) {
12975
+ if (node.type === import_types47.NodeType.InfraNode) {
12322
12976
  graph.replaceNodeAttributes(shape.infraId, {
12323
12977
  ...node,
12324
12978
  columns: foldColumns(
12325
12979
  node.columns,
12326
12980
  shape.fields,
12327
- import_types46.Provenance.EXTRACTED,
12328
- (0, import_types46.confidenceForExtracted)("structural")
12981
+ import_types47.Provenance.EXTRACTED,
12982
+ (0, import_types47.confidenceForExtracted)("structural")
12329
12983
  )
12330
12984
  });
12331
12985
  }
@@ -12339,15 +12993,15 @@ async function addZodShapes(graph, services) {
12339
12993
  );
12340
12994
  nodesAdded += n;
12341
12995
  edgesAdded += e;
12342
- const edgeId = (0, import_types46.extractedEdgeId)(fileNodeId, shape.infraId, import_types46.EdgeType.CONTAINS);
12996
+ const edgeId = (0, import_types47.extractedEdgeId)(fileNodeId, shape.infraId, import_types47.EdgeType.CONTAINS);
12343
12997
  if (!graph.hasEdge(edgeId)) {
12344
12998
  const edge = {
12345
12999
  id: edgeId,
12346
13000
  source: fileNodeId,
12347
13001
  target: shape.infraId,
12348
- type: import_types46.EdgeType.CONTAINS,
12349
- provenance: import_types46.Provenance.EXTRACTED,
12350
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
13002
+ type: import_types47.EdgeType.CONTAINS,
13003
+ provenance: import_types47.Provenance.EXTRACTED,
13004
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
12351
13005
  evidence: shape.evidence
12352
13006
  };
12353
13007
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -12361,7 +13015,7 @@ async function addZodShapes(graph, services) {
12361
13015
 
12362
13016
  // src/extract/firestore-rules.ts
12363
13017
  init_cjs_shims();
12364
- var import_types47 = require("@neat.is/types");
13018
+ var import_types48 = require("@neat.is/types");
12365
13019
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
12366
13020
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
12367
13021
  function stripComments(src) {
@@ -12501,7 +13155,7 @@ async function addFirestoreRules(graph, services) {
12501
13155
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
12502
13156
  graph.forEachNode((id, attrs) => {
12503
13157
  const node = attrs;
12504
- if (node.type !== import_types47.NodeType.InfraNode) return;
13158
+ if (node.type !== import_types48.NodeType.InfraNode) return;
12505
13159
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
12506
13160
  const fields = guards.get(collectionKeyFromName(node.name));
12507
13161
  if (!fields || fields.size === 0) return;
@@ -12514,17 +13168,17 @@ async function addFirestoreRules(graph, services) {
12514
13168
  }
12515
13169
 
12516
13170
  // src/extract/index.ts
12517
- var import_node_path58 = __toESM(require("path"), 1);
13171
+ var import_node_path59 = __toESM(require("path"), 1);
12518
13172
 
12519
13173
  // src/extract/retire.ts
12520
13174
  init_cjs_shims();
12521
13175
  var import_node_fs25 = require("fs");
12522
- var import_node_path57 = __toESM(require("path"), 1);
12523
- var import_types48 = require("@neat.is/types");
13176
+ var import_node_path58 = __toESM(require("path"), 1);
13177
+ var import_types49 = require("@neat.is/types");
12524
13178
  function dropOrphanedFileNodes(graph) {
12525
13179
  const orphans = [];
12526
13180
  graph.forEachNode((id, attrs) => {
12527
- if (attrs.type !== import_types48.NodeType.FileNode) return;
13181
+ if (attrs.type !== import_types49.NodeType.FileNode) return;
12528
13182
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
12529
13183
  orphans.push(id);
12530
13184
  }
@@ -12537,14 +13191,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
12537
13191
  const bases = [scanPath, ...serviceDirs];
12538
13192
  graph.forEachEdge((id, attrs) => {
12539
13193
  const edge = attrs;
12540
- if (edge.provenance !== import_types48.Provenance.EXTRACTED) return;
13194
+ if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
12541
13195
  const evidenceFile = edge.evidence?.file;
12542
13196
  if (!evidenceFile) return;
12543
- if (import_node_path57.default.isAbsolute(evidenceFile)) {
13197
+ if (import_node_path58.default.isAbsolute(evidenceFile)) {
12544
13198
  if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
12545
13199
  return;
12546
13200
  }
12547
- const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path57.default.join(base, evidenceFile)));
13201
+ const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path58.default.join(base, evidenceFile)));
12548
13202
  if (!found) toDrop.push(id);
12549
13203
  });
12550
13204
  for (const id of toDrop) graph.dropEdge(id);
@@ -12601,7 +13255,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12601
13255
  }
12602
13256
  const droppedEntries = drainDroppedExtracted();
12603
13257
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
12604
- const rejectedPath = import_node_path58.default.join(import_node_path58.default.dirname(opts.errorsPath), "rejected.ndjson");
13258
+ const rejectedPath = import_node_path59.default.join(import_node_path59.default.dirname(opts.errorsPath), "rejected.ndjson");
12605
13259
  try {
12606
13260
  await writeRejectedExtracted(droppedEntries, rejectedPath);
12607
13261
  } catch (err) {
@@ -12636,8 +13290,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12636
13290
  // src/persist.ts
12637
13291
  init_cjs_shims();
12638
13292
  var import_node_fs26 = require("fs");
12639
- var import_node_path59 = __toESM(require("path"), 1);
12640
- var import_types49 = require("@neat.is/types");
13293
+ var import_node_path60 = __toESM(require("path"), 1);
13294
+ var import_types50 = require("@neat.is/types");
12641
13295
  var SCHEMA_VERSION = 7;
12642
13296
  function migrateV1ToV2(payload) {
12643
13297
  const nodes = payload.graph.nodes;
@@ -12661,7 +13315,7 @@ function migrateV5ToV6(payload) {
12661
13315
  if (Array.isArray(nodes)) {
12662
13316
  for (const node of nodes) {
12663
13317
  const attrs = node.attributes;
12664
- if (!attrs || attrs.type !== import_types49.NodeType.InfraNode) continue;
13318
+ if (!attrs || attrs.type !== import_types50.NodeType.InfraNode) continue;
12665
13319
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
12666
13320
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
12667
13321
  }
@@ -12677,12 +13331,12 @@ function migrateV2ToV3(payload) {
12677
13331
  for (const edge of edges) {
12678
13332
  const attrs = edge.attributes;
12679
13333
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
12680
- attrs.provenance = import_types49.Provenance.OBSERVED;
13334
+ attrs.provenance = import_types50.Provenance.OBSERVED;
12681
13335
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
12682
13336
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
12683
13337
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
12684
13338
  if (type && source && target) {
12685
- const newId = (0, import_types49.observedEdgeId)(source, target, type);
13339
+ const newId = (0, import_types50.observedEdgeId)(source, target, type);
12686
13340
  attrs.id = newId;
12687
13341
  if (edge.key) edge.key = newId;
12688
13342
  }
@@ -12691,7 +13345,7 @@ function migrateV2ToV3(payload) {
12691
13345
  return { ...payload, schemaVersion: 3 };
12692
13346
  }
12693
13347
  async function ensureDir(filePath) {
12694
- await import_node_fs26.promises.mkdir(import_node_path59.default.dirname(filePath), { recursive: true });
13348
+ await import_node_fs26.promises.mkdir(import_node_path60.default.dirname(filePath), { recursive: true });
12695
13349
  }
12696
13350
  async function saveGraphToDisk(graph, outPath) {
12697
13351
  await ensureDir(outPath);
@@ -12783,19 +13437,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
12783
13437
  init_cjs_shims();
12784
13438
  var import_fastify2 = __toESM(require("fastify"), 1);
12785
13439
  var import_cors = __toESM(require("@fastify/cors"), 1);
12786
- var import_types79 = require("@neat.is/types");
13440
+ var import_types80 = require("@neat.is/types");
12787
13441
 
12788
13442
  // src/extend/index.ts
12789
13443
  init_cjs_shims();
12790
13444
  var import_node_fs28 = require("fs");
12791
- var import_node_path61 = __toESM(require("path"), 1);
13445
+ var import_node_path62 = __toESM(require("path"), 1);
12792
13446
  var import_node_os2 = __toESM(require("os"), 1);
12793
13447
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
12794
13448
 
12795
13449
  // src/installers/package-manager.ts
12796
13450
  init_cjs_shims();
12797
13451
  var import_node_fs27 = require("fs");
12798
- var import_node_path60 = __toESM(require("path"), 1);
13452
+ var import_node_path61 = __toESM(require("path"), 1);
12799
13453
  var import_node_child_process = require("child_process");
12800
13454
  var LOCKFILE_PRIORITY = [
12801
13455
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -12817,22 +13471,22 @@ async function exists2(p) {
12817
13471
  }
12818
13472
  }
12819
13473
  async function detectPackageManager(serviceDir) {
12820
- let dir = import_node_path60.default.resolve(serviceDir);
13474
+ let dir = import_node_path61.default.resolve(serviceDir);
12821
13475
  const stops = /* @__PURE__ */ new Set();
12822
13476
  for (let i = 0; i < 64; i++) {
12823
13477
  if (stops.has(dir)) break;
12824
13478
  stops.add(dir);
12825
13479
  for (const candidate of LOCKFILE_PRIORITY) {
12826
- const lockPath = import_node_path60.default.join(dir, candidate.lockfile);
13480
+ const lockPath = import_node_path61.default.join(dir, candidate.lockfile);
12827
13481
  if (await exists2(lockPath)) {
12828
13482
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
12829
13483
  }
12830
13484
  }
12831
- const parent = import_node_path60.default.dirname(dir);
13485
+ const parent = import_node_path61.default.dirname(dir);
12832
13486
  if (parent === dir) break;
12833
13487
  dir = parent;
12834
13488
  }
12835
- return { pm: "npm", cwd: import_node_path60.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
13489
+ return { pm: "npm", cwd: import_node_path61.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
12836
13490
  }
12837
13491
  async function runPackageManagerInstall(cmd) {
12838
13492
  return new Promise((resolve) => {
@@ -12881,7 +13535,7 @@ async function fileExists2(p) {
12881
13535
  }
12882
13536
  }
12883
13537
  async function readPackageJson(scanPath) {
12884
- const pkgPath = import_node_path61.default.join(scanPath, "package.json");
13538
+ const pkgPath = import_node_path62.default.join(scanPath, "package.json");
12885
13539
  const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
12886
13540
  return JSON.parse(raw);
12887
13541
  }
@@ -12895,27 +13549,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
12895
13549
  ]);
12896
13550
  async function findHookFiles(scanPath) {
12897
13551
  const found = [];
12898
- const walk8 = async (dir) => {
13552
+ const walk9 = async (dir) => {
12899
13553
  const entries = await import_node_fs28.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
12900
13554
  for (const entry of entries) {
12901
13555
  if (entry.isDirectory()) {
12902
13556
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
12903
- await walk8(import_node_path61.default.join(dir, entry.name));
13557
+ await walk9(import_node_path62.default.join(dir, entry.name));
12904
13558
  } else if (entry.isFile()) {
12905
13559
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
12906
- const rel = import_node_path61.default.relative(scanPath, import_node_path61.default.join(dir, entry.name));
12907
- found.push(rel.split(import_node_path61.default.sep).join("/"));
13560
+ const rel = import_node_path62.default.relative(scanPath, import_node_path62.default.join(dir, entry.name));
13561
+ found.push(rel.split(import_node_path62.default.sep).join("/"));
12908
13562
  }
12909
13563
  }
12910
13564
  }
12911
13565
  };
12912
- await walk8(scanPath);
13566
+ await walk9(scanPath);
12913
13567
  return found.sort();
12914
13568
  }
12915
13569
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
12916
13570
  let fallback = null;
12917
13571
  for (const file of hookFiles) {
12918
- const content = await import_node_fs28.promises.readFile(import_node_path61.default.join(scanPath, file), "utf8");
13572
+ const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(scanPath, file), "utf8");
12919
13573
  const patched = splicedContent(content, snippet2);
12920
13574
  if (patched !== null) return { file, content, patched };
12921
13575
  if (fallback === null) fallback = { file, content };
@@ -12923,11 +13577,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
12923
13577
  return { file: fallback.file, content: fallback.content, patched: null };
12924
13578
  }
12925
13579
  function extendLogPath() {
12926
- return process.env.NEAT_EXTEND_LOG ?? import_node_path61.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
13580
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path62.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
12927
13581
  }
12928
13582
  async function appendExtendLog(entry) {
12929
13583
  const logPath = extendLogPath();
12930
- await import_node_fs28.promises.mkdir(import_node_path61.default.dirname(logPath), { recursive: true });
13584
+ await import_node_fs28.promises.mkdir(import_node_path62.default.dirname(logPath), { recursive: true });
12931
13585
  await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
12932
13586
  }
12933
13587
  function splicedContent(fileContent, snippet2) {
@@ -12986,7 +13640,7 @@ function lookupInstrumentation(library, installedVersion) {
12986
13640
  }
12987
13641
  async function describeProjectInstrumentation(ctx) {
12988
13642
  const hookFiles = await findHookFiles(ctx.scanPath);
12989
- const envNeat = await fileExists2(import_node_path61.default.join(ctx.scanPath, ".env.neat"));
13643
+ const envNeat = await fileExists2(import_node_path62.default.join(ctx.scanPath, ".env.neat"));
12990
13644
  const registryInstrPackages = new Set(
12991
13645
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
12992
13646
  );
@@ -13008,7 +13662,7 @@ async function applyExtension(ctx, args, options) {
13008
13662
  );
13009
13663
  }
13010
13664
  for (const file of hookFiles) {
13011
- const content = await import_node_fs28.promises.readFile(import_node_path61.default.join(ctx.scanPath, file), "utf8");
13665
+ const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(ctx.scanPath, file), "utf8");
13012
13666
  if (content.includes(args.registration_snippet)) {
13013
13667
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
13014
13668
  }
@@ -13020,10 +13674,10 @@ async function applyExtension(ctx, args, options) {
13020
13674
  );
13021
13675
  }
13022
13676
  const primaryFile = primary.file;
13023
- const primaryPath = import_node_path61.default.join(ctx.scanPath, primaryFile);
13677
+ const primaryPath = import_node_path62.default.join(ctx.scanPath, primaryFile);
13024
13678
  const filesTouched = [];
13025
13679
  const depsAdded = [];
13026
- const pkgPath = import_node_path61.default.join(ctx.scanPath, "package.json");
13680
+ const pkgPath = import_node_path62.default.join(ctx.scanPath, "package.json");
13027
13681
  const pkg = await readPackageJson(ctx.scanPath);
13028
13682
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
13029
13683
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -13062,7 +13716,7 @@ async function dryRunExtension(ctx, args) {
13062
13716
  };
13063
13717
  }
13064
13718
  for (const file of hookFiles) {
13065
- const content = await import_node_fs28.promises.readFile(import_node_path61.default.join(ctx.scanPath, file), "utf8");
13719
+ const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(ctx.scanPath, file), "utf8");
13066
13720
  if (content.includes(args.registration_snippet)) {
13067
13721
  return {
13068
13722
  library: args.library,
@@ -13103,7 +13757,7 @@ async function rollbackExtension(ctx, args) {
13103
13757
  if (!match) {
13104
13758
  return { undone: false, message: "no apply found for library" };
13105
13759
  }
13106
- const pkgPath = import_node_path61.default.join(ctx.scanPath, "package.json");
13760
+ const pkgPath = import_node_path62.default.join(ctx.scanPath, "package.json");
13107
13761
  if (await fileExists2(pkgPath)) {
13108
13762
  const pkg = await readPackageJson(ctx.scanPath);
13109
13763
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -13114,7 +13768,7 @@ async function rollbackExtension(ctx, args) {
13114
13768
  }
13115
13769
  const hookFiles = await findHookFiles(ctx.scanPath);
13116
13770
  for (const file of hookFiles) {
13117
- const filePath = import_node_path61.default.join(ctx.scanPath, file);
13771
+ const filePath = import_node_path62.default.join(ctx.scanPath, file);
13118
13772
  const content = await import_node_fs28.promises.readFile(filePath, "utf8");
13119
13773
  if (content.includes(match.registration_snippet)) {
13120
13774
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -13130,39 +13784,39 @@ async function rollbackExtension(ctx, args) {
13130
13784
 
13131
13785
  // src/divergences.ts
13132
13786
  init_cjs_shims();
13133
- var import_types50 = require("@neat.is/types");
13787
+ var import_types51 = require("@neat.is/types");
13134
13788
  function bucketKey(source, target, type) {
13135
13789
  return `${type}|${source}|${target}`;
13136
13790
  }
13137
13791
  function bucketSourceFor(graph, edge) {
13138
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) return edge.source;
13139
- const parsed = (0, import_types50.parseFileId)(edge.source);
13792
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) return edge.source;
13793
+ const parsed = (0, import_types51.parseFileId)(edge.source);
13140
13794
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
13141
13795
  const target = graph.getNodeAttributes(edge.target);
13142
- if (target.type !== import_types50.NodeType.DatabaseNode) return edge.source;
13143
- return (0, import_types50.serviceId)(parsed.service);
13796
+ if (target.type !== import_types51.NodeType.DatabaseNode) return edge.source;
13797
+ return (0, import_types51.serviceId)(parsed.service);
13144
13798
  }
13145
13799
  function bucketEdges(graph) {
13146
13800
  const buckets2 = /* @__PURE__ */ new Map();
13147
13801
  graph.forEachEdge((id, attrs) => {
13148
13802
  const e = attrs;
13149
- const parsed = (0, import_types50.parseEdgeId)(id);
13803
+ const parsed = (0, import_types51.parseEdgeId)(id);
13150
13804
  const provenance = parsed?.provenance ?? e.provenance;
13151
13805
  const source = bucketSourceFor(graph, e);
13152
13806
  const key = bucketKey(source, e.target, e.type);
13153
13807
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
13154
13808
  switch (provenance) {
13155
- case import_types50.Provenance.EXTRACTED:
13809
+ case import_types51.Provenance.EXTRACTED:
13156
13810
  cur.extracted = e;
13157
13811
  break;
13158
- case import_types50.Provenance.OBSERVED:
13812
+ case import_types51.Provenance.OBSERVED:
13159
13813
  cur.observed = e;
13160
13814
  break;
13161
- case import_types50.Provenance.INFERRED:
13815
+ case import_types51.Provenance.INFERRED:
13162
13816
  cur.inferred = e;
13163
13817
  break;
13164
13818
  default:
13165
- if (e.provenance === import_types50.Provenance.STALE) cur.stale = e;
13819
+ if (e.provenance === import_types51.Provenance.STALE) cur.stale = e;
13166
13820
  }
13167
13821
  buckets2.set(key, cur);
13168
13822
  });
@@ -13171,22 +13825,22 @@ function bucketEdges(graph) {
13171
13825
  function nodeIsFrontier(graph, nodeId) {
13172
13826
  if (!graph.hasNode(nodeId)) return false;
13173
13827
  const attrs = graph.getNodeAttributes(nodeId);
13174
- return attrs.type === import_types50.NodeType.FrontierNode;
13828
+ return attrs.type === import_types51.NodeType.FrontierNode;
13175
13829
  }
13176
13830
  function nodeIsWebsocketChannel(graph, nodeId) {
13177
13831
  if (!graph.hasNode(nodeId)) return false;
13178
13832
  const attrs = graph.getNodeAttributes(nodeId);
13179
- return attrs.type === import_types50.NodeType.WebSocketChannelNode;
13833
+ return attrs.type === import_types51.NodeType.WebSocketChannelNode;
13180
13834
  }
13181
13835
  function nodeIsServerAction(graph, nodeId) {
13182
13836
  if (!graph.hasNode(nodeId)) return false;
13183
13837
  const attrs = graph.getNodeAttributes(nodeId);
13184
- return attrs.type === import_types50.NodeType.ServerActionNode;
13838
+ return attrs.type === import_types51.NodeType.ServerActionNode;
13185
13839
  }
13186
13840
  function nodeIsSymbol(graph, nodeId) {
13187
13841
  if (!graph.hasNode(nodeId)) return false;
13188
13842
  const attrs = graph.getNodeAttributes(nodeId);
13189
- return attrs.type === import_types50.NodeType.SymbolNode;
13843
+ return attrs.type === import_types51.NodeType.SymbolNode;
13190
13844
  }
13191
13845
  function clampConfidence(n) {
13192
13846
  if (!Number.isFinite(n)) return 0;
@@ -13206,14 +13860,14 @@ function gradedConfidence(edge) {
13206
13860
  return clampConfidence(confidenceForEdge(edge));
13207
13861
  }
13208
13862
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
13209
- import_types50.EdgeType.CALLS,
13210
- import_types50.EdgeType.CONNECTS_TO,
13211
- import_types50.EdgeType.PUBLISHES_TO,
13212
- import_types50.EdgeType.CONSUMES_FROM
13863
+ import_types51.EdgeType.CALLS,
13864
+ import_types51.EdgeType.CONNECTS_TO,
13865
+ import_types51.EdgeType.PUBLISHES_TO,
13866
+ import_types51.EdgeType.CONSUMES_FROM
13213
13867
  ]);
13214
13868
  function detectMissingDivergences(graph, bucket) {
13215
13869
  const out = [];
13216
- if (bucket.type === import_types50.EdgeType.CONTAINS) return out;
13870
+ if (bucket.type === import_types51.EdgeType.CONTAINS) return out;
13217
13871
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
13218
13872
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
13219
13873
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -13255,7 +13909,7 @@ function declaredHostFor(svc) {
13255
13909
  function hasExtractedConfiguredBy(graph, svcId) {
13256
13910
  for (const edgeId of graph.outboundEdges(svcId)) {
13257
13911
  const e = graph.getEdgeAttributes(edgeId);
13258
- if (e.type === import_types50.EdgeType.CONFIGURED_BY && e.provenance === import_types50.Provenance.EXTRACTED) {
13912
+ if (e.type === import_types51.EdgeType.CONFIGURED_BY && e.provenance === import_types51.Provenance.EXTRACTED) {
13259
13913
  return true;
13260
13914
  }
13261
13915
  }
@@ -13268,10 +13922,10 @@ function detectHostMismatch(graph, svcId, svc) {
13268
13922
  const out = [];
13269
13923
  for (const edgeId of graph.outboundEdges(svcId)) {
13270
13924
  const edge = graph.getEdgeAttributes(edgeId);
13271
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13272
- if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
13925
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) continue;
13926
+ if (edge.provenance !== import_types51.Provenance.OBSERVED) continue;
13273
13927
  const target = graph.getNodeAttributes(edge.target);
13274
- if (target.type !== import_types50.NodeType.DatabaseNode) continue;
13928
+ if (target.type !== import_types51.NodeType.DatabaseNode) continue;
13275
13929
  const observedHost = target.host?.trim();
13276
13930
  if (!observedHost) continue;
13277
13931
  if (observedHost === declaredHost) continue;
@@ -13293,10 +13947,10 @@ function detectCompatDivergences(graph, svcId, svc) {
13293
13947
  const deps = svc.dependencies ?? {};
13294
13948
  for (const edgeId of graph.outboundEdges(svcId)) {
13295
13949
  const edge = graph.getEdgeAttributes(edgeId);
13296
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13297
- if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
13950
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) continue;
13951
+ if (edge.provenance !== import_types51.Provenance.OBSERVED) continue;
13298
13952
  const target = graph.getNodeAttributes(edge.target);
13299
- if (target.type !== import_types50.NodeType.DatabaseNode) continue;
13953
+ if (target.type !== import_types51.NodeType.DatabaseNode) continue;
13300
13954
  for (const pair of compatPairs()) {
13301
13955
  if (pair.engine !== target.engine) continue;
13302
13956
  const declared = deps[pair.driver];
@@ -13393,7 +14047,7 @@ function suppressHostMismatchHalves(all) {
13393
14047
  for (const d of all) {
13394
14048
  if (d.type !== "host-mismatch") continue;
13395
14049
  observedHalf.add(`${d.source}->${d.target}`);
13396
- declaredHalf.add((0, import_types50.databaseId)(d.extractedHost));
14050
+ declaredHalf.add((0, import_types51.databaseId)(d.extractedHost));
13397
14051
  }
13398
14052
  if (observedHalf.size === 0) return all;
13399
14053
  return all.filter((d) => {
@@ -13412,13 +14066,13 @@ function computeDivergences(graph, opts = {}) {
13412
14066
  }
13413
14067
  graph.forEachNode((nodeId, attrs) => {
13414
14068
  const n = attrs;
13415
- if (n.type === import_types50.NodeType.ServiceNode) {
14069
+ if (n.type === import_types51.NodeType.ServiceNode) {
13416
14070
  const svc = n;
13417
14071
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
13418
14072
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
13419
14073
  return;
13420
14074
  }
13421
- if (n.type === import_types50.NodeType.InfraNode && n.kind === "sql-table") {
14075
+ if (n.type === import_types51.NodeType.InfraNode && n.kind === "sql-table") {
13422
14076
  for (const d of detectColumnDrift(n)) all.push(d);
13423
14077
  }
13424
14078
  });
@@ -13454,7 +14108,7 @@ function computeDivergences(graph, opts = {}) {
13454
14108
  const bc = "column" in b && b.column ? b.column : "";
13455
14109
  return ac.localeCompare(bc);
13456
14110
  });
13457
- return import_types50.DivergenceResultSchema.parse({
14111
+ return import_types51.DivergenceResultSchema.parse({
13458
14112
  divergences: filtered,
13459
14113
  totalAffected: filtered.length,
13460
14114
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13585,23 +14239,23 @@ function canonicalJson(value) {
13585
14239
 
13586
14240
  // src/projects.ts
13587
14241
  init_cjs_shims();
13588
- var import_node_path62 = __toESM(require("path"), 1);
14242
+ var import_node_path63 = __toESM(require("path"), 1);
13589
14243
  function pathsForProject(project, baseDir) {
13590
14244
  if (project === DEFAULT_PROJECT) {
13591
14245
  return {
13592
- snapshotPath: import_node_path62.default.join(baseDir, "graph.json"),
13593
- errorsPath: import_node_path62.default.join(baseDir, "errors.ndjson"),
13594
- staleEventsPath: import_node_path62.default.join(baseDir, "stale-events.ndjson"),
13595
- embeddingsCachePath: import_node_path62.default.join(baseDir, "embeddings.json"),
13596
- policyViolationsPath: import_node_path62.default.join(baseDir, "policy-violations.ndjson")
14246
+ snapshotPath: import_node_path63.default.join(baseDir, "graph.json"),
14247
+ errorsPath: import_node_path63.default.join(baseDir, "errors.ndjson"),
14248
+ staleEventsPath: import_node_path63.default.join(baseDir, "stale-events.ndjson"),
14249
+ embeddingsCachePath: import_node_path63.default.join(baseDir, "embeddings.json"),
14250
+ policyViolationsPath: import_node_path63.default.join(baseDir, "policy-violations.ndjson")
13597
14251
  };
13598
14252
  }
13599
14253
  return {
13600
- snapshotPath: import_node_path62.default.join(baseDir, `${project}.json`),
13601
- errorsPath: import_node_path62.default.join(baseDir, `errors.${project}.ndjson`),
13602
- staleEventsPath: import_node_path62.default.join(baseDir, `stale-events.${project}.ndjson`),
13603
- embeddingsCachePath: import_node_path62.default.join(baseDir, `embeddings.${project}.json`),
13604
- policyViolationsPath: import_node_path62.default.join(baseDir, `policy-violations.${project}.ndjson`)
14254
+ snapshotPath: import_node_path63.default.join(baseDir, `${project}.json`),
14255
+ errorsPath: import_node_path63.default.join(baseDir, `errors.${project}.ndjson`),
14256
+ staleEventsPath: import_node_path63.default.join(baseDir, `stale-events.${project}.ndjson`),
14257
+ embeddingsCachePath: import_node_path63.default.join(baseDir, `embeddings.${project}.json`),
14258
+ policyViolationsPath: import_node_path63.default.join(baseDir, `policy-violations.${project}.ndjson`)
13605
14259
  };
13606
14260
  }
13607
14261
  var Projects = class {
@@ -13639,26 +14293,26 @@ var Projects = class {
13639
14293
  init_cjs_shims();
13640
14294
  var import_node_fs30 = require("fs");
13641
14295
  var import_node_os3 = __toESM(require("os"), 1);
13642
- var import_node_path63 = __toESM(require("path"), 1);
13643
- var import_types51 = require("@neat.is/types");
14296
+ var import_node_path64 = __toESM(require("path"), 1);
14297
+ var import_types52 = require("@neat.is/types");
13644
14298
  var LOCK_TIMEOUT_MS = 5e3;
13645
14299
  var LOCK_RETRY_MS = 50;
13646
14300
  function neatHome() {
13647
14301
  const override = process.env.NEAT_HOME;
13648
- if (override && override.length > 0) return import_node_path63.default.resolve(override);
13649
- return import_node_path63.default.join(import_node_os3.default.homedir(), ".neat");
14302
+ if (override && override.length > 0) return import_node_path64.default.resolve(override);
14303
+ return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
13650
14304
  }
13651
14305
  function registryPath() {
13652
- return import_node_path63.default.join(neatHome(), "projects.json");
14306
+ return import_node_path64.default.join(neatHome(), "projects.json");
13653
14307
  }
13654
14308
  function registryLockPath() {
13655
- return import_node_path63.default.join(neatHome(), "projects.json.lock");
14309
+ return import_node_path64.default.join(neatHome(), "projects.json.lock");
13656
14310
  }
13657
14311
  function daemonPidPath() {
13658
- return import_node_path63.default.join(neatHome(), "neatd.pid");
14312
+ return import_node_path64.default.join(neatHome(), "neatd.pid");
13659
14313
  }
13660
14314
  function daemonsDir() {
13661
- return import_node_path63.default.join(neatHome(), "daemons");
14315
+ return import_node_path64.default.join(neatHome(), "daemons");
13662
14316
  }
13663
14317
  function isFiniteInt(v) {
13664
14318
  return typeof v === "number" && Number.isFinite(v);
@@ -13699,7 +14353,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
13699
14353
  const out = [];
13700
14354
  for (const name of names) {
13701
14355
  if (!name.endsWith(".json")) continue;
13702
- const file = import_node_path63.default.join(dir, name);
14356
+ const file = import_node_path64.default.join(dir, name);
13703
14357
  let raw;
13704
14358
  try {
13705
14359
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -13776,7 +14430,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
13776
14430
  }
13777
14431
  }
13778
14432
  async function normalizeProjectPath(input) {
13779
- const resolved = import_node_path63.default.resolve(input);
14433
+ const resolved = import_node_path64.default.resolve(input);
13780
14434
  try {
13781
14435
  return await import_node_fs30.promises.realpath(resolved);
13782
14436
  } catch {
@@ -13784,7 +14438,7 @@ async function normalizeProjectPath(input) {
13784
14438
  }
13785
14439
  }
13786
14440
  async function writeAtomically(target, contents) {
13787
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(target), { recursive: true });
14441
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(target), { recursive: true });
13788
14442
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
13789
14443
  const fd = await import_node_fs30.promises.open(tmp, "w");
13790
14444
  try {
@@ -13797,7 +14451,7 @@ async function writeAtomically(target, contents) {
13797
14451
  }
13798
14452
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
13799
14453
  const deadline = Date.now() + timeoutMs;
13800
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(lockPath), { recursive: true });
14454
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(lockPath), { recursive: true });
13801
14455
  let probedHolder = false;
13802
14456
  while (true) {
13803
14457
  try {
@@ -13850,10 +14504,10 @@ async function readRegistry() {
13850
14504
  throw err;
13851
14505
  }
13852
14506
  const parsed = JSON.parse(raw);
13853
- return import_types51.RegistryFileSchema.parse(parsed);
14507
+ return import_types52.RegistryFileSchema.parse(parsed);
13854
14508
  }
13855
14509
  async function writeRegistry(reg) {
13856
- const validated = import_types51.RegistryFileSchema.parse(reg);
14510
+ const validated = import_types52.RegistryFileSchema.parse(reg);
13857
14511
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
13858
14512
  }
13859
14513
  var ProjectNameCollisionError = class extends Error {
@@ -14048,7 +14702,7 @@ init_auth();
14048
14702
  // src/connectors-config.ts
14049
14703
  init_cjs_shims();
14050
14704
  var import_node_os4 = __toESM(require("os"), 1);
14051
- var import_node_path64 = __toESM(require("path"), 1);
14705
+ var import_node_path65 = __toESM(require("path"), 1);
14052
14706
  var import_node_fs31 = require("fs");
14053
14707
  var CONNECTORS_CONFIG_VERSION = 1;
14054
14708
  var EnvRefUnsetError = class extends Error {
@@ -14063,11 +14717,11 @@ var EnvRefUnsetError = class extends Error {
14063
14717
  };
14064
14718
  function neatHome2() {
14065
14719
  const override = process.env.NEAT_HOME;
14066
- if (override && override.length > 0) return import_node_path64.default.resolve(override);
14067
- return import_node_path64.default.join(import_node_os4.default.homedir(), ".neat");
14720
+ if (override && override.length > 0) return import_node_path65.default.resolve(override);
14721
+ return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
14068
14722
  }
14069
14723
  function connectorsConfigPath(home = neatHome2()) {
14070
- return import_node_path64.default.join(home, "connectors.json");
14724
+ return import_node_path65.default.join(home, "connectors.json");
14071
14725
  }
14072
14726
  var MODE_MASK_LOOSER_THAN_0600 = 63;
14073
14727
  async function warnIfModeLooserThan0600(file) {
@@ -14254,15 +14908,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
14254
14908
 
14255
14909
  // src/connectors/index.ts
14256
14910
  init_cjs_shims();
14257
- var import_types52 = require("@neat.is/types");
14911
+ var import_types53 = require("@neat.is/types");
14258
14912
  var NO_ENV = "unknown";
14259
14913
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
14260
14914
  if (!graph.hasNode(targetNodeId)) return void 0;
14261
14915
  const sites = [];
14262
14916
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
14263
14917
  const edge = graph.getEdgeAttributes(edgeId);
14264
- if (edge.provenance !== import_types52.Provenance.EXTRACTED) continue;
14265
- const parsed = (0, import_types52.parseFileId)(edge.source);
14918
+ if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
14919
+ const parsed = (0, import_types53.parseFileId)(edge.source);
14266
14920
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
14267
14921
  const site = { relPath: edge.evidence.file };
14268
14922
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -14273,7 +14927,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
14273
14927
  function routeCallSiteFor(graph, targetNodeId) {
14274
14928
  if (!graph.hasNode(targetNodeId)) return void 0;
14275
14929
  const attrs = graph.getNodeAttributes(targetNodeId);
14276
- if (attrs.type !== import_types52.NodeType.RouteNode || !attrs.path) return void 0;
14930
+ if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
14277
14931
  const site = { relPath: attrs.path };
14278
14932
  if (attrs.line !== void 0) site.line = attrs.line;
14279
14933
  return site;
@@ -14754,10 +15408,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
14754
15408
  // src/connectors/supabase/map.ts
14755
15409
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
14756
15410
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
14757
- function targetFromRestPath(path67) {
14758
- const rpcMatch = REST_RPC_PATH_RE.exec(path67);
15411
+ function targetFromRestPath(path68) {
15412
+ const rpcMatch = REST_RPC_PATH_RE.exec(path68);
14759
15413
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
14760
- const tableMatch = REST_TABLE_PATH_RE.exec(path67);
15414
+ const tableMatch = REST_TABLE_PATH_RE.exec(path68);
14761
15415
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
14762
15416
  return null;
14763
15417
  }
@@ -14868,23 +15522,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
14868
15522
 
14869
15523
  // src/connectors/supabase/resolve.ts
14870
15524
  init_cjs_shims();
14871
- var import_types54 = require("@neat.is/types");
15525
+ var import_types55 = require("@neat.is/types");
14872
15526
  function createSupabaseResolveTarget(graph, config) {
14873
15527
  return (signal, _ctx) => {
14874
15528
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
14875
15529
  return null;
14876
15530
  }
14877
- const subResourceId = (0, import_types54.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15531
+ const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
14878
15532
  if (graph.hasNode(subResourceId)) {
14879
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15533
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14880
15534
  }
14881
- const bareResourceId = (0, import_types54.infraId)(signal.targetKind, signal.targetName);
15535
+ const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
14882
15536
  if (graph.hasNode(bareResourceId)) {
14883
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15537
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14884
15538
  }
14885
- const projectLevelId = (0, import_types54.infraId)("supabase", config.nodeRef);
15539
+ const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
14886
15540
  if (graph.hasNode(projectLevelId)) {
14887
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15541
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14888
15542
  }
14889
15543
  return null;
14890
15544
  };
@@ -14977,7 +15631,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
14977
15631
 
14978
15632
  // src/connectors/railway/index.ts
14979
15633
  init_cjs_shims();
14980
- var import_types58 = require("@neat.is/types");
15634
+ var import_types59 = require("@neat.is/types");
14981
15635
 
14982
15636
  // src/connectors/railway/client.ts
14983
15637
  init_cjs_shims();
@@ -15128,7 +15782,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
15128
15782
  const out = [];
15129
15783
  graph.forEachNode((_id, attrs) => {
15130
15784
  const node = attrs;
15131
- if (node.type !== import_types58.NodeType.RouteNode) return;
15785
+ if (node.type !== import_types59.NodeType.RouteNode) return;
15132
15786
  const route = attrs;
15133
15787
  if (route.service !== serviceName) return;
15134
15788
  out.push({
@@ -15232,12 +15886,12 @@ function createRailwayResolveTarget(config) {
15232
15886
  const serviceName = config.serviceNameById[config.serviceId];
15233
15887
  if (!serviceName) return null;
15234
15888
  if (signal.targetKind === ROUTE_TARGET_KIND) {
15235
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types58.EdgeType.CALLS };
15889
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
15236
15890
  }
15237
15891
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
15238
15892
  const peerName = config.serviceNameById[signal.targetName];
15239
15893
  if (!peerName) return null;
15240
- return { targetNodeId: (0, import_types58.serviceId)(peerName), serviceName, edgeType: import_types58.EdgeType.CONNECTS_TO };
15894
+ return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
15241
15895
  }
15242
15896
  return null;
15243
15897
  };
@@ -15361,9 +16015,9 @@ function parseFirebaseTargetName(targetName) {
15361
16015
  const secondSep = rest.indexOf(FIELD_SEP);
15362
16016
  if (secondSep === -1) return null;
15363
16017
  const method = rest.slice(0, secondSep);
15364
- const path67 = rest.slice(secondSep + 1);
15365
- if (!resourceName || !method || !path67) return null;
15366
- return { resourceName, method, path: path67 };
16018
+ const path68 = rest.slice(secondSep + 1);
16019
+ if (!resourceName || !method || !path68) return null;
16020
+ return { resourceName, method, path: path68 };
15367
16021
  }
15368
16022
  function resourceNameFor(type, labels) {
15369
16023
  if (!labels) return null;
@@ -15401,14 +16055,14 @@ function mapLogEntryToSignal(entry) {
15401
16055
  if (!req) return null;
15402
16056
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
15403
16057
  const method = req.requestMethod.toUpperCase();
15404
- const path67 = pathFromRequestUrl(req.requestUrl);
15405
- if (path67 === null) return null;
16058
+ const path68 = pathFromRequestUrl(req.requestUrl);
16059
+ if (path68 === null) return null;
15406
16060
  const timestamp = entry.timestamp;
15407
16061
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15408
16062
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
15409
16063
  return {
15410
16064
  targetKind: resourceType,
15411
- targetName: packFirebaseTargetName({ resourceName, method, path: path67 }),
16065
+ targetName: packFirebaseTargetName({ resourceName, method, path: path68 }),
15412
16066
  callCount: 1,
15413
16067
  errorCount: isError ? 1 : 0,
15414
16068
  lastObservedIso: timestamp
@@ -15425,7 +16079,7 @@ function mapLogEntriesToSignals(entries) {
15425
16079
 
15426
16080
  // src/connectors/firebase/resolve.ts
15427
16081
  init_cjs_shims();
15428
- var import_types59 = require("@neat.is/types");
16082
+ var import_types60 = require("@neat.is/types");
15429
16083
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
15430
16084
  switch (resourceType) {
15431
16085
  case "cloud_function":
@@ -15440,7 +16094,7 @@ function routeEntriesFor(graph, serviceName) {
15440
16094
  const entries = [];
15441
16095
  graph.forEachNode((_id, attrs) => {
15442
16096
  const node = attrs;
15443
- if (node.type !== import_types59.NodeType.RouteNode) return;
16097
+ if (node.type !== import_types60.NodeType.RouteNode) return;
15444
16098
  const route = attrs;
15445
16099
  if (route.service !== serviceName) return;
15446
16100
  entries.push({
@@ -15472,7 +16126,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
15472
16126
  return {
15473
16127
  targetNodeId: match.routeNodeId,
15474
16128
  serviceName,
15475
- edgeType: import_types59.EdgeType.CALLS
16129
+ edgeType: import_types60.EdgeType.CALLS
15476
16130
  };
15477
16131
  };
15478
16132
  }
@@ -15499,7 +16153,7 @@ init_cjs_shims();
15499
16153
 
15500
16154
  // src/connectors/cloudflare/connector.ts
15501
16155
  init_cjs_shims();
15502
- var import_types61 = require("@neat.is/types");
16156
+ var import_types62 = require("@neat.is/types");
15503
16157
 
15504
16158
  // src/connectors/cloudflare/client.ts
15505
16159
  init_cjs_shims();
@@ -15615,7 +16269,7 @@ function mapEventToSignal(event) {
15615
16269
  if (Number.isNaN(observedAt.getTime())) return null;
15616
16270
  const statusCode = metadata?.statusCode;
15617
16271
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
15618
- const path67 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
16272
+ const path68 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15619
16273
  return {
15620
16274
  targetKind: CLOUDFLARE_TARGET_KIND,
15621
16275
  targetName: scriptName,
@@ -15623,7 +16277,7 @@ function mapEventToSignal(event) {
15623
16277
  errorCount: isError ? 1 : 0,
15624
16278
  lastObservedIso: observedAt.toISOString(),
15625
16279
  method,
15626
- ...path67 ? { path: path67 } : {},
16280
+ ...path68 ? { path: path68 } : {},
15627
16281
  ...typeof statusCode === "number" ? { statusCode } : {},
15628
16282
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
15629
16283
  };
@@ -15663,19 +16317,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
15663
16317
  graph.forEachNode((id, attrs) => {
15664
16318
  if (found) return;
15665
16319
  const a = attrs;
15666
- if (a.type === import_types61.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
16320
+ if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15667
16321
  found = id;
15668
16322
  }
15669
16323
  });
15670
16324
  return found;
15671
16325
  }
15672
- function findMatchingRouteNode(graph, serviceName, method, path67) {
15673
- const normalizedPath = normalizePathTemplate(path67);
16326
+ function findMatchingRouteNode(graph, serviceName, method, path68) {
16327
+ const normalizedPath = normalizePathTemplate(path68);
15674
16328
  let found = null;
15675
16329
  graph.forEachNode((id, attrs) => {
15676
16330
  if (found) return;
15677
16331
  const a = attrs;
15678
- if (a.type !== import_types61.NodeType.RouteNode || a.service !== serviceName) return;
16332
+ if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
15679
16333
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
15680
16334
  const routeMethod = (a.method ?? "").toUpperCase();
15681
16335
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -15687,18 +16341,18 @@ function createCloudflareResolveTarget(config, graph) {
15687
16341
  return (signal) => {
15688
16342
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
15689
16343
  const scriptName = signal.targetName;
15690
- const { method, path: path67 } = signal;
16344
+ const { method, path: path68 } = signal;
15691
16345
  const resolveRouteGrain = (serviceName, wholeFileId) => {
15692
- if (!method || !path67) return wholeFileId;
15693
- return findMatchingRouteNode(graph, serviceName, method, path67) ?? wholeFileId;
16346
+ if (!method || !path68) return wholeFileId;
16347
+ return findMatchingRouteNode(graph, serviceName, method, path68) ?? wholeFileId;
15694
16348
  };
15695
16349
  const mapping = config.workers?.[scriptName];
15696
16350
  if (mapping) {
15697
- const wholeFileId = (0, import_types61.fileId)(mapping.service, mapping.entryFile);
16351
+ const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
15698
16352
  return {
15699
16353
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
15700
16354
  serviceName: mapping.service,
15701
- edgeType: import_types61.EdgeType.CALLS
16355
+ edgeType: import_types62.EdgeType.CALLS
15702
16356
  };
15703
16357
  }
15704
16358
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -15707,13 +16361,13 @@ function createCloudflareResolveTarget(config, graph) {
15707
16361
  return {
15708
16362
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
15709
16363
  serviceName: fileNode.service,
15710
- edgeType: import_types61.EdgeType.CALLS
16364
+ edgeType: import_types62.EdgeType.CALLS
15711
16365
  };
15712
16366
  }
15713
16367
  return {
15714
- targetNodeId: (0, import_types61.infraId)("cloudflare-worker", scriptName),
16368
+ targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
15715
16369
  serviceName: scriptName,
15716
- edgeType: import_types61.EdgeType.CALLS,
16370
+ edgeType: import_types62.EdgeType.CALLS,
15717
16371
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
15718
16372
  };
15719
16373
  };
@@ -15909,14 +16563,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
15909
16563
 
15910
16564
  // src/connectors/neon/resolve.ts
15911
16565
  init_cjs_shims();
15912
- var import_types65 = require("@neat.is/types");
16566
+ var import_types66 = require("@neat.is/types");
15913
16567
  function createNeonResolveTarget(config) {
15914
16568
  return (signal) => {
15915
16569
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
15916
16570
  return {
15917
- targetNodeId: (0, import_types65.infraId)("sql-table", signal.targetName),
16571
+ targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
15918
16572
  serviceName: config.serviceName,
15919
- edgeType: import_types65.EdgeType.CALLS,
16573
+ edgeType: import_types66.EdgeType.CALLS,
15920
16574
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
15921
16575
  };
15922
16576
  };
@@ -16042,9 +16696,9 @@ function parseCloudRunTargetName(targetName) {
16042
16696
  const secondSep = rest.indexOf(FIELD_SEP2);
16043
16697
  if (secondSep === -1) return null;
16044
16698
  const method = rest.slice(0, secondSep);
16045
- const path67 = rest.slice(secondSep + 1);
16046
- if (!serviceName || !method || !path67) return null;
16047
- return { serviceName, method, path: path67 };
16699
+ const path68 = rest.slice(secondSep + 1);
16700
+ if (!serviceName || !method || !path68) return null;
16701
+ return { serviceName, method, path: path68 };
16048
16702
  }
16049
16703
 
16050
16704
  // src/connectors/cloud-run/map.ts
@@ -16073,14 +16727,14 @@ function mapLogEntryToSignal2(entry) {
16073
16727
  if (!req) return null;
16074
16728
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
16075
16729
  const method = req.requestMethod.toUpperCase();
16076
- const path67 = pathFromRequestUrl2(req.requestUrl);
16077
- if (path67 === null) return null;
16730
+ const path68 = pathFromRequestUrl2(req.requestUrl);
16731
+ if (path68 === null) return null;
16078
16732
  const timestamp = entry.timestamp;
16079
16733
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
16080
16734
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
16081
16735
  return {
16082
16736
  targetKind: CLOUD_RUN_TARGET_KIND,
16083
- targetName: packCloudRunTargetName({ serviceName, method, path: path67 }),
16737
+ targetName: packCloudRunTargetName({ serviceName, method, path: path68 }),
16084
16738
  callCount: 1,
16085
16739
  errorCount: isError ? 1 : 0,
16086
16740
  lastObservedIso: timestamp
@@ -16097,14 +16751,14 @@ function mapLogEntriesToSignals2(entries) {
16097
16751
 
16098
16752
  // src/connectors/cloud-run/resolve.ts
16099
16753
  init_cjs_shims();
16100
- var import_types69 = require("@neat.is/types");
16754
+ var import_types70 = require("@neat.is/types");
16101
16755
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
16102
16756
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
16103
16757
  let found = null;
16104
16758
  graph.forEachNode((_id, attrs) => {
16105
16759
  if (found) return;
16106
16760
  const node = attrs;
16107
- if (node.type !== import_types69.NodeType.RouteNode) return;
16761
+ if (node.type !== import_types70.NodeType.RouteNode) return;
16108
16762
  const route = attrs;
16109
16763
  if (route.service !== serviceName || !route.pathTemplate) return;
16110
16764
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -16119,23 +16773,23 @@ function createCloudRunResolveTarget(graph, config) {
16119
16773
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
16120
16774
  const identity = parseCloudRunTargetName(signal.targetName);
16121
16775
  if (!identity) return null;
16122
- const { serviceName: gcpServiceName, method, path: path67 } = identity;
16776
+ const { serviceName: gcpServiceName, method, path: path68 } = identity;
16123
16777
  const mappedService = config.serviceMap?.[gcpServiceName];
16124
16778
  if (mappedService) {
16125
16779
  const routeNodeId = findMatchingRouteNode2(
16126
16780
  graph,
16127
16781
  mappedService,
16128
16782
  method,
16129
- normalizePathTemplate(path67)
16783
+ normalizePathTemplate(path68)
16130
16784
  );
16131
16785
  if (routeNodeId) {
16132
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types69.EdgeType.CALLS };
16786
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
16133
16787
  }
16134
16788
  }
16135
16789
  return {
16136
- targetNodeId: (0, import_types69.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16790
+ targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16137
16791
  serviceName: mappedService ?? gcpServiceName,
16138
- edgeType: import_types69.EdgeType.CALLS,
16792
+ edgeType: import_types70.EdgeType.CALLS,
16139
16793
  ensureInfraNode: {
16140
16794
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
16141
16795
  name: gcpServiceName,
@@ -16176,7 +16830,7 @@ function createCloudRunConnector(graph, config = {}) {
16176
16830
 
16177
16831
  // src/connectors/render/index.ts
16178
16832
  init_cjs_shims();
16179
- var import_types72 = require("@neat.is/types");
16833
+ var import_types73 = require("@neat.is/types");
16180
16834
 
16181
16835
  // src/connectors/render/types.ts
16182
16836
  init_cjs_shims();
@@ -16254,7 +16908,7 @@ function buildRenderRouteIndex(graph, serviceName) {
16254
16908
  const out = [];
16255
16909
  graph.forEachNode((_id, attrs) => {
16256
16910
  const node = attrs;
16257
- if (node.type !== import_types72.NodeType.RouteNode) return;
16911
+ if (node.type !== import_types73.NodeType.RouteNode) return;
16258
16912
  const route = attrs;
16259
16913
  if (route.service !== serviceName) return;
16260
16914
  out.push({
@@ -16339,7 +16993,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
16339
16993
  function createRenderResolveTarget(config) {
16340
16994
  return (signal) => {
16341
16995
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
16342
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types72.EdgeType.CALLS };
16996
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
16343
16997
  }
16344
16998
  return null;
16345
16999
  };
@@ -16477,21 +17131,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
16477
17131
 
16478
17132
  // src/connectors/planetscale/resolve.ts
16479
17133
  init_cjs_shims();
16480
- var import_types76 = require("@neat.is/types");
17134
+ var import_types77 = require("@neat.is/types");
16481
17135
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
16482
17136
  function createPlanetscaleResolveTarget(graph, config) {
16483
17137
  const databaseName = `${config.organization}/${config.database}`;
16484
17138
  return (signal, _ctx) => {
16485
17139
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16486
- const tableId = (0, import_types76.infraId)("sql-table", signal.targetName);
17140
+ const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
16487
17141
  if (graph.hasNode(tableId)) {
16488
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types76.EdgeType.CALLS };
17142
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
16489
17143
  }
16490
- const providerId = (0, import_types76.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
17144
+ const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16491
17145
  return {
16492
17146
  targetNodeId: providerId,
16493
17147
  serviceName: config.serviceName,
16494
- edgeType: import_types76.EdgeType.CALLS,
17148
+ edgeType: import_types77.EdgeType.CALLS,
16495
17149
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
16496
17150
  };
16497
17151
  };
@@ -17161,11 +17815,11 @@ function registerRoutes(scope, ctx) {
17161
17815
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
17162
17816
  const parsed = [];
17163
17817
  for (const c of candidates) {
17164
- const r = import_types79.DivergenceTypeSchema.safeParse(c);
17818
+ const r = import_types80.DivergenceTypeSchema.safeParse(c);
17165
17819
  if (!r.success) {
17166
17820
  return reply.code(400).send({
17167
17821
  error: `unknown divergence type "${c}"`,
17168
- allowed: import_types79.DivergenceTypeSchema.options
17822
+ allowed: import_types80.DivergenceTypeSchema.options
17169
17823
  });
17170
17824
  }
17171
17825
  parsed.push(r.data);
@@ -17474,7 +18128,7 @@ function registerRoutes(scope, ctx) {
17474
18128
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
17475
18129
  let violations = await log.readAll();
17476
18130
  if (req.query.severity) {
17477
- const sev = import_types79.PolicySeveritySchema.safeParse(req.query.severity);
18131
+ const sev = import_types80.PolicySeveritySchema.safeParse(req.query.severity);
17478
18132
  if (!sev.success) {
17479
18133
  return reply.code(400).send({
17480
18134
  error: "invalid severity",
@@ -17513,7 +18167,7 @@ function registerRoutes(scope, ctx) {
17513
18167
  scope.post("/policies/check", async (req, reply) => {
17514
18168
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
17515
18169
  if (!proj) return;
17516
- const parsed = import_types79.PoliciesCheckBodySchema.safeParse(req.body ?? {});
18170
+ const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17517
18171
  if (!parsed.success) {
17518
18172
  return reply.code(400).send({
17519
18173
  error: "invalid /policies/check body",
@@ -17835,7 +18489,7 @@ init_otel_grpc();
17835
18489
  // src/daemon.ts
17836
18490
  init_cjs_shims();
17837
18491
  var import_node_fs33 = require("fs");
17838
- var import_node_path66 = __toESM(require("path"), 1);
18492
+ var import_node_path67 = __toESM(require("path"), 1);
17839
18493
  var import_node_module = require("module");
17840
18494
  init_otel();
17841
18495
  init_auth();
@@ -17843,7 +18497,7 @@ init_auth();
17843
18497
  // src/unrouted.ts
17844
18498
  init_cjs_shims();
17845
18499
  var import_node_fs32 = require("fs");
17846
- var import_node_path65 = __toESM(require("path"), 1);
18500
+ var import_node_path66 = __toESM(require("path"), 1);
17847
18501
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
17848
18502
  return {
17849
18503
  timestamp: now.toISOString(),
@@ -17853,34 +18507,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
17853
18507
  };
17854
18508
  }
17855
18509
  async function appendUnroutedSpan(neatHome3, record) {
17856
- const target = import_node_path65.default.join(neatHome3, "errors.ndjson");
18510
+ const target = import_node_path66.default.join(neatHome3, "errors.ndjson");
17857
18511
  await import_node_fs32.promises.mkdir(neatHome3, { recursive: true });
17858
18512
  await import_node_fs32.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
17859
18513
  }
17860
18514
  function unroutedErrorsPath(neatHome3) {
17861
- return import_node_path65.default.join(neatHome3, "errors.ndjson");
18515
+ return import_node_path66.default.join(neatHome3, "errors.ndjson");
17862
18516
  }
17863
18517
 
17864
18518
  // src/daemon.ts
17865
- var import_types80 = require("@neat.is/types");
18519
+ var import_types81 = require("@neat.is/types");
17866
18520
  function daemonJsonPath(scanPath) {
17867
- return import_node_path66.default.join(scanPath, "neat-out", "daemon.json");
18521
+ return import_node_path67.default.join(scanPath, "neat-out", "daemon.json");
17868
18522
  }
17869
18523
  function daemonsDiscoveryDir(home) {
17870
18524
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
17871
- return import_node_path66.default.join(base, "daemons");
18525
+ return import_node_path67.default.join(base, "daemons");
17872
18526
  }
17873
18527
  function daemonDiscoveryPath(project, home) {
17874
- return import_node_path66.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
18528
+ return import_node_path67.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
17875
18529
  }
17876
18530
  function sanitizeDiscoveryName(project) {
17877
18531
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
17878
18532
  }
17879
18533
  function neatHomeFromEnv() {
17880
18534
  const env = process.env.NEAT_HOME;
17881
- if (env && env.length > 0) return import_node_path66.default.resolve(env);
18535
+ if (env && env.length > 0) return import_node_path67.default.resolve(env);
17882
18536
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17883
- return import_node_path66.default.join(home, ".neat");
18537
+ return import_node_path67.default.join(home, ".neat");
17884
18538
  }
17885
18539
  function resolveNeatVersion() {
17886
18540
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -17935,11 +18589,11 @@ function teardownSlot(slot) {
17935
18589
  }
17936
18590
  }
17937
18591
  function neatHomeFor(opts) {
17938
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path66.default.resolve(opts.neatHome);
18592
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path67.default.resolve(opts.neatHome);
17939
18593
  const env = process.env.NEAT_HOME;
17940
- if (env && env.length > 0) return import_node_path66.default.resolve(env);
18594
+ if (env && env.length > 0) return import_node_path67.default.resolve(env);
17941
18595
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17942
- return import_node_path66.default.join(home, ".neat");
18596
+ return import_node_path67.default.join(home, ".neat");
17943
18597
  }
17944
18598
  function routeSpanToProject(serviceName, projects) {
17945
18599
  if (!serviceName) return DEFAULT_PROJECT;
@@ -17987,11 +18641,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
17987
18641
  if (!serviceName) return true;
17988
18642
  if (serviceNameMatchesProject(serviceName, project)) return true;
17989
18643
  return graph.someNode(
17990
- (_id, attrs) => attrs.type === import_types80.NodeType.ServiceNode && attrs.name === serviceName
18644
+ (_id, attrs) => attrs.type === import_types81.NodeType.ServiceNode && attrs.name === serviceName
17991
18645
  );
17992
18646
  }
17993
18647
  async function bootstrapProject(entry, connectors = [], neatHome3) {
17994
- const paths = pathsForProject(entry.name, import_node_path66.default.join(entry.path, "neat-out"));
18648
+ const paths = pathsForProject(entry.name, import_node_path67.default.join(entry.path, "neat-out"));
17995
18649
  try {
17996
18650
  const stat = await import_node_fs33.promises.stat(entry.path);
17997
18651
  if (!stat.isDirectory()) {
@@ -18107,7 +18761,7 @@ async function startDaemon(opts = {}) {
18107
18761
  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;
18108
18762
  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;
18109
18763
  const singleProject = projectArg;
18110
- const singleProjectPath = singleProject && projectPathArg ? import_node_path66.default.resolve(projectPathArg) : null;
18764
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path67.default.resolve(projectPathArg) : null;
18111
18765
  if (singleProject && !singleProjectPath) {
18112
18766
  throw new Error(
18113
18767
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -18122,7 +18776,7 @@ async function startDaemon(opts = {}) {
18122
18776
  );
18123
18777
  }
18124
18778
  }
18125
- const pidPath = import_node_path66.default.join(home, "neatd.pid");
18779
+ const pidPath = import_node_path67.default.join(home, "neatd.pid");
18126
18780
  await writeAtomically(pidPath, `${process.pid}
18127
18781
  `);
18128
18782
  const slots = /* @__PURE__ */ new Map();
@@ -18534,8 +19188,8 @@ async function startDaemon(opts = {}) {
18534
19188
  let registryWatcher = null;
18535
19189
  let reloadTimer = null;
18536
19190
  if (!singleProject) try {
18537
- const regDir = import_node_path66.default.dirname(regPath);
18538
- const regBase = import_node_path66.default.basename(regPath);
19191
+ const regDir = import_node_path67.default.dirname(regPath);
19192
+ const regBase = import_node_path67.default.basename(regPath);
18539
19193
  registryWatcher = (0, import_node_fs33.watch)(regDir, (_eventType, filename) => {
18540
19194
  if (filename !== null && filename !== regBase) return;
18541
19195
  if (reloadTimer) clearTimeout(reloadTimer);