@neat.is/core 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.cjs CHANGED
@@ -59,8 +59,8 @@ function mountBearerAuth(app, opts) {
59
59
  ]);
60
60
  const publicRead = opts.publicRead === true;
61
61
  app.addHook("preHandler", (req, reply, done) => {
62
- const path58 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
- if (exactUnauthPaths.has(path58) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path58)) {
62
+ const path59 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
+ if (exactUnauthPaths.has(path59) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path59)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -192,8 +192,8 @@ function reshapeGrpcRequest(req) {
192
192
  };
193
193
  }
194
194
  function resolveProtoRoot() {
195
- const here = import_node_path39.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
- return import_node_path39.default.resolve(here, "..", "proto");
195
+ const here = import_node_path40.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
+ return import_node_path40.default.resolve(here, "..", "proto");
197
197
  }
198
198
  function loadTraceService() {
199
199
  const protoRoot = resolveProtoRoot();
@@ -261,13 +261,13 @@ async function startOtelGrpcReceiver(opts) {
261
261
  })
262
262
  };
263
263
  }
264
- var import_node_url, import_node_path39, import_node_crypto2, grpc, protoLoader;
264
+ var import_node_url, import_node_path40, import_node_crypto2, grpc, protoLoader;
265
265
  var init_otel_grpc = __esm({
266
266
  "src/otel-grpc.ts"() {
267
267
  "use strict";
268
268
  init_cjs_shims();
269
269
  import_node_url = require("url");
270
- import_node_path39 = __toESM(require("path"), 1);
270
+ import_node_path40 = __toESM(require("path"), 1);
271
271
  import_node_crypto2 = require("crypto");
272
272
  grpc = __toESM(require("@grpc/grpc-js"), 1);
273
273
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -353,6 +353,46 @@ function tableFromSqlStatement(sql) {
353
353
  const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
354
354
  return m ? m[1] : null;
355
355
  }
356
+ function columnsFromSqlStatement(sql) {
357
+ if (typeof sql !== "string" || sql.length === 0) return [];
358
+ const s = sql.replace(/\s+/g, " ").trim();
359
+ if (/\bjoin\b/i.test(s)) return [];
360
+ if ((s.match(/\bfrom\b/gi) ?? []).length > 1) return [];
361
+ const bare = (raw) => {
362
+ let t = raw.trim().replace(/"/g, "");
363
+ if (/[()*]/.test(t)) return null;
364
+ t = t.split(/\s+as\s+/i)[0].trim();
365
+ t = t.split(".").pop();
366
+ return /^[a-z_][\w$]*$/i.test(t) ? t.toLowerCase() : null;
367
+ };
368
+ const isCol = (c) => c !== null;
369
+ const cols = (list) => [...new Set(list.split(",").map(bare).filter(isCol))];
370
+ const whereCols = (w) => w ? [
371
+ ...new Set(
372
+ [
373
+ ...w.matchAll(
374
+ /(?:"?[\w$]+"?\.)?"?([a-z_][\w$]*)"?\s*(?:=|<|>|<=|>=|<>|!=|\bis\b|\bin\b|\blike\b)/gi
375
+ )
376
+ ].map((match) => match[1].toLowerCase()).filter((c) => !/^(and|or|not|null)$/i.test(c))
377
+ )
378
+ ] : [];
379
+ let m;
380
+ if (m = /\binsert\s+into\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s*\(([^)]*)\)/i.exec(s)) {
381
+ return cols(m[1]);
382
+ }
383
+ if (m = /\bupdate\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s+set\s+(.+?)(?:\bwhere\b(.+))?$/i.exec(s)) {
384
+ const set = m[1].split(",").map((p) => bare(p.split("=")[0])).filter(isCol);
385
+ return [.../* @__PURE__ */ new Set([...set, ...whereCols(m[2] ?? void 0)])];
386
+ }
387
+ if (m = /\bdelete\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
388
+ return whereCols(m[1] ?? void 0);
389
+ }
390
+ if (m = /\bselect\s+(.+?)\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
391
+ if (m[1].trim() === "*") return [];
392
+ return [.../* @__PURE__ */ new Set([...cols(m[1]), ...whereCols(m[2] ?? void 0)])];
393
+ }
394
+ return [];
395
+ }
356
396
  function messagingDestinationOf(attrs) {
357
397
  for (const key of ["messaging.destination.name", "messaging.destination"]) {
358
398
  const v = attrs[key];
@@ -373,8 +413,8 @@ function websocketChannelPathOf(attrs) {
373
413
  const v = attrs[key];
374
414
  if (typeof v === "string" && v.length > 0) {
375
415
  const q = v.indexOf("?");
376
- const path58 = q === -1 ? v : v.slice(0, q);
377
- if (path58.length > 0) return path58;
416
+ const path59 = q === -1 ? v : v.slice(0, q);
417
+ if (path59.length > 0) return path59;
378
418
  }
379
419
  }
380
420
  return void 0;
@@ -411,6 +451,7 @@ function parseOtlpRequest(body) {
411
451
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
412
452
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
413
453
  dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
454
+ dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
414
455
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
415
456
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
416
457
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -432,10 +473,10 @@ function parseOtlpRequest(body) {
432
473
  return out;
433
474
  }
434
475
  function loadProtoRoot() {
435
- const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
436
- const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
476
+ const here = import_node_path41.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
477
+ const protoRoot = import_node_path41.default.resolve(here, "..", "proto");
437
478
  const root = new import_protobufjs.default.Root();
438
- root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
479
+ root.resolvePath = (_origin, target) => import_node_path41.default.resolve(protoRoot, target);
439
480
  root.loadSync(
440
481
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
441
482
  { keepCase: true }
@@ -650,12 +691,12 @@ async function buildOtelReceiver(opts) {
650
691
  };
651
692
  return decorated;
652
693
  }
653
- var import_node_path40, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
694
+ var import_node_path41, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
654
695
  var init_otel = __esm({
655
696
  "src/otel.ts"() {
656
697
  "use strict";
657
698
  init_cjs_shims();
658
- import_node_path40 = __toESM(require("path"), 1);
699
+ import_node_path41 = __toESM(require("path"), 1);
659
700
  import_node_url2 = require("url");
660
701
  import_fastify = __toESM(require("fastify"), 1);
661
702
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -671,7 +712,7 @@ var init_otel = __esm({
671
712
 
672
713
  // src/server.ts
673
714
  init_cjs_shims();
674
- var import_node_path57 = __toESM(require("path"), 1);
715
+ var import_node_path58 = __toESM(require("path"), 1);
675
716
 
676
717
  // src/graph.ts
677
718
  init_cjs_shims();
@@ -695,7 +736,7 @@ function getGraph(project = DEFAULT_PROJECT) {
695
736
  init_cjs_shims();
696
737
  var import_fastify2 = __toESM(require("fastify"), 1);
697
738
  var import_cors = __toESM(require("@fastify/cors"), 1);
698
- var import_types56 = require("@neat.is/types");
739
+ var import_types58 = require("@neat.is/types");
699
740
 
700
741
  // src/extend/index.ts
701
742
  init_cjs_shims();
@@ -1042,7 +1083,43 @@ async function rollbackExtension(ctx, args) {
1042
1083
 
1043
1084
  // src/divergences.ts
1044
1085
  init_cjs_shims();
1045
- var import_types7 = require("@neat.is/types");
1086
+ var import_types8 = require("@neat.is/types");
1087
+
1088
+ // src/columns.ts
1089
+ init_cjs_shims();
1090
+ var import_types = require("@neat.is/types");
1091
+ var OBSERVED_COLUMN_CONFIDENCE = 0.9;
1092
+ function normalizeProvenances(provenances) {
1093
+ return [...new Set(provenances)].sort();
1094
+ }
1095
+ function foldColumns(existing, names, provenance, confidence) {
1096
+ const out = (existing ?? []).map((c) => ({
1097
+ ...c,
1098
+ provenances: [...c.provenances]
1099
+ }));
1100
+ const byName = new Map(out.map((c) => [c.name, c]));
1101
+ for (const raw of names) {
1102
+ const name = raw.toLowerCase();
1103
+ const prior = byName.get(name);
1104
+ if (!prior) {
1105
+ const col = { name, provenances: [provenance], confidence };
1106
+ byName.set(name, col);
1107
+ out.push(col);
1108
+ continue;
1109
+ }
1110
+ if (!prior.provenances.includes(provenance)) {
1111
+ prior.provenances = normalizeProvenances([...prior.provenances, provenance]);
1112
+ }
1113
+ if (confidence > prior.confidence) prior.confidence = confidence;
1114
+ }
1115
+ return out;
1116
+ }
1117
+ function columnIsDeclared(col) {
1118
+ return col.provenances.includes(import_types.Provenance.EXTRACTED);
1119
+ }
1120
+ function columnIsObserved(col) {
1121
+ return col.provenances.includes(import_types.Provenance.OBSERVED);
1122
+ }
1046
1123
 
1047
1124
  // src/compat.ts
1048
1125
  init_cjs_shims();
@@ -1370,7 +1447,7 @@ var sourceMapJs = __toESM(require("source-map-js"), 1);
1370
1447
  init_cjs_shims();
1371
1448
  var import_node_fs4 = require("fs");
1372
1449
  var import_node_path4 = __toESM(require("path"), 1);
1373
- var import_types = require("@neat.is/types");
1450
+ var import_types2 = require("@neat.is/types");
1374
1451
 
1375
1452
  // src/events.ts
1376
1453
  init_cjs_shims();
@@ -1422,7 +1499,7 @@ var evaluateStructural = ({
1422
1499
  const e = graph.getEdgeAttributes(edgeId);
1423
1500
  if (e.type !== rule.edgeType) continue;
1424
1501
  const target = graph.getNodeAttributes(e.target);
1425
- if (target.type === import_types.NodeType.FrontierNode) continue;
1502
+ if (target.type === import_types2.NodeType.FrontierNode) continue;
1426
1503
  if (target.type === rule.toNodeType) {
1427
1504
  satisfied = true;
1428
1505
  break;
@@ -1534,16 +1611,16 @@ var evaluateCompatibility = ({
1534
1611
  const wantsKind = (kind) => rule.kind === void 0 || rule.kind === kind;
1535
1612
  graph.forEachNode((svcId, attrs) => {
1536
1613
  const a = attrs;
1537
- if (a.type !== import_types.NodeType.ServiceNode) return;
1614
+ if (a.type !== import_types2.NodeType.ServiceNode) return;
1538
1615
  const svc = a;
1539
1616
  const deps = svc.dependencies ?? {};
1540
1617
  if (wantsKind("driver-engine")) {
1541
1618
  for (const edgeId of graph.outboundEdges(svcId)) {
1542
1619
  const e = graph.getEdgeAttributes(edgeId);
1543
- if (e.type !== import_types.EdgeType.CONNECTS_TO) continue;
1620
+ if (e.type !== import_types2.EdgeType.CONNECTS_TO) continue;
1544
1621
  const dbAttrs = graph.getNodeAttributes(e.target);
1545
- if (dbAttrs.type === import_types.NodeType.FrontierNode) continue;
1546
- if (dbAttrs.type !== import_types.NodeType.DatabaseNode) continue;
1622
+ if (dbAttrs.type === import_types2.NodeType.FrontierNode) continue;
1623
+ if (dbAttrs.type !== import_types2.NodeType.DatabaseNode) continue;
1547
1624
  const db = dbAttrs;
1548
1625
  for (const pair of compatPairs()) {
1549
1626
  if (pair.engine !== db.engine) continue;
@@ -1743,14 +1820,14 @@ function matchPolicyToNode(graph, policy, nodeId, node) {
1743
1820
  }
1744
1821
  case "compatibility": {
1745
1822
  const kindLabel = rule.kind ?? "all compat shapes";
1746
- if (node.type === import_types.NodeType.ServiceNode) {
1823
+ if (node.type === import_types2.NodeType.ServiceNode) {
1747
1824
  return {
1748
1825
  match: "subject",
1749
1826
  reason: `this service's dependencies are compatibility-checked (${kindLabel})`
1750
1827
  };
1751
1828
  }
1752
1829
  const reachesDriverEngine = rule.kind === void 0 || rule.kind === "driver-engine";
1753
- if (reachesDriverEngine && node.type === import_types.NodeType.DatabaseNode && nodeTouchesEdgeType(graph, nodeId, import_types.EdgeType.CONNECTS_TO)) {
1830
+ if (reachesDriverEngine && node.type === import_types2.NodeType.DatabaseNode && nodeTouchesEdgeType(graph, nodeId, import_types2.EdgeType.CONNECTS_TO)) {
1754
1831
  return {
1755
1832
  match: "region",
1756
1833
  reason: "services connecting to this database have their driver/engine compatibility checked against it"
@@ -1785,7 +1862,7 @@ async function loadPolicyFile(policyPath) {
1785
1862
  throw err;
1786
1863
  }
1787
1864
  const json = JSON.parse(raw);
1788
- const file = import_types.PolicyFileSchema.parse(json);
1865
+ const file = import_types2.PolicyFileSchema.parse(json);
1789
1866
  return file.policies;
1790
1867
  }
1791
1868
  var PolicyViolationsLog = class {
@@ -1826,7 +1903,7 @@ var PolicyViolationsLog = class {
1826
1903
  };
1827
1904
 
1828
1905
  // src/ingest.ts
1829
- var import_types5 = require("@neat.is/types");
1906
+ var import_types6 = require("@neat.is/types");
1830
1907
 
1831
1908
  // src/extract/routes.ts
1832
1909
  init_cjs_shims();
@@ -1835,14 +1912,14 @@ var import_tree_sitter = __toESM(require("tree-sitter"), 1);
1835
1912
  var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
1836
1913
  var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
1837
1914
  var import_tree_sitter_go = __toESM(require("tree-sitter-go"), 1);
1838
- var import_types4 = require("@neat.is/types");
1915
+ var import_types5 = require("@neat.is/types");
1839
1916
 
1840
1917
  // src/extract/shared.ts
1841
1918
  init_cjs_shims();
1842
1919
  var import_node_fs5 = require("fs");
1843
1920
  var import_node_path5 = __toESM(require("path"), 1);
1844
1921
  var import_yaml = require("yaml");
1845
- var import_types2 = require("@neat.is/types");
1922
+ var import_types3 = require("@neat.is/types");
1846
1923
  var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go"]);
1847
1924
  var CONFIG_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
1848
1925
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
@@ -2097,7 +2174,7 @@ async function writeRejectedExtracted(drops, rejectedPath) {
2097
2174
  init_cjs_shims();
2098
2175
  var import_node_fs7 = require("fs");
2099
2176
  var import_node_path7 = __toESM(require("path"), 1);
2100
- var import_types3 = require("@neat.is/types");
2177
+ var import_types4 = require("@neat.is/types");
2101
2178
  function buildServiceHostIndex(services) {
2102
2179
  const knownHosts = /* @__PURE__ */ new Set();
2103
2180
  const hostToNodeId = /* @__PURE__ */ new Map();
@@ -2175,12 +2252,12 @@ function languageForPath(relPath) {
2175
2252
  function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
2176
2253
  let nodesAdded = 0;
2177
2254
  let edgesAdded = 0;
2178
- const fileNodeId = (0, import_types3.fileId)(serviceName, relPath);
2255
+ const fileNodeId = (0, import_types4.fileId)(serviceName, relPath);
2179
2256
  if (!graph.hasNode(fileNodeId)) {
2180
2257
  const language = languageForPath(relPath);
2181
2258
  const node = {
2182
2259
  id: fileNodeId,
2183
- type: import_types3.NodeType.FileNode,
2260
+ type: import_types4.NodeType.FileNode,
2184
2261
  service: serviceName,
2185
2262
  path: relPath,
2186
2263
  ...language ? { language } : {},
@@ -2189,15 +2266,15 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
2189
2266
  graph.addNode(fileNodeId, node);
2190
2267
  nodesAdded++;
2191
2268
  }
2192
- const containsId = (0, import_types3.extractedEdgeId)(serviceNodeId, fileNodeId, import_types3.EdgeType.CONTAINS);
2269
+ const containsId = (0, import_types4.extractedEdgeId)(serviceNodeId, fileNodeId, import_types4.EdgeType.CONTAINS);
2193
2270
  if (!graph.hasEdge(containsId)) {
2194
2271
  const edge = {
2195
2272
  id: containsId,
2196
2273
  source: serviceNodeId,
2197
2274
  target: fileNodeId,
2198
- type: import_types3.EdgeType.CONTAINS,
2199
- provenance: import_types3.Provenance.EXTRACTED,
2200
- confidence: (0, import_types3.confidenceForExtracted)("structural"),
2275
+ type: import_types4.EdgeType.CONTAINS,
2276
+ provenance: import_types4.Provenance.EXTRACTED,
2277
+ confidence: (0, import_types4.confidenceForExtracted)("structural"),
2201
2278
  evidence: { file: relPath }
2202
2279
  };
2203
2280
  graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
@@ -2831,11 +2908,11 @@ async function addRoutes(graph, services) {
2831
2908
  }
2832
2909
  if (routes.length === 0) continue;
2833
2910
  for (const route of routes) {
2834
- const rid = (0, import_types4.routeId)(service.pkg.name, route.method, route.pathTemplate);
2911
+ const rid = (0, import_types5.routeId)(service.pkg.name, route.method, route.pathTemplate);
2835
2912
  if (!graph.hasNode(rid)) {
2836
2913
  const node = {
2837
2914
  id: rid,
2838
- type: import_types4.NodeType.RouteNode,
2915
+ type: import_types5.NodeType.RouteNode,
2839
2916
  name: `${route.method} ${route.pathTemplate}`,
2840
2917
  service: service.pkg.name,
2841
2918
  method: route.method,
@@ -2848,15 +2925,15 @@ async function addRoutes(graph, services) {
2848
2925
  graph.addNode(rid, node);
2849
2926
  nodesAdded++;
2850
2927
  }
2851
- const containsId = (0, import_types4.extractedEdgeId)(service.node.id, rid, import_types4.EdgeType.CONTAINS);
2928
+ const containsId = (0, import_types5.extractedEdgeId)(service.node.id, rid, import_types5.EdgeType.CONTAINS);
2852
2929
  if (!graph.hasEdge(containsId)) {
2853
2930
  const edge = {
2854
2931
  id: containsId,
2855
2932
  source: service.node.id,
2856
2933
  target: rid,
2857
- type: import_types4.EdgeType.CONTAINS,
2858
- provenance: import_types4.Provenance.EXTRACTED,
2859
- confidence: (0, import_types4.confidenceForExtracted)("structural"),
2934
+ type: import_types5.EdgeType.CONTAINS,
2935
+ provenance: import_types5.Provenance.EXTRACTED,
2936
+ confidence: (0, import_types5.confidenceForExtracted)("structural"),
2860
2937
  evidence: {
2861
2938
  file: relFile,
2862
2939
  line: route.line,
@@ -3171,11 +3248,11 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
3171
3248
  };
3172
3249
  }
3173
3250
  function reconcileObservedRelPath(graph, serviceName, relPath) {
3174
- if (graph.hasNode((0, import_types5.fileId)(serviceName, relPath))) return relPath;
3251
+ if (graph.hasNode((0, import_types6.fileId)(serviceName, relPath))) return relPath;
3175
3252
  let best = null;
3176
3253
  graph.forEachNode((_id, attrs) => {
3177
3254
  const a = attrs;
3178
- if (a.type !== import_types5.NodeType.FileNode || a.service !== serviceName) return;
3255
+ if (a.type !== import_types6.NodeType.FileNode || a.service !== serviceName) return;
3179
3256
  if (a.discoveredVia === "otel") return;
3180
3257
  const p = a.path;
3181
3258
  if (!p) return;
@@ -3187,14 +3264,14 @@ function reconcileObservedRelPath(graph, serviceName, relPath) {
3187
3264
  }
3188
3265
  function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
3189
3266
  const svcAttrs = graph.hasNode(serviceNodeId) ? graph.getNodeAttributes(serviceNodeId) : void 0;
3190
- const canonicalService = svcAttrs && svcAttrs.type === import_types5.NodeType.ServiceNode && typeof svcAttrs.name === "string" ? svcAttrs.name : serviceName;
3267
+ const canonicalService = svcAttrs && svcAttrs.type === import_types6.NodeType.ServiceNode && typeof svcAttrs.name === "string" ? svcAttrs.name : serviceName;
3191
3268
  const relPath = reconcileObservedRelPath(graph, canonicalService, callSite.relPath);
3192
- const fileNodeId = (0, import_types5.fileId)(canonicalService, relPath);
3269
+ const fileNodeId = (0, import_types6.fileId)(canonicalService, relPath);
3193
3270
  if (!graph.hasNode(fileNodeId)) {
3194
3271
  const language = languageForExt(relPath);
3195
3272
  const node = {
3196
3273
  id: fileNodeId,
3197
- type: import_types5.NodeType.FileNode,
3274
+ type: import_types6.NodeType.FileNode,
3198
3275
  service: canonicalService,
3199
3276
  path: relPath,
3200
3277
  ...language ? { language } : {},
@@ -3203,14 +3280,14 @@ function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
3203
3280
  };
3204
3281
  graph.addNode(fileNodeId, node);
3205
3282
  }
3206
- const containsId = makeObservedEdgeId(import_types5.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
3283
+ const containsId = makeObservedEdgeId(import_types6.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
3207
3284
  if (!graph.hasEdge(containsId)) {
3208
3285
  const edge = {
3209
3286
  id: containsId,
3210
3287
  source: serviceNodeId,
3211
3288
  target: fileNodeId,
3212
- type: import_types5.EdgeType.CONTAINS,
3213
- provenance: import_types5.Provenance.OBSERVED
3289
+ type: import_types6.EdgeType.CONTAINS,
3290
+ provenance: import_types6.Provenance.OBSERVED
3214
3291
  };
3215
3292
  graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
3216
3293
  }
@@ -3234,11 +3311,11 @@ function pickContainingSymbol(candidates, fn) {
3234
3311
  return [...candidates].sort(bySpan)[0].id;
3235
3312
  }
3236
3313
  function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line) {
3237
- const sid = (0, import_types5.symbolId)(service, relPath, fn);
3314
+ const sid = (0, import_types6.symbolId)(service, relPath, fn);
3238
3315
  if (!graph.hasNode(sid)) {
3239
3316
  const node = {
3240
3317
  id: sid,
3241
- type: import_types5.NodeType.SymbolNode,
3318
+ type: import_types6.NodeType.SymbolNode,
3242
3319
  kind: "function",
3243
3320
  qualname: fn,
3244
3321
  span: { startLine: line, endLine: line },
@@ -3248,14 +3325,14 @@ function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line)
3248
3325
  };
3249
3326
  graph.addNode(sid, node);
3250
3327
  }
3251
- const containsId = makeObservedEdgeId(import_types5.EdgeType.CONTAINS, fileNodeId, sid);
3328
+ const containsId = makeObservedEdgeId(import_types6.EdgeType.CONTAINS, fileNodeId, sid);
3252
3329
  if (!graph.hasEdge(containsId)) {
3253
3330
  const edge = {
3254
3331
  id: containsId,
3255
3332
  source: fileNodeId,
3256
3333
  target: sid,
3257
- type: import_types5.EdgeType.CONTAINS,
3258
- provenance: import_types5.Provenance.OBSERVED
3334
+ type: import_types6.EdgeType.CONTAINS,
3335
+ provenance: import_types6.Provenance.OBSERVED
3259
3336
  };
3260
3337
  graph.addEdgeWithKey(containsId, fileNodeId, sid, edge);
3261
3338
  }
@@ -3267,9 +3344,9 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
3267
3344
  let sawSymbol = false;
3268
3345
  const candidates = [];
3269
3346
  graph.forEachOutboundEdge(fileNodeId, (_edge, edgeAttrs, _source, target) => {
3270
- if (edgeAttrs.type !== import_types5.EdgeType.CONTAINS) return;
3347
+ if (edgeAttrs.type !== import_types6.EdgeType.CONTAINS) return;
3271
3348
  const t = graph.getNodeAttributes(target);
3272
- if (t.type !== import_types5.NodeType.SymbolNode) return;
3349
+ if (t.type !== import_types6.NodeType.SymbolNode) return;
3273
3350
  sawSymbol = true;
3274
3351
  if (line >= t.span.startLine && line <= t.span.endLine) {
3275
3352
  candidates.push({ id: target, symbol: t });
@@ -3282,17 +3359,17 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
3282
3359
  return fileNodeId;
3283
3360
  }
3284
3361
  function makeObservedEdgeId(type, source, target) {
3285
- return (0, import_types5.observedEdgeId)(source, target, type);
3362
+ return (0, import_types6.observedEdgeId)(source, target, type);
3286
3363
  }
3287
3364
  function makeInferredEdgeId(type, source, target) {
3288
- return (0, import_types5.inferredEdgeId)(source, target, type);
3365
+ return (0, import_types6.inferredEdgeId)(source, target, type);
3289
3366
  }
3290
3367
  var INFERRED_CONFIDENCE = 0.6;
3291
3368
  var STITCH_MAX_DEPTH = 2;
3292
3369
  var STITCH_EDGE_TYPES = /* @__PURE__ */ new Set([
3293
- import_types5.EdgeType.CALLS,
3294
- import_types5.EdgeType.CONNECTS_TO,
3295
- import_types5.EdgeType.DEPENDS_ON
3370
+ import_types6.EdgeType.CALLS,
3371
+ import_types6.EdgeType.CONNECTS_TO,
3372
+ import_types6.EdgeType.DEPENDS_ON
3296
3373
  ]);
3297
3374
  var WIRE_SPAN_KIND_CLIENT = 3;
3298
3375
  var WIRE_SPAN_KIND_PRODUCER = 4;
@@ -3308,11 +3385,11 @@ function spanServesGraphqlOperation(kind) {
3308
3385
  return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
3309
3386
  }
3310
3387
  function ensureGraphqlOperationNode(graph, serviceName, operationType, operationName) {
3311
- const id = (0, import_types5.graphqlOperationId)(serviceName, operationType, operationName);
3388
+ const id = (0, import_types6.graphqlOperationId)(serviceName, operationType, operationName);
3312
3389
  if (graph.hasNode(id)) return id;
3313
3390
  const node = {
3314
3391
  id,
3315
- type: import_types5.NodeType.GraphQLOperationNode,
3392
+ type: import_types6.NodeType.GraphQLOperationNode,
3316
3393
  name: operationName,
3317
3394
  service: serviceName,
3318
3395
  operationType: operationType.toLowerCase(),
@@ -3326,11 +3403,11 @@ function spanServesGrpcMethod(kind) {
3326
3403
  return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
3327
3404
  }
3328
3405
  function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
3329
- const id = (0, import_types5.grpcMethodId)(rpcService, rpcMethod);
3406
+ const id = (0, import_types6.grpcMethodId)(rpcService, rpcMethod);
3330
3407
  if (graph.hasNode(id)) return id;
3331
3408
  const node = {
3332
3409
  id,
3333
- type: import_types5.NodeType.GrpcMethodNode,
3410
+ type: import_types6.NodeType.GrpcMethodNode,
3334
3411
  name: `${rpcService}/${rpcMethod}`,
3335
3412
  rpcService,
3336
3413
  rpcMethod,
@@ -3343,11 +3420,11 @@ function spanServesWebsocketChannel(kind) {
3343
3420
  return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
3344
3421
  }
3345
3422
  function ensureWebsocketChannelNode(graph, serviceName, channel) {
3346
- const id = (0, import_types5.websocketChannelId)(serviceName, channel);
3423
+ const id = (0, import_types6.websocketChannelId)(serviceName, channel);
3347
3424
  if (graph.hasNode(id)) return id;
3348
3425
  const node = {
3349
3426
  id,
3350
- type: import_types5.NodeType.WebSocketChannelNode,
3427
+ type: import_types6.NodeType.WebSocketChannelNode,
3351
3428
  name: channel,
3352
3429
  service: serviceName,
3353
3430
  channel,
@@ -3360,11 +3437,11 @@ function messagingDestinationKind(system) {
3360
3437
  return `${system}-topic`;
3361
3438
  }
3362
3439
  function ensureMessagingDestinationNode(graph, system, destination) {
3363
- const id = (0, import_types5.infraId)(messagingDestinationKind(system), destination);
3440
+ const id = (0, import_types6.infraId)(messagingDestinationKind(system), destination);
3364
3441
  if (graph.hasNode(id)) return id;
3365
3442
  const node = {
3366
3443
  id,
3367
- type: import_types5.NodeType.InfraNode,
3444
+ type: import_types6.NodeType.InfraNode,
3368
3445
  name: destination,
3369
3446
  provider: "self",
3370
3447
  kind: messagingDestinationKind(system)
@@ -3408,9 +3485,9 @@ function lookupParentSpan(traceId, parentSpanId, now) {
3408
3485
  };
3409
3486
  }
3410
3487
  function resolveServiceId(graph, host, env) {
3411
- const envTagged = (0, import_types5.serviceId)(host, env);
3488
+ const envTagged = (0, import_types6.serviceId)(host, env);
3412
3489
  if (graph.hasNode(envTagged)) return envTagged;
3413
- const envLess = (0, import_types5.serviceId)(host);
3490
+ const envLess = (0, import_types6.serviceId)(host);
3414
3491
  if (envLess !== envTagged && graph.hasNode(envLess)) return envLess;
3415
3492
  let sameEnv = null;
3416
3493
  let envLessMatch = null;
@@ -3418,7 +3495,7 @@ function resolveServiceId(graph, host, env) {
3418
3495
  graph.forEachNode((id, attrs) => {
3419
3496
  if (sameEnv) return;
3420
3497
  const a = attrs;
3421
- if (a.type !== import_types5.NodeType.ServiceNode) return;
3498
+ if (a.type !== import_types6.NodeType.ServiceNode) return;
3422
3499
  const matchesByName = a.name === host;
3423
3500
  const matchesByAlias = a.aliases ? a.aliases.includes(host) : false;
3424
3501
  if (!matchesByName && !matchesByAlias) return;
@@ -3433,14 +3510,14 @@ function resolveServiceId(graph, host, env) {
3433
3510
  return sameEnv ?? envLessMatch ?? anyMatch;
3434
3511
  }
3435
3512
  function frontierIdFor(host) {
3436
- return (0, import_types5.frontierId)(host);
3513
+ return (0, import_types6.frontierId)(host);
3437
3514
  }
3438
3515
  function ensureServiceNode(graph, serviceName, env) {
3439
- const id = (0, import_types5.serviceId)(serviceName, env);
3516
+ const id = (0, import_types6.serviceId)(serviceName, env);
3440
3517
  if (graph.hasNode(id)) return id;
3441
3518
  const wanted = serviceName.toLowerCase();
3442
3519
  const extractedId = graph.findNode((_nid, attrs) => {
3443
- if (attrs.type !== import_types5.NodeType.ServiceNode) return false;
3520
+ if (attrs.type !== import_types6.NodeType.ServiceNode) return false;
3444
3521
  const svc = attrs;
3445
3522
  if (svc.discoveredVia === "otel") return false;
3446
3523
  return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
@@ -3448,7 +3525,7 @@ function ensureServiceNode(graph, serviceName, env) {
3448
3525
  if (extractedId) return extractedId;
3449
3526
  const node = {
3450
3527
  id,
3451
- type: import_types5.NodeType.ServiceNode,
3528
+ type: import_types6.NodeType.ServiceNode,
3452
3529
  name: serviceName,
3453
3530
  language: "unknown",
3454
3531
  discoveredVia: "otel",
@@ -3458,11 +3535,11 @@ function ensureServiceNode(graph, serviceName, env) {
3458
3535
  return id;
3459
3536
  }
3460
3537
  function ensureInfraNode(graph, kind, name, provider) {
3461
- const id = (0, import_types5.infraId)(kind, name);
3538
+ const id = (0, import_types6.infraId)(kind, name);
3462
3539
  if (graph.hasNode(id)) return id;
3463
3540
  const node = {
3464
3541
  id,
3465
- type: import_types5.NodeType.InfraNode,
3542
+ type: import_types6.NodeType.InfraNode,
3466
3543
  name,
3467
3544
  provider,
3468
3545
  kind
@@ -3470,12 +3547,27 @@ function ensureInfraNode(graph, kind, name, provider) {
3470
3547
  graph.addNode(id, node);
3471
3548
  return id;
3472
3549
  }
3550
+ var COLUMN_BEARING_INFRA_KINDS = /* @__PURE__ */ new Set(["sql-table", "supabase-table"]);
3551
+ function mergeColumnsAt(graph, tableNodeId, columns, provenance, confidence) {
3552
+ if (!columns || columns.length === 0 || !graph.hasNode(tableNodeId)) return;
3553
+ const node = graph.getNodeAttributes(tableNodeId);
3554
+ if (node.type !== import_types6.NodeType.InfraNode || !node.kind || !COLUMN_BEARING_INFRA_KINDS.has(node.kind)) {
3555
+ return;
3556
+ }
3557
+ graph.replaceNodeAttributes(tableNodeId, {
3558
+ ...node,
3559
+ columns: foldColumns(node.columns, columns, provenance, confidence)
3560
+ });
3561
+ }
3562
+ function mergeObservedColumns(graph, tableNodeId, columns) {
3563
+ mergeColumnsAt(graph, tableNodeId, columns, import_types6.Provenance.OBSERVED, OBSERVED_COLUMN_CONFIDENCE);
3564
+ }
3473
3565
  function ensureDatabaseNode(graph, host, engine) {
3474
- const id = (0, import_types5.databaseId)(host);
3566
+ const id = (0, import_types6.databaseId)(host);
3475
3567
  if (graph.hasNode(id)) return id;
3476
3568
  const node = {
3477
3569
  id,
3478
- type: import_types5.NodeType.DatabaseNode,
3570
+ type: import_types6.NodeType.DatabaseNode,
3479
3571
  name: host,
3480
3572
  engine,
3481
3573
  engineVersion: "unknown",
@@ -3487,11 +3579,11 @@ function ensureDatabaseNode(graph, host, engine) {
3487
3579
  return id;
3488
3580
  }
3489
3581
  function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
3490
- const id = (0, import_types5.localDatabaseId)(serviceName, name);
3582
+ const id = (0, import_types6.localDatabaseId)(serviceName, name);
3491
3583
  if (graph.hasNode(id)) return id;
3492
3584
  const node = {
3493
3585
  id,
3494
- type: import_types5.NodeType.DatabaseNode,
3586
+ type: import_types6.NodeType.DatabaseNode,
3495
3587
  name,
3496
3588
  engine,
3497
3589
  engineVersion: "unknown",
@@ -3506,17 +3598,17 @@ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
3506
3598
  const sources = [serviceNodeId];
3507
3599
  for (const edgeId of graph.outboundEdges(serviceNodeId)) {
3508
3600
  const e = graph.getEdgeAttributes(edgeId);
3509
- if (e.type === import_types5.EdgeType.CONTAINS) sources.push(e.target);
3601
+ if (e.type === import_types6.EdgeType.CONTAINS) sources.push(e.target);
3510
3602
  }
3511
3603
  const matches = /* @__PURE__ */ new Set();
3512
3604
  for (const src of sources) {
3513
3605
  if (!graph.hasNode(src)) continue;
3514
3606
  for (const edgeId of graph.outboundEdges(src)) {
3515
3607
  const edge = graph.getEdgeAttributes(edgeId);
3516
- if (edge.type !== import_types5.EdgeType.CONNECTS_TO || edge.provenance !== import_types5.Provenance.EXTRACTED) continue;
3608
+ if (edge.type !== import_types6.EdgeType.CONNECTS_TO || edge.provenance !== import_types6.Provenance.EXTRACTED) continue;
3517
3609
  if (!graph.hasNode(edge.target)) continue;
3518
3610
  const target = graph.getNodeAttributes(edge.target);
3519
- if (target.type !== import_types5.NodeType.DatabaseNode || target.engine !== engine) continue;
3611
+ if (target.type !== import_types6.NodeType.DatabaseNode || target.engine !== engine) continue;
3520
3612
  matches.add(edge.target);
3521
3613
  }
3522
3614
  }
@@ -3531,7 +3623,7 @@ function ensureFrontierNode(graph, host, ts) {
3531
3623
  }
3532
3624
  const node = {
3533
3625
  id,
3534
- type: import_types5.NodeType.FrontierNode,
3626
+ type: import_types6.NodeType.FrontierNode,
3535
3627
  name: host,
3536
3628
  host,
3537
3629
  firstObserved: ts,
@@ -3555,11 +3647,11 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
3555
3647
  };
3556
3648
  const updated = {
3557
3649
  ...existing,
3558
- provenance: import_types5.Provenance.OBSERVED,
3650
+ provenance: import_types6.Provenance.OBSERVED,
3559
3651
  lastObserved: ts,
3560
3652
  callCount: newSpanCount,
3561
3653
  signal: newSignal,
3562
- confidence: (0, import_types5.confidenceForObservedSignal)(newSignal),
3654
+ confidence: (0, import_types6.confidenceForObservedSignal)(newSignal),
3563
3655
  grain
3564
3656
  // backfills legacy edges that predate ADR-142
3565
3657
  };
@@ -3576,8 +3668,8 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
3576
3668
  source,
3577
3669
  target,
3578
3670
  type,
3579
- provenance: import_types5.Provenance.OBSERVED,
3580
- confidence: (0, import_types5.confidenceForObservedSignal)(signal),
3671
+ provenance: import_types6.Provenance.OBSERVED,
3672
+ confidence: (0, import_types6.confidenceForObservedSignal)(signal),
3581
3673
  lastObserved: ts,
3582
3674
  callCount: 1,
3583
3675
  signal,
@@ -3599,9 +3691,9 @@ function stitchTrace(graph, sourceServiceId, ts) {
3599
3691
  const outbound = graph.outboundEdges(nodeId);
3600
3692
  for (const edgeId of outbound) {
3601
3693
  const edge = graph.getEdgeAttributes(edgeId);
3602
- if (edge.provenance !== import_types5.Provenance.EXTRACTED) continue;
3694
+ if (edge.provenance !== import_types6.Provenance.EXTRACTED) continue;
3603
3695
  if (!STITCH_EDGE_TYPES.has(edge.type)) continue;
3604
- if (graph.hasEdge((0, import_types5.observedEdgeId)(edge.source, edge.target, edge.type))) continue;
3696
+ if (graph.hasEdge((0, import_types6.observedEdgeId)(edge.source, edge.target, edge.type))) continue;
3605
3697
  upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts);
3606
3698
  if (!visited.has(edge.target)) {
3607
3699
  visited.add(edge.target);
@@ -3623,7 +3715,7 @@ function upsertInferredEdge(graph, type, source, target, ts) {
3623
3715
  source,
3624
3716
  target,
3625
3717
  type,
3626
- provenance: import_types5.Provenance.INFERRED,
3718
+ provenance: import_types6.Provenance.INFERRED,
3627
3719
  confidence: INFERRED_CONFIDENCE,
3628
3720
  lastObserved: ts
3629
3721
  };
@@ -3634,12 +3726,12 @@ async function appendErrorEvent(ctx, ev) {
3634
3726
  await import_node_fs8.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
3635
3727
  }
3636
3728
  function incidentAffectedNode(span, graph, scanPath) {
3637
- const sid = (0, import_types5.serviceId)(span.service, span.env);
3729
+ const sid = (0, import_types6.serviceId)(span.service, span.env);
3638
3730
  const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
3639
3731
  const callSite = callSiteFromSpan(span, serviceNode, scanPath);
3640
3732
  if (callSite) {
3641
3733
  const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
3642
- return (0, import_types5.fileId)(span.service, relPath);
3734
+ return (0, import_types6.fileId)(span.service, relPath);
3643
3735
  }
3644
3736
  return sid;
3645
3737
  }
@@ -3739,7 +3831,7 @@ function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
3739
3831
  graph.forEachNode((id, attrs) => {
3740
3832
  if (found) return;
3741
3833
  const a = attrs;
3742
- if (a.type !== import_types5.NodeType.RouteNode || a.service !== serviceName) return;
3834
+ if (a.type !== import_types6.NodeType.RouteNode || a.service !== serviceName) return;
3743
3835
  if (m && a.method !== "ALL" && a.method !== m) return;
3744
3836
  if (normalizePathTemplate(a.pathTemplate) === target) found = id;
3745
3837
  });
@@ -3770,7 +3862,7 @@ async function handleSpan(ctx, span) {
3770
3862
  let targetId;
3771
3863
  if (host) {
3772
3864
  ensureDatabaseNode(ctx.graph, host, span.dbSystem);
3773
- targetId = (0, import_types5.databaseId)(host);
3865
+ targetId = (0, import_types6.databaseId)(host);
3774
3866
  } else {
3775
3867
  const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
3776
3868
  if (declared) {
@@ -3787,7 +3879,7 @@ async function handleSpan(ctx, span) {
3787
3879
  }
3788
3880
  const result = upsertObservedEdge(
3789
3881
  ctx.graph,
3790
- import_types5.EdgeType.CONNECTS_TO,
3882
+ import_types6.EdgeType.CONNECTS_TO,
3791
3883
  observedSource(),
3792
3884
  targetId,
3793
3885
  ts,
@@ -3799,7 +3891,7 @@ async function handleSpan(ctx, span) {
3799
3891
  const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
3800
3892
  upsertObservedEdge(
3801
3893
  ctx.graph,
3802
- import_types5.EdgeType.CALLS,
3894
+ import_types6.EdgeType.CALLS,
3803
3895
  observedSource(),
3804
3896
  collectionId,
3805
3897
  ts,
@@ -3811,13 +3903,14 @@ async function handleSpan(ctx, span) {
3811
3903
  const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
3812
3904
  upsertObservedEdge(
3813
3905
  ctx.graph,
3814
- import_types5.EdgeType.CALLS,
3906
+ import_types6.EdgeType.CALLS,
3815
3907
  observedSource(),
3816
3908
  tableId,
3817
3909
  ts,
3818
3910
  isError,
3819
3911
  callSiteEvidence
3820
3912
  );
3913
+ mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
3821
3914
  }
3822
3915
  }
3823
3916
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
@@ -3826,7 +3919,7 @@ async function handleSpan(ctx, span) {
3826
3919
  span.messagingSystem,
3827
3920
  span.messagingDestination
3828
3921
  );
3829
- const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types5.EdgeType.CONSUMES_FROM : import_types5.EdgeType.PUBLISHES_TO;
3922
+ const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types6.EdgeType.CONSUMES_FROM : import_types6.EdgeType.PUBLISHES_TO;
3830
3923
  const result = upsertObservedEdge(
3831
3924
  ctx.graph,
3832
3925
  edgeType,
@@ -3846,7 +3939,7 @@ async function handleSpan(ctx, span) {
3846
3939
  );
3847
3940
  const result = upsertObservedEdge(
3848
3941
  ctx.graph,
3849
- import_types5.EdgeType.CONTAINS,
3942
+ import_types6.EdgeType.CONTAINS,
3850
3943
  observedSource(),
3851
3944
  targetId,
3852
3945
  ts,
@@ -3858,7 +3951,7 @@ async function handleSpan(ctx, span) {
3858
3951
  const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
3859
3952
  const result = upsertObservedEdge(
3860
3953
  ctx.graph,
3861
- import_types5.EdgeType.CONTAINS,
3954
+ import_types6.EdgeType.CONTAINS,
3862
3955
  observedSource(),
3863
3956
  targetId,
3864
3957
  ts,
@@ -3874,7 +3967,7 @@ async function handleSpan(ctx, span) {
3874
3967
  );
3875
3968
  const result = upsertObservedEdge(
3876
3969
  ctx.graph,
3877
- import_types5.EdgeType.CONNECTS_TO,
3970
+ import_types6.EdgeType.CONNECTS_TO,
3878
3971
  observedSource(),
3879
3972
  targetId,
3880
3973
  ts,
@@ -3890,7 +3983,7 @@ async function handleSpan(ctx, span) {
3890
3983
  if (targetId && targetId !== sourceId) {
3891
3984
  upsertObservedEdge(
3892
3985
  ctx.graph,
3893
- import_types5.EdgeType.CALLS,
3986
+ import_types6.EdgeType.CALLS,
3894
3987
  observedSource(),
3895
3988
  targetId,
3896
3989
  ts,
@@ -3903,7 +3996,7 @@ async function handleSpan(ctx, span) {
3903
3996
  const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
3904
3997
  upsertObservedEdge(
3905
3998
  ctx.graph,
3906
- import_types5.EdgeType.CALLS,
3999
+ import_types6.EdgeType.CALLS,
3907
4000
  observedSource(),
3908
4001
  frontierNodeId,
3909
4002
  ts,
@@ -3929,7 +4022,7 @@ async function handleSpan(ctx, span) {
3929
4022
  } : void 0;
3930
4023
  upsertObservedEdge(
3931
4024
  ctx.graph,
3932
- import_types5.EdgeType.CALLS,
4025
+ import_types6.EdgeType.CALLS,
3933
4026
  fallbackSource,
3934
4027
  sourceId,
3935
4028
  ts,
@@ -3939,7 +4032,7 @@ async function handleSpan(ctx, span) {
3939
4032
  }
3940
4033
  }
3941
4034
  }
3942
- if (span.httpRoute && (span.kind === 2 || span.kind === 0 || span.kind === void 0)) {
4035
+ if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
3943
4036
  const routeNodeId = findRouteNodeByHttpRoute(
3944
4037
  ctx.graph,
3945
4038
  span.service,
@@ -3948,7 +4041,7 @@ async function handleSpan(ctx, span) {
3948
4041
  );
3949
4042
  if (routeNodeId) {
3950
4043
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
3951
- upsertObservedEdge(ctx.graph, import_types5.EdgeType.CONTAINS, (0, import_types5.serviceId)(routeSvc), routeNodeId, ts, isError);
4044
+ upsertObservedEdge(ctx.graph, import_types6.EdgeType.CONTAINS, (0, import_types6.serviceId)(routeSvc), routeNodeId, ts, isError);
3952
4045
  }
3953
4046
  }
3954
4047
  if (span.statusCode === 2) {
@@ -3987,7 +4080,7 @@ function promoteFrontierNodes(graph, opts = {}) {
3987
4080
  const aliasIndex = /* @__PURE__ */ new Map();
3988
4081
  graph.forEachNode((id, attrs) => {
3989
4082
  const a = attrs;
3990
- if (a.type !== import_types5.NodeType.ServiceNode) return;
4083
+ if (a.type !== import_types6.NodeType.ServiceNode) return;
3991
4084
  aliasIndex.set(a.name, id);
3992
4085
  if (a.aliases) {
3993
4086
  for (const alias of a.aliases) aliasIndex.set(alias, id);
@@ -3996,7 +4089,7 @@ function promoteFrontierNodes(graph, opts = {}) {
3996
4089
  const toPromote = [];
3997
4090
  graph.forEachNode((id, attrs) => {
3998
4091
  const a = attrs;
3999
- if (a.type !== import_types5.NodeType.FrontierNode) return;
4092
+ if (a.type !== import_types6.NodeType.FrontierNode) return;
4000
4093
  const target = aliasIndex.get(a.host);
4001
4094
  if (!target) return;
4002
4095
  if (target === id) return;
@@ -4030,7 +4123,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId7) {
4030
4123
  }
4031
4124
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
4032
4125
  graph.dropEdge(oldEdgeId);
4033
- const newId = edge.provenance === import_types5.Provenance.OBSERVED ? (0, import_types5.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types5.Provenance.INFERRED ? (0, import_types5.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types5.extractedEdgeId)(newSource, newTarget, edge.type);
4126
+ const newId = edge.provenance === import_types6.Provenance.OBSERVED ? (0, import_types6.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types6.Provenance.INFERRED ? (0, import_types6.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types6.extractedEdgeId)(newSource, newTarget, edge.type);
4034
4127
  if (graph.hasEdge(newId)) {
4035
4128
  const existing = graph.getEdgeAttributes(newId);
4036
4129
  const merged = {
@@ -4064,12 +4157,12 @@ async function markStaleEdges(graph, options = {}) {
4064
4157
  const project = options.project ?? DEFAULT_PROJECT;
4065
4158
  graph.forEachEdge((id, attrs) => {
4066
4159
  const e = attrs;
4067
- if (e.provenance !== import_types5.Provenance.OBSERVED) return;
4160
+ if (e.provenance !== import_types6.Provenance.OBSERVED) return;
4068
4161
  if (!e.lastObserved) return;
4069
4162
  const threshold = thresholdForEdgeType(e.type, thresholds);
4070
4163
  const age = now - new Date(e.lastObserved).getTime();
4071
4164
  if (age > threshold) {
4072
- const updated = { ...e, provenance: import_types5.Provenance.STALE, confidence: 0.3 };
4165
+ const updated = { ...e, provenance: import_types6.Provenance.STALE, confidence: 0.3 };
4073
4166
  graph.replaceEdgeAttributes(id, updated);
4074
4167
  events.push({
4075
4168
  edgeId: id,
@@ -4086,8 +4179,8 @@ async function markStaleEdges(graph, options = {}) {
4086
4179
  project,
4087
4180
  payload: {
4088
4181
  edgeId: id,
4089
- from: import_types5.Provenance.OBSERVED,
4090
- to: import_types5.Provenance.STALE
4182
+ from: import_types6.Provenance.OBSERVED,
4183
+ to: import_types6.Provenance.STALE
4091
4184
  }
4092
4185
  });
4093
4186
  }
@@ -4196,7 +4289,7 @@ function mergeSnapshot(graph, snapshot) {
4196
4289
  const validEdges = [];
4197
4290
  for (const node of incomingNodes) {
4198
4291
  if (node.attributes === void 0) continue;
4199
- const parsed = import_types5.GraphNodeSchema.safeParse(node.attributes);
4292
+ const parsed = import_types6.GraphNodeSchema.safeParse(node.attributes);
4200
4293
  if (!parsed.success) {
4201
4294
  issues.push(`node "${node.key}": ${describeZodIssues(parsed.error)}`);
4202
4295
  continue;
@@ -4205,7 +4298,7 @@ function mergeSnapshot(graph, snapshot) {
4205
4298
  }
4206
4299
  for (const edge of incomingEdges) {
4207
4300
  if (edge.attributes === void 0) continue;
4208
- const parsed = import_types5.GraphEdgeSchema.safeParse(edge.attributes);
4301
+ const parsed = import_types6.GraphEdgeSchema.safeParse(edge.attributes);
4209
4302
  if (!parsed.success) {
4210
4303
  const label = edge.key ?? `${edge.source}->${edge.target}`;
4211
4304
  issues.push(`edge "${label}": ${describeZodIssues(parsed.error)}`);
@@ -4234,29 +4327,29 @@ function mergeSnapshot(graph, snapshot) {
4234
4327
  }
4235
4328
 
4236
4329
  // src/traverse.ts
4237
- var import_types6 = require("@neat.is/types");
4330
+ var import_types7 = require("@neat.is/types");
4238
4331
  var ROOT_CAUSE_MAX_DEPTH = 5;
4239
4332
  var BLAST_RADIUS_DEFAULT_DEPTH = 10;
4240
4333
  function isFrontierNode(graph, nodeId) {
4241
4334
  if (!graph.hasNode(nodeId)) return false;
4242
4335
  const attrs = graph.getNodeAttributes(nodeId);
4243
- return attrs.type === import_types6.NodeType.FrontierNode;
4336
+ return attrs.type === import_types7.NodeType.FrontierNode;
4244
4337
  }
4245
4338
  function resolveOwningService(graph, nodeId) {
4246
4339
  if (!graph.hasNode(nodeId)) return null;
4247
4340
  const attrs = graph.getNodeAttributes(nodeId);
4248
- if (attrs.type === import_types6.NodeType.ServiceNode) {
4341
+ if (attrs.type === import_types7.NodeType.ServiceNode) {
4249
4342
  return { id: nodeId, svc: attrs };
4250
4343
  }
4251
- if (attrs.type === import_types6.NodeType.FileNode || attrs.type === import_types6.NodeType.SymbolNode) {
4344
+ if (attrs.type === import_types7.NodeType.FileNode || attrs.type === import_types7.NodeType.SymbolNode) {
4252
4345
  for (const edgeId of graph.inboundEdges(nodeId)) {
4253
4346
  const e = graph.getEdgeAttributes(edgeId);
4254
- if (e.type !== import_types6.EdgeType.CONTAINS) continue;
4347
+ if (e.type !== import_types7.EdgeType.CONTAINS) continue;
4255
4348
  const owner = graph.getNodeAttributes(e.source);
4256
- if (owner.type === import_types6.NodeType.ServiceNode) {
4349
+ if (owner.type === import_types7.NodeType.ServiceNode) {
4257
4350
  return { id: e.source, svc: owner };
4258
4351
  }
4259
- if (owner.type === import_types6.NodeType.FileNode) {
4352
+ if (owner.type === import_types7.NodeType.FileNode) {
4260
4353
  return resolveOwningService(graph, e.source);
4261
4354
  }
4262
4355
  }
@@ -4269,7 +4362,7 @@ function bestEdgeBySource(graph, edgeIds) {
4269
4362
  const e = graph.getEdgeAttributes(id);
4270
4363
  if (isFrontierNode(graph, e.source)) continue;
4271
4364
  const cur = best.get(e.source);
4272
- if (!cur || import_types6.PROV_RANK[e.provenance] > import_types6.PROV_RANK[cur.provenance]) {
4365
+ if (!cur || import_types7.PROV_RANK[e.provenance] > import_types7.PROV_RANK[cur.provenance]) {
4273
4366
  best.set(e.source, e);
4274
4367
  }
4275
4368
  }
@@ -4281,7 +4374,7 @@ function bestEdgeByTarget(graph, edgeIds) {
4281
4374
  const e = graph.getEdgeAttributes(id);
4282
4375
  if (isFrontierNode(graph, e.target)) continue;
4283
4376
  const cur = best.get(e.target);
4284
- if (!cur || import_types6.PROV_RANK[e.provenance] > import_types6.PROV_RANK[cur.provenance]) {
4377
+ if (!cur || import_types7.PROV_RANK[e.provenance] > import_types7.PROV_RANK[cur.provenance]) {
4285
4378
  best.set(e.target, e);
4286
4379
  }
4287
4380
  }
@@ -4344,19 +4437,19 @@ function confidenceFromMix(edges, now = Date.now()) {
4344
4437
  function longestIncomingWalk(graph, start, maxDepth) {
4345
4438
  let best = { path: [start], edges: [] };
4346
4439
  const visited = /* @__PURE__ */ new Set([start]);
4347
- function step(node, path58, edges) {
4348
- if (path58.length > best.path.length) {
4349
- best = { path: [...path58], edges: [...edges] };
4440
+ function step(node, path59, edges) {
4441
+ if (path59.length > best.path.length) {
4442
+ best = { path: [...path59], edges: [...edges] };
4350
4443
  }
4351
- if (path58.length - 1 >= maxDepth) return;
4444
+ if (path59.length - 1 >= maxDepth) return;
4352
4445
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
4353
4446
  for (const [srcId, edge] of incoming) {
4354
4447
  if (visited.has(srcId)) continue;
4355
4448
  visited.add(srcId);
4356
- path58.push(srcId);
4449
+ path59.push(srcId);
4357
4450
  edges.push(edge);
4358
- step(srcId, path58, edges);
4359
- path58.pop();
4451
+ step(srcId, path59, edges);
4452
+ path59.pop();
4360
4453
  edges.pop();
4361
4454
  visited.delete(srcId);
4362
4455
  }
@@ -4443,10 +4536,10 @@ function symbolRootCauseShape(graph, origin, walk6) {
4443
4536
  return serviceRootCauseShape(graph, owner.svc, walk6);
4444
4537
  }
4445
4538
  var rootCauseShapes = {
4446
- [import_types6.NodeType.DatabaseNode]: databaseRootCauseShape,
4447
- [import_types6.NodeType.ServiceNode]: serviceRootCauseShape,
4448
- [import_types6.NodeType.FileNode]: fileRootCauseShape,
4449
- [import_types6.NodeType.SymbolNode]: symbolRootCauseShape
4539
+ [import_types7.NodeType.DatabaseNode]: databaseRootCauseShape,
4540
+ [import_types7.NodeType.ServiceNode]: serviceRootCauseShape,
4541
+ [import_types7.NodeType.FileNode]: fileRootCauseShape,
4542
+ [import_types7.NodeType.SymbolNode]: symbolRootCauseShape
4450
4543
  };
4451
4544
  function getRootCause(graph, errorNodeId, errorEvent, incidents) {
4452
4545
  if (!graph.hasNode(errorNodeId)) return null;
@@ -4457,7 +4550,7 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
4457
4550
  const match = shape(graph, origin, walk6);
4458
4551
  if (match) {
4459
4552
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
4460
- return import_types6.RootCauseResultSchema.parse({
4553
+ return import_types7.RootCauseResultSchema.parse({
4461
4554
  rootCauseNode: match.rootCauseNode,
4462
4555
  rootCauseReason: reason,
4463
4556
  traversalPath: walk6.path,
@@ -4467,7 +4560,7 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
4467
4560
  });
4468
4561
  }
4469
4562
  }
4470
- if (origin.type === import_types6.NodeType.ServiceNode) {
4563
+ if (origin.type === import_types7.NodeType.ServiceNode) {
4471
4564
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
4472
4565
  if (crossService) return crossService;
4473
4566
  }
@@ -4507,8 +4600,8 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
4507
4600
  const loc = localizeFromIncidents(nodeId, incidents, errorEvent);
4508
4601
  if (!loc) return null;
4509
4602
  const traversalPath = loc.fileNode ? [nodeId, loc.fileNode] : [nodeId];
4510
- const edgeProvenances = loc.fileNode ? [import_types6.Provenance.OBSERVED] : [];
4511
- return import_types6.RootCauseResultSchema.parse({
4603
+ const edgeProvenances = loc.fileNode ? [import_types7.Provenance.OBSERVED] : [];
4604
+ return import_types7.RootCauseResultSchema.parse({
4512
4605
  rootCauseNode: loc.rootCauseNode,
4513
4606
  rootCauseReason: loc.rootCauseReason,
4514
4607
  traversalPath,
@@ -4518,21 +4611,21 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
4518
4611
  });
4519
4612
  }
4520
4613
  function isFailingCallEdge(e) {
4521
- return e.type === import_types6.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
4614
+ return e.type === import_types7.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
4522
4615
  }
4523
4616
  function callSourcesForService(graph, serviceId7) {
4524
4617
  const ids = [serviceId7];
4525
4618
  for (const edgeId of graph.outboundEdges(serviceId7)) {
4526
4619
  const e = graph.getEdgeAttributes(edgeId);
4527
- if (e.type !== import_types6.EdgeType.CONTAINS) continue;
4620
+ if (e.type !== import_types7.EdgeType.CONTAINS) continue;
4528
4621
  const tgt = graph.getNodeAttributes(e.target);
4529
- if (tgt.type !== import_types6.NodeType.FileNode) continue;
4622
+ if (tgt.type !== import_types7.NodeType.FileNode) continue;
4530
4623
  ids.push(e.target);
4531
4624
  for (const symEdgeId of graph.outboundEdges(e.target)) {
4532
4625
  const se = graph.getEdgeAttributes(symEdgeId);
4533
- if (se.type !== import_types6.EdgeType.CONTAINS) continue;
4626
+ if (se.type !== import_types7.EdgeType.CONTAINS) continue;
4534
4627
  const sym = graph.getNodeAttributes(se.target);
4535
- if (sym.type === import_types6.NodeType.SymbolNode) ids.push(se.target);
4628
+ if (sym.type === import_types7.NodeType.SymbolNode) ids.push(se.target);
4536
4629
  }
4537
4630
  }
4538
4631
  return ids;
@@ -4541,8 +4634,8 @@ function failingCallDominates(e, id, curEdge, curId) {
4541
4634
  const ec = e.signal?.errorCount ?? 0;
4542
4635
  const cc = curEdge.signal?.errorCount ?? 0;
4543
4636
  if (ec !== cc) return ec > cc;
4544
- if (import_types6.PROV_RANK[e.provenance] !== import_types6.PROV_RANK[curEdge.provenance]) {
4545
- return import_types6.PROV_RANK[e.provenance] > import_types6.PROV_RANK[curEdge.provenance];
4637
+ if (import_types7.PROV_RANK[e.provenance] !== import_types7.PROV_RANK[curEdge.provenance]) {
4638
+ return import_types7.PROV_RANK[e.provenance] > import_types7.PROV_RANK[curEdge.provenance];
4546
4639
  }
4547
4640
  return id < curId;
4548
4641
  }
@@ -4563,26 +4656,26 @@ function dominantFailingCall(graph, serviceId7, visited) {
4563
4656
  return best;
4564
4657
  }
4565
4658
  function followFailingCallChain(graph, originServiceId, maxDepth) {
4566
- const path58 = [originServiceId];
4659
+ const path59 = [originServiceId];
4567
4660
  const edges = [];
4568
4661
  const visited = /* @__PURE__ */ new Set([originServiceId]);
4569
4662
  let current = originServiceId;
4570
4663
  for (let depth = 0; depth < maxDepth; depth++) {
4571
4664
  const hop = dominantFailingCall(graph, current, visited);
4572
4665
  if (!hop) break;
4573
- path58.push(hop.nextService);
4666
+ path59.push(hop.nextService);
4574
4667
  edges.push(hop.edge);
4575
4668
  visited.add(hop.nextService);
4576
4669
  current = hop.nextService;
4577
4670
  }
4578
4671
  if (edges.length === 0) return null;
4579
- return { path: path58, edges, culprit: current };
4672
+ return { path: path59, edges, culprit: current };
4580
4673
  }
4581
4674
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
4582
4675
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
4583
4676
  if (!chain) return null;
4584
4677
  const culprit = chain.culprit;
4585
- const path58 = [...chain.path];
4678
+ const path59 = [...chain.path];
4586
4679
  const edgeProvenances = chain.edges.map((e) => e.provenance);
4587
4680
  const baseConfidence = confidenceFromMix(chain.edges);
4588
4681
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -4590,14 +4683,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
4590
4683
  if (loc) {
4591
4684
  let rootCauseNode = culprit;
4592
4685
  if (loc.fileNode) {
4593
- path58.push(loc.fileNode);
4594
- edgeProvenances.push(import_types6.Provenance.OBSERVED);
4686
+ path59.push(loc.fileNode);
4687
+ edgeProvenances.push(import_types7.Provenance.OBSERVED);
4595
4688
  rootCauseNode = loc.fileNode;
4596
4689
  }
4597
- return import_types6.RootCauseResultSchema.parse({
4690
+ return import_types7.RootCauseResultSchema.parse({
4598
4691
  rootCauseNode,
4599
4692
  rootCauseReason: loc.rootCauseReason,
4600
- traversalPath: path58,
4693
+ traversalPath: path59,
4601
4694
  edgeProvenances,
4602
4695
  confidence,
4603
4696
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -4606,10 +4699,10 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
4606
4699
  const lastEdge = chain.edges[chain.edges.length - 1];
4607
4700
  const errs = lastEdge.signal?.errorCount ?? 0;
4608
4701
  const culpritName = culprit.replace(/^service:/, "");
4609
- return import_types6.RootCauseResultSchema.parse({
4702
+ return import_types7.RootCauseResultSchema.parse({
4610
4703
  rootCauseNode: culprit,
4611
4704
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
4612
- traversalPath: path58,
4705
+ traversalPath: path59,
4613
4706
  edgeProvenances,
4614
4707
  confidence,
4615
4708
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -4617,7 +4710,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
4617
4710
  }
4618
4711
  function getBlastRadius(graph, nodeId, maxDepth = BLAST_RADIUS_DEFAULT_DEPTH) {
4619
4712
  if (!graph.hasNode(nodeId)) {
4620
- return import_types6.BlastRadiusResultSchema.parse({ origin: nodeId, affectedNodes: [], totalAffected: 0 });
4713
+ return import_types7.BlastRadiusResultSchema.parse({ origin: nodeId, affectedNodes: [], totalAffected: 0 });
4621
4714
  }
4622
4715
  const seen = /* @__PURE__ */ new Map();
4623
4716
  const queue = [{ nodeId, distance: 0, path: [nodeId], pathEdges: [] }];
@@ -4650,7 +4743,7 @@ function getBlastRadius(graph, nodeId, maxDepth = BLAST_RADIUS_DEFAULT_DEPTH) {
4650
4743
  const affectedNodes = [...seen.values()].sort(
4651
4744
  (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId)
4652
4745
  );
4653
- return import_types6.BlastRadiusResultSchema.parse({
4746
+ return import_types7.BlastRadiusResultSchema.parse({
4654
4747
  origin: nodeId,
4655
4748
  affectedNodes,
4656
4749
  totalAffected: affectedNodes.length
@@ -4660,7 +4753,7 @@ var TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH = 3;
4660
4753
  var TRANSITIVE_DEPENDENCIES_MAX_DEPTH = 10;
4661
4754
  function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH) {
4662
4755
  if (!graph.hasNode(nodeId)) {
4663
- return import_types6.TransitiveDependenciesResultSchema.parse({
4756
+ return import_types7.TransitiveDependenciesResultSchema.parse({
4664
4757
  origin: nodeId,
4665
4758
  depth,
4666
4759
  dependencies: [],
@@ -4672,7 +4765,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
4672
4765
  const enqueued = /* @__PURE__ */ new Set([nodeId]);
4673
4766
  while (queue.length > 0) {
4674
4767
  const frame = queue.shift();
4675
- if (frame.distance > 0 && frame.edge && frame.edge.type !== import_types6.EdgeType.CONTAINS) {
4768
+ if (frame.distance > 0 && frame.edge && frame.edge.type !== import_types7.EdgeType.CONTAINS) {
4676
4769
  seen.set(frame.nodeId, {
4677
4770
  nodeId: frame.nodeId,
4678
4771
  distance: frame.distance,
@@ -4691,7 +4784,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
4691
4784
  const dependencies = [...seen.values()].sort(
4692
4785
  (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId)
4693
4786
  );
4694
- return import_types6.TransitiveDependenciesResultSchema.parse({
4787
+ return import_types7.TransitiveDependenciesResultSchema.parse({
4695
4788
  origin: nodeId,
4696
4789
  depth,
4697
4790
  dependencies,
@@ -4700,7 +4793,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
4700
4793
  }
4701
4794
  function getObservedDependencies(graph, nodeId) {
4702
4795
  if (!graph.hasNode(nodeId)) {
4703
- return import_types6.ObservedDependenciesResultSchema.parse({
4796
+ return import_types7.ObservedDependenciesResultSchema.parse({
4704
4797
  origin: nodeId,
4705
4798
  dependencies: [],
4706
4799
  observed: false,
@@ -4710,12 +4803,12 @@ function getObservedDependencies(graph, nodeId) {
4710
4803
  }
4711
4804
  const attrs = graph.getNodeAttributes(nodeId);
4712
4805
  const scope = [nodeId];
4713
- if (attrs.type === import_types6.NodeType.ServiceNode) {
4806
+ if (attrs.type === import_types7.NodeType.ServiceNode) {
4714
4807
  for (const edgeId of graph.outboundEdges(nodeId)) {
4715
4808
  const e = graph.getEdgeAttributes(edgeId);
4716
- if (e.type !== import_types6.EdgeType.CONTAINS) continue;
4809
+ if (e.type !== import_types7.EdgeType.CONTAINS) continue;
4717
4810
  const owned = graph.getNodeAttributes(e.target);
4718
- if (owned.type === import_types6.NodeType.FileNode) scope.push(e.target);
4811
+ if (owned.type === import_types7.NodeType.FileNode) scope.push(e.target);
4719
4812
  }
4720
4813
  }
4721
4814
  const dependencies = [];
@@ -4724,13 +4817,13 @@ function getObservedDependencies(graph, nodeId) {
4724
4817
  for (const src of scope) {
4725
4818
  for (const edgeId of graph.outboundEdges(src)) {
4726
4819
  const e = graph.getEdgeAttributes(edgeId);
4727
- if (e.type === import_types6.EdgeType.CONTAINS) continue;
4728
- if (e.provenance === import_types6.Provenance.OBSERVED) {
4820
+ if (e.type === import_types7.EdgeType.CONTAINS) continue;
4821
+ if (e.provenance === import_types7.Provenance.OBSERVED) {
4729
4822
  if (!seenEdge.has(e.id)) {
4730
4823
  seenEdge.add(e.id);
4731
4824
  dependencies.push(e);
4732
4825
  }
4733
- } else if (e.provenance === import_types6.Provenance.EXTRACTED) {
4826
+ } else if (e.provenance === import_types7.Provenance.EXTRACTED) {
4734
4827
  hasExtractedOutbound = true;
4735
4828
  }
4736
4829
  }
@@ -4739,14 +4832,14 @@ function getObservedDependencies(graph, nodeId) {
4739
4832
  for (const tgt of scope) {
4740
4833
  for (const edgeId of graph.inboundEdges(tgt)) {
4741
4834
  const e = graph.getEdgeAttributes(edgeId);
4742
- if (e.type === import_types6.EdgeType.CONTAINS) continue;
4743
- if (e.provenance === import_types6.Provenance.OBSERVED) inboundObservedCount += 1;
4835
+ if (e.type === import_types7.EdgeType.CONTAINS) continue;
4836
+ if (e.provenance === import_types7.Provenance.OBSERVED) inboundObservedCount += 1;
4744
4837
  }
4745
4838
  }
4746
4839
  dependencies.sort(
4747
4840
  (a, b) => a.target.localeCompare(b.target) || a.source.localeCompare(b.source) || a.id.localeCompare(b.id)
4748
4841
  );
4749
- return import_types6.ObservedDependenciesResultSchema.parse({
4842
+ return import_types7.ObservedDependenciesResultSchema.parse({
4750
4843
  origin: nodeId,
4751
4844
  dependencies,
4752
4845
  observed: dependencies.length > 0 || inboundObservedCount > 0,
@@ -4760,34 +4853,34 @@ function bucketKey(source, target, type) {
4760
4853
  return `${type}|${source}|${target}`;
4761
4854
  }
4762
4855
  function bucketSourceFor(graph, edge) {
4763
- if (edge.type !== import_types7.EdgeType.CONNECTS_TO) return edge.source;
4764
- const parsed = (0, import_types7.parseFileId)(edge.source);
4856
+ if (edge.type !== import_types8.EdgeType.CONNECTS_TO) return edge.source;
4857
+ const parsed = (0, import_types8.parseFileId)(edge.source);
4765
4858
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
4766
4859
  const target = graph.getNodeAttributes(edge.target);
4767
- if (target.type !== import_types7.NodeType.DatabaseNode) return edge.source;
4768
- return (0, import_types7.serviceId)(parsed.service);
4860
+ if (target.type !== import_types8.NodeType.DatabaseNode) return edge.source;
4861
+ return (0, import_types8.serviceId)(parsed.service);
4769
4862
  }
4770
4863
  function bucketEdges(graph) {
4771
4864
  const buckets2 = /* @__PURE__ */ new Map();
4772
4865
  graph.forEachEdge((id, attrs) => {
4773
4866
  const e = attrs;
4774
- const parsed = (0, import_types7.parseEdgeId)(id);
4867
+ const parsed = (0, import_types8.parseEdgeId)(id);
4775
4868
  const provenance = parsed?.provenance ?? e.provenance;
4776
4869
  const source = bucketSourceFor(graph, e);
4777
4870
  const key = bucketKey(source, e.target, e.type);
4778
4871
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
4779
4872
  switch (provenance) {
4780
- case import_types7.Provenance.EXTRACTED:
4873
+ case import_types8.Provenance.EXTRACTED:
4781
4874
  cur.extracted = e;
4782
4875
  break;
4783
- case import_types7.Provenance.OBSERVED:
4876
+ case import_types8.Provenance.OBSERVED:
4784
4877
  cur.observed = e;
4785
4878
  break;
4786
- case import_types7.Provenance.INFERRED:
4879
+ case import_types8.Provenance.INFERRED:
4787
4880
  cur.inferred = e;
4788
4881
  break;
4789
4882
  default:
4790
- if (e.provenance === import_types7.Provenance.STALE) cur.stale = e;
4883
+ if (e.provenance === import_types8.Provenance.STALE) cur.stale = e;
4791
4884
  }
4792
4885
  buckets2.set(key, cur);
4793
4886
  });
@@ -4796,17 +4889,17 @@ function bucketEdges(graph) {
4796
4889
  function nodeIsFrontier(graph, nodeId) {
4797
4890
  if (!graph.hasNode(nodeId)) return false;
4798
4891
  const attrs = graph.getNodeAttributes(nodeId);
4799
- return attrs.type === import_types7.NodeType.FrontierNode;
4892
+ return attrs.type === import_types8.NodeType.FrontierNode;
4800
4893
  }
4801
4894
  function nodeIsWebsocketChannel(graph, nodeId) {
4802
4895
  if (!graph.hasNode(nodeId)) return false;
4803
4896
  const attrs = graph.getNodeAttributes(nodeId);
4804
- return attrs.type === import_types7.NodeType.WebSocketChannelNode;
4897
+ return attrs.type === import_types8.NodeType.WebSocketChannelNode;
4805
4898
  }
4806
4899
  function nodeIsSymbol(graph, nodeId) {
4807
4900
  if (!graph.hasNode(nodeId)) return false;
4808
4901
  const attrs = graph.getNodeAttributes(nodeId);
4809
- return attrs.type === import_types7.NodeType.SymbolNode;
4902
+ return attrs.type === import_types8.NodeType.SymbolNode;
4810
4903
  }
4811
4904
  function clampConfidence(n) {
4812
4905
  if (!Number.isFinite(n)) return 0;
@@ -4826,14 +4919,14 @@ function gradedConfidence(edge) {
4826
4919
  return clampConfidence(confidenceForEdge(edge));
4827
4920
  }
4828
4921
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
4829
- import_types7.EdgeType.CALLS,
4830
- import_types7.EdgeType.CONNECTS_TO,
4831
- import_types7.EdgeType.PUBLISHES_TO,
4832
- import_types7.EdgeType.CONSUMES_FROM
4922
+ import_types8.EdgeType.CALLS,
4923
+ import_types8.EdgeType.CONNECTS_TO,
4924
+ import_types8.EdgeType.PUBLISHES_TO,
4925
+ import_types8.EdgeType.CONSUMES_FROM
4833
4926
  ]);
4834
4927
  function detectMissingDivergences(graph, bucket) {
4835
4928
  const out = [];
4836
- if (bucket.type === import_types7.EdgeType.CONTAINS) return out;
4929
+ if (bucket.type === import_types8.EdgeType.CONTAINS) return out;
4837
4930
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
4838
4931
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
4839
4932
  if (!nodeIsFrontier(graph, bucket.target)) {
@@ -4875,7 +4968,7 @@ function declaredHostFor(svc) {
4875
4968
  function hasExtractedConfiguredBy(graph, svcId) {
4876
4969
  for (const edgeId of graph.outboundEdges(svcId)) {
4877
4970
  const e = graph.getEdgeAttributes(edgeId);
4878
- if (e.type === import_types7.EdgeType.CONFIGURED_BY && e.provenance === import_types7.Provenance.EXTRACTED) {
4971
+ if (e.type === import_types8.EdgeType.CONFIGURED_BY && e.provenance === import_types8.Provenance.EXTRACTED) {
4879
4972
  return true;
4880
4973
  }
4881
4974
  }
@@ -4888,10 +4981,10 @@ function detectHostMismatch(graph, svcId, svc) {
4888
4981
  const out = [];
4889
4982
  for (const edgeId of graph.outboundEdges(svcId)) {
4890
4983
  const edge = graph.getEdgeAttributes(edgeId);
4891
- if (edge.type !== import_types7.EdgeType.CONNECTS_TO) continue;
4892
- if (edge.provenance !== import_types7.Provenance.OBSERVED) continue;
4984
+ if (edge.type !== import_types8.EdgeType.CONNECTS_TO) continue;
4985
+ if (edge.provenance !== import_types8.Provenance.OBSERVED) continue;
4893
4986
  const target = graph.getNodeAttributes(edge.target);
4894
- if (target.type !== import_types7.NodeType.DatabaseNode) continue;
4987
+ if (target.type !== import_types8.NodeType.DatabaseNode) continue;
4895
4988
  const observedHost = target.host?.trim();
4896
4989
  if (!observedHost) continue;
4897
4990
  if (observedHost === declaredHost) continue;
@@ -4913,10 +5006,10 @@ function detectCompatDivergences(graph, svcId, svc) {
4913
5006
  const deps = svc.dependencies ?? {};
4914
5007
  for (const edgeId of graph.outboundEdges(svcId)) {
4915
5008
  const edge = graph.getEdgeAttributes(edgeId);
4916
- if (edge.type !== import_types7.EdgeType.CONNECTS_TO) continue;
4917
- if (edge.provenance !== import_types7.Provenance.OBSERVED) continue;
5009
+ if (edge.type !== import_types8.EdgeType.CONNECTS_TO) continue;
5010
+ if (edge.provenance !== import_types8.Provenance.OBSERVED) continue;
4918
5011
  const target = graph.getNodeAttributes(edge.target);
4919
- if (target.type !== import_types7.NodeType.DatabaseNode) continue;
5012
+ if (target.type !== import_types8.NodeType.DatabaseNode) continue;
4920
5013
  for (const pair of compatPairs()) {
4921
5014
  if (pair.engine !== target.engine) continue;
4922
5015
  const declared = deps[pair.driver];
@@ -4966,6 +5059,44 @@ function detectCompatDivergences(graph, svcId, svc) {
4966
5059
  }
4967
5060
  return out;
4968
5061
  }
5062
+ var RECOMMENDATION_COLUMN_MISSING_OBSERVED = "Verify the column is exercised in production; a migration that renamed or dropped it may have left a writer declaring the old name.";
5063
+ var RECOMMENDATION_COLUMN_MISSING_EXTRACTED = "The schema or migration is likely behind the code \u2014 production writes a column the declared schema does not carry. Check for a field rename that updated the query but not the model.";
5064
+ function detectColumnDrift(node) {
5065
+ const columns = node.columns;
5066
+ if (!columns || columns.length === 0) return [];
5067
+ const anyDeclared = columns.some(columnIsDeclared);
5068
+ const anyObserved = columns.some(columnIsObserved);
5069
+ if (!anyDeclared || !anyObserved) return [];
5070
+ const out = [];
5071
+ for (const col of columns) {
5072
+ const declared = columnIsDeclared(col);
5073
+ const observed = columnIsObserved(col);
5074
+ if (declared && !observed) {
5075
+ out.push({
5076
+ type: "missing-observed",
5077
+ source: node.id,
5078
+ target: node.id,
5079
+ table: node.id,
5080
+ column: col.name,
5081
+ confidence: clampConfidence(col.confidence),
5082
+ reason: `Schema declares column ${node.name}.${col.name} but no production statement has touched it.`,
5083
+ recommendation: RECOMMENDATION_COLUMN_MISSING_OBSERVED
5084
+ });
5085
+ } else if (observed && !declared) {
5086
+ out.push({
5087
+ type: "missing-extracted",
5088
+ source: node.id,
5089
+ target: node.id,
5090
+ table: node.id,
5091
+ column: col.name,
5092
+ confidence: clampConfidence(col.confidence),
5093
+ reason: `Production touched column ${node.name}.${col.name} but the schema does not declare it.`,
5094
+ recommendation: RECOMMENDATION_COLUMN_MISSING_EXTRACTED
5095
+ });
5096
+ }
5097
+ }
5098
+ return out;
5099
+ }
4969
5100
  function involvesNode(d, nodeId) {
4970
5101
  return d.source === nodeId || d.target === nodeId;
4971
5102
  }
@@ -4975,7 +5106,7 @@ function suppressHostMismatchHalves(all) {
4975
5106
  for (const d of all) {
4976
5107
  if (d.type !== "host-mismatch") continue;
4977
5108
  observedHalf.add(`${d.source}->${d.target}`);
4978
- declaredHalf.add((0, import_types7.databaseId)(d.extractedHost));
5109
+ declaredHalf.add((0, import_types8.databaseId)(d.extractedHost));
4979
5110
  }
4980
5111
  if (observedHalf.size === 0) return all;
4981
5112
  return all.filter((d) => {
@@ -4994,10 +5125,15 @@ function computeDivergences(graph, opts = {}) {
4994
5125
  }
4995
5126
  graph.forEachNode((nodeId, attrs) => {
4996
5127
  const n = attrs;
4997
- if (n.type !== import_types7.NodeType.ServiceNode) return;
4998
- const svc = n;
4999
- for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
5000
- for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
5128
+ if (n.type === import_types8.NodeType.ServiceNode) {
5129
+ const svc = n;
5130
+ for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
5131
+ for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
5132
+ return;
5133
+ }
5134
+ if (n.type === import_types8.NodeType.InfraNode && n.kind === "sql-table") {
5135
+ for (const d of detectColumnDrift(n)) all.push(d);
5136
+ }
5001
5137
  });
5002
5138
  const reconciled = suppressHostMismatchHalves(all);
5003
5139
  let filtered = reconciled;
@@ -5026,9 +5162,12 @@ function computeDivergences(graph, opts = {}) {
5026
5162
  if (lead !== 0) return lead;
5027
5163
  if (a.type !== b.type) return a.type.localeCompare(b.type);
5028
5164
  if (a.source !== b.source) return a.source.localeCompare(b.source);
5029
- return a.target.localeCompare(b.target);
5165
+ if (a.target !== b.target) return a.target.localeCompare(b.target);
5166
+ const ac = "column" in a && a.column ? a.column : "";
5167
+ const bc = "column" in b && b.column ? b.column : "";
5168
+ return ac.localeCompare(bc);
5030
5169
  });
5031
- return import_types7.DivergenceResultSchema.parse({
5170
+ return import_types8.DivergenceResultSchema.parse({
5032
5171
  divergences: filtered,
5033
5172
  totalAffected: filtered.length,
5034
5173
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -5092,7 +5231,7 @@ var import_node_fs12 = require("fs");
5092
5231
  var import_node_path13 = __toESM(require("path"), 1);
5093
5232
  var import_ignore = __toESM(require("ignore"), 1);
5094
5233
  var import_minimatch2 = require("minimatch");
5095
- var import_types9 = require("@neat.is/types");
5234
+ var import_types10 = require("@neat.is/types");
5096
5235
 
5097
5236
  // src/extract/python.ts
5098
5237
  init_cjs_shims();
@@ -5165,7 +5304,7 @@ function pythonToPackage(service) {
5165
5304
  init_cjs_shims();
5166
5305
  var import_node_fs10 = require("fs");
5167
5306
  var import_node_path11 = __toESM(require("path"), 1);
5168
- var import_types8 = require("@neat.is/types");
5307
+ var import_types9 = require("@neat.is/types");
5169
5308
  function parseGoMod(source) {
5170
5309
  const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
5171
5310
  if (!module2) return null;
@@ -5193,8 +5332,8 @@ async function discoverGoService(scanPath, dir) {
5193
5332
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
5194
5333
  const pkg = { name, dependencies: mod.dependencies };
5195
5334
  const node = {
5196
- id: (0, import_types8.serviceId)(name),
5197
- type: import_types8.NodeType.ServiceNode,
5335
+ id: (0, import_types9.serviceId)(name),
5336
+ type: import_types9.NodeType.ServiceNode,
5198
5337
  name,
5199
5338
  language: "go",
5200
5339
  dependencies: mod.dependencies,
@@ -5380,8 +5519,8 @@ async function discoverNodeService(scanPath, dir) {
5380
5519
  const framework = detectJsFramework(pkg);
5381
5520
  const language = await detectJsServiceLanguage(dir, pkg);
5382
5521
  const node = {
5383
- id: (0, import_types9.serviceId)(pkg.name),
5384
- type: import_types9.NodeType.ServiceNode,
5522
+ id: (0, import_types10.serviceId)(pkg.name),
5523
+ type: import_types10.NodeType.ServiceNode,
5385
5524
  name: pkg.name,
5386
5525
  language,
5387
5526
  version: pkg.version,
@@ -5397,8 +5536,8 @@ async function discoverPyService(scanPath, dir) {
5397
5536
  if (!py) return null;
5398
5537
  const pkg = pythonToPackage(py);
5399
5538
  const node = {
5400
- id: (0, import_types9.serviceId)(py.name),
5401
- type: import_types9.NodeType.ServiceNode,
5539
+ id: (0, import_types10.serviceId)(py.name),
5540
+ type: import_types10.NodeType.ServiceNode,
5402
5541
  name: py.name,
5403
5542
  language: "python",
5404
5543
  version: py.version,
@@ -5494,7 +5633,7 @@ init_cjs_shims();
5494
5633
  var import_node_path14 = __toESM(require("path"), 1);
5495
5634
  var import_node_fs13 = require("fs");
5496
5635
  var import_yaml2 = require("yaml");
5497
- var import_types10 = require("@neat.is/types");
5636
+ var import_types11 = require("@neat.is/types");
5498
5637
  var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
5499
5638
  "Service",
5500
5639
  "Deployment",
@@ -5504,7 +5643,7 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
5504
5643
  function addAliases(graph, serviceId7, candidates) {
5505
5644
  if (!graph.hasNode(serviceId7)) return;
5506
5645
  const node = graph.getNodeAttributes(serviceId7);
5507
- if (node.type !== import_types10.NodeType.ServiceNode) return;
5646
+ if (node.type !== import_types11.NodeType.ServiceNode) return;
5508
5647
  const set = new Set(node.aliases ?? []);
5509
5648
  for (const c of candidates) {
5510
5649
  if (!c) continue;
@@ -5685,7 +5824,7 @@ var import_node_path16 = __toESM(require("path"), 1);
5685
5824
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
5686
5825
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
5687
5826
  var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
5688
- var import_types11 = require("@neat.is/types");
5827
+ var import_types12 = require("@neat.is/types");
5689
5828
  var PARSE_CHUNK2 = 16384;
5690
5829
  var GRAMMAR_BY_EXT = {
5691
5830
  ".ts": import_tree_sitter_typescript.default.typescript,
@@ -5776,7 +5915,7 @@ function disambiguate(defs) {
5776
5915
  }
5777
5916
  async function addSymbols(graph, services) {
5778
5917
  const parsers = /* @__PURE__ */ new Map();
5779
- const parserForExt = (ext) => {
5918
+ const parserForExt2 = (ext) => {
5780
5919
  const grammar = GRAMMAR_BY_EXT[ext];
5781
5920
  if (!grammar) return null;
5782
5921
  let parser = parsers.get(ext);
@@ -5792,7 +5931,7 @@ async function addSymbols(graph, services) {
5792
5931
  for (const service of services) {
5793
5932
  const files = await loadSourceFiles(service.dir);
5794
5933
  for (const file of files) {
5795
- const parser = parserForExt(import_node_path16.default.extname(file.path));
5934
+ const parser = parserForExt2(import_node_path16.default.extname(file.path));
5796
5935
  if (!parser) continue;
5797
5936
  const relPath = toPosix(import_node_path16.default.relative(service.dir, file.path));
5798
5937
  let defs;
@@ -5813,11 +5952,11 @@ async function addSymbols(graph, services) {
5813
5952
  nodesAdded += fn;
5814
5953
  edgesAdded += fe;
5815
5954
  for (const { def, disambiguator } of disambiguate(defs)) {
5816
- const sid = (0, import_types11.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
5955
+ const sid = (0, import_types12.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
5817
5956
  if (!graph.hasNode(sid)) {
5818
5957
  const node = {
5819
5958
  id: sid,
5820
- type: import_types11.NodeType.SymbolNode,
5959
+ type: import_types12.NodeType.SymbolNode,
5821
5960
  kind: def.kind,
5822
5961
  qualname: def.qualname,
5823
5962
  span: { startLine: def.startLine, endLine: def.endLine },
@@ -5828,15 +5967,15 @@ async function addSymbols(graph, services) {
5828
5967
  graph.addNode(sid, node);
5829
5968
  nodesAdded++;
5830
5969
  }
5831
- const containsId = (0, import_types11.extractedEdgeId)(fileNodeId, sid, import_types11.EdgeType.CONTAINS);
5970
+ const containsId = (0, import_types12.extractedEdgeId)(fileNodeId, sid, import_types12.EdgeType.CONTAINS);
5832
5971
  if (!graph.hasEdge(containsId)) {
5833
5972
  const edge = {
5834
5973
  id: containsId,
5835
5974
  source: fileNodeId,
5836
5975
  target: sid,
5837
- type: import_types11.EdgeType.CONTAINS,
5838
- provenance: import_types11.Provenance.EXTRACTED,
5839
- confidence: (0, import_types11.confidenceForExtracted)("structural"),
5976
+ type: import_types12.EdgeType.CONTAINS,
5977
+ provenance: import_types12.Provenance.EXTRACTED,
5978
+ confidence: (0, import_types12.confidenceForExtracted)("structural"),
5840
5979
  evidence: {
5841
5980
  file: relPath,
5842
5981
  line: def.startLine,
@@ -5856,7 +5995,7 @@ async function addSymbols(graph, services) {
5856
5995
  init_cjs_shims();
5857
5996
  var import_node_path18 = __toESM(require("path"), 1);
5858
5997
  var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
5859
- var import_types13 = require("@neat.is/types");
5998
+ var import_types14 = require("@neat.is/types");
5860
5999
 
5861
6000
  // src/extract/imports.ts
5862
6001
  init_cjs_shims();
@@ -5866,7 +6005,7 @@ var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
5866
6005
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
5867
6006
  var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
5868
6007
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
5869
- var import_types12 = require("@neat.is/types");
6008
+ var import_types13 = require("@neat.is/types");
5870
6009
  var PARSE_CHUNK3 = 16384;
5871
6010
  function parseSource3(parser, source) {
5872
6011
  return parser.parse(
@@ -6140,17 +6279,17 @@ async function resolveGoImport(specifier, modulePath, serviceDir) {
6140
6279
  return toPosix(import_node_path17.default.relative(serviceDir, candidates[0]));
6141
6280
  }
6142
6281
  function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
6143
- const importeeFileId = (0, import_types12.fileId)(serviceName, importeeRelPath);
6282
+ const importeeFileId = (0, import_types13.fileId)(serviceName, importeeRelPath);
6144
6283
  if (!graph.hasNode(importeeFileId)) return 0;
6145
- const edgeId = (0, import_types12.extractedEdgeId)(importerFileId, importeeFileId, import_types12.EdgeType.IMPORTS);
6284
+ const edgeId = (0, import_types13.extractedEdgeId)(importerFileId, importeeFileId, import_types13.EdgeType.IMPORTS);
6146
6285
  if (graph.hasEdge(edgeId)) return 0;
6147
6286
  const edge = {
6148
6287
  id: edgeId,
6149
6288
  source: importerFileId,
6150
6289
  target: importeeFileId,
6151
- type: import_types12.EdgeType.IMPORTS,
6152
- provenance: import_types12.Provenance.EXTRACTED,
6153
- confidence: (0, import_types12.confidenceForExtracted)("structural"),
6290
+ type: import_types13.EdgeType.IMPORTS,
6291
+ provenance: import_types13.Provenance.EXTRACTED,
6292
+ confidence: (0, import_types13.confidenceForExtracted)("structural"),
6154
6293
  evidence: { file: importerRelPath, line, snippet: snippet2 }
6155
6294
  };
6156
6295
  graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
@@ -6167,7 +6306,7 @@ async function addImports(graph, services) {
6167
6306
  for (const file of files) {
6168
6307
  if (isTestPath(file.path)) continue;
6169
6308
  const relFile = toPosix(import_node_path17.default.relative(service.dir, file.path));
6170
- const importerFileId = (0, import_types12.fileId)(service.pkg.name, relFile);
6309
+ const importerFileId = (0, import_types13.fileId)(service.pkg.name, relFile);
6171
6310
  const isPython = import_node_path17.default.extname(file.path) === ".py";
6172
6311
  const isGo = import_node_path17.default.extname(file.path) === ".go";
6173
6312
  if (isGo) {
@@ -6328,7 +6467,7 @@ function stringInner(node) {
6328
6467
  }
6329
6468
  async function addSymbolEdges(graph, services) {
6330
6469
  const parsers = /* @__PURE__ */ new Map();
6331
- const parserForExt = (ext) => {
6470
+ const parserForExt2 = (ext) => {
6332
6471
  const grammar = GRAMMAR_BY_EXT[ext];
6333
6472
  if (!grammar) return null;
6334
6473
  let parser = parsers.get(ext);
@@ -6345,7 +6484,7 @@ async function addSymbolEdges(graph, services) {
6345
6484
  const tsPaths = await loadTsPathConfig(service.dir);
6346
6485
  const files = await loadSourceFiles(service.dir);
6347
6486
  for (const file of files) {
6348
- const parser = parserForExt(import_node_path18.default.extname(file.path));
6487
+ const parser = parserForExt2(import_node_path18.default.extname(file.path));
6349
6488
  if (!parser) continue;
6350
6489
  const relPath = toPosix(import_node_path18.default.relative(service.dir, file.path));
6351
6490
  const fileDir = import_node_path18.default.dirname(file.path);
@@ -6358,7 +6497,7 @@ async function addSymbolEdges(graph, services) {
6358
6497
  }
6359
6498
  const disambiguated = disambiguate(collectSymbolDefs(root));
6360
6499
  const locals = disambiguated.map(({ def, disambiguator }) => ({
6361
- sid: (0, import_types13.symbolId)(serviceName, relPath, def.qualname, disambiguator),
6500
+ sid: (0, import_types14.symbolId)(serviceName, relPath, def.qualname, disambiguator),
6362
6501
  qualname: def.qualname,
6363
6502
  kind: def.kind,
6364
6503
  startLine: def.startLine,
@@ -6387,10 +6526,10 @@ async function addSymbolEdges(graph, services) {
6387
6526
  if (local) return local.kind === wantKind ? local.sid : null;
6388
6527
  const imp = resolvedImports.get(name);
6389
6528
  if (imp) {
6390
- const candidate = (0, import_types13.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
6529
+ const candidate = (0, import_types14.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
6391
6530
  if (graph.hasNode(candidate)) {
6392
6531
  const node = graph.getNodeAttributes(candidate);
6393
- if (node.type === import_types13.NodeType.SymbolNode && node.kind === wantKind) return candidate;
6532
+ if (node.type === import_types14.NodeType.SymbolNode && node.kind === wantKind) return candidate;
6394
6533
  }
6395
6534
  }
6396
6535
  return null;
@@ -6417,7 +6556,7 @@ async function addSymbolEdges(graph, services) {
6417
6556
  sourceSid: self.sid,
6418
6557
  targetName: ext.name,
6419
6558
  wantKind: "class",
6420
- edgeType: import_types13.EdgeType.INHERITS,
6559
+ edgeType: import_types14.EdgeType.INHERITS,
6421
6560
  line: ext.line
6422
6561
  });
6423
6562
  }
@@ -6426,7 +6565,7 @@ async function addSymbolEdges(graph, services) {
6426
6565
  sourceSid: self.sid,
6427
6566
  targetName: impl.name,
6428
6567
  wantKind: "class",
6429
- edgeType: import_types13.EdgeType.IMPLEMENTS,
6568
+ edgeType: import_types14.EdgeType.IMPLEMENTS,
6430
6569
  line: impl.line
6431
6570
  });
6432
6571
  }
@@ -6442,7 +6581,7 @@ async function addSymbolEdges(graph, services) {
6442
6581
  sourceSid: caller.sid,
6443
6582
  targetName: fn.text,
6444
6583
  wantKind: "function",
6445
- edgeType: import_types13.EdgeType.CALLS,
6584
+ edgeType: import_types14.EdgeType.CALLS,
6446
6585
  line
6447
6586
  });
6448
6587
  }
@@ -6458,15 +6597,15 @@ async function addSymbolEdges(graph, services) {
6458
6597
  const targetSid = resolveTarget(req.targetName, req.wantKind);
6459
6598
  if (!targetSid) continue;
6460
6599
  if (targetSid === req.sourceSid) continue;
6461
- const edgeId = (0, import_types13.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
6600
+ const edgeId = (0, import_types14.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
6462
6601
  if (graph.hasEdge(edgeId)) continue;
6463
6602
  const edge = {
6464
6603
  id: edgeId,
6465
6604
  source: req.sourceSid,
6466
6605
  target: targetSid,
6467
6606
  type: req.edgeType,
6468
- provenance: import_types13.Provenance.EXTRACTED,
6469
- confidence: (0, import_types13.confidenceForExtracted)("structural"),
6607
+ provenance: import_types14.Provenance.EXTRACTED,
6608
+ confidence: (0, import_types14.confidenceForExtracted)("structural"),
6470
6609
  evidence: {
6471
6610
  file: relPath,
6472
6611
  line: req.line,
@@ -6484,7 +6623,7 @@ async function addSymbolEdges(graph, services) {
6484
6623
  // src/extract/databases/index.ts
6485
6624
  init_cjs_shims();
6486
6625
  var import_node_path26 = __toESM(require("path"), 1);
6487
- var import_types14 = require("@neat.is/types");
6626
+ var import_types15 = require("@neat.is/types");
6488
6627
 
6489
6628
  // src/extract/databases/db-config-yaml.ts
6490
6629
  init_cjs_shims();
@@ -6988,8 +7127,8 @@ function compatibleDriversFor(engine) {
6988
7127
  }
6989
7128
  function toDatabaseNode(config) {
6990
7129
  return {
6991
- id: (0, import_types14.databaseId)(config.host),
6992
- type: import_types14.NodeType.DatabaseNode,
7130
+ id: (0, import_types15.databaseId)(config.host),
7131
+ type: import_types15.NodeType.DatabaseNode,
6993
7132
  name: config.database || config.host,
6994
7133
  engine: config.engine,
6995
7134
  engineVersion: config.engineVersion,
@@ -7130,12 +7269,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
7130
7269
  edgesAdded += fe;
7131
7270
  const evidenceFile = toPosix(import_node_path26.default.relative(scanPath, config.sourceFile));
7132
7271
  const edge = {
7133
- id: (0, import_types2.extractedEdgeId)(fileNodeId, dbNode.id, import_types14.EdgeType.CONNECTS_TO),
7272
+ id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types15.EdgeType.CONNECTS_TO),
7134
7273
  source: fileNodeId,
7135
7274
  target: dbNode.id,
7136
- type: import_types14.EdgeType.CONNECTS_TO,
7137
- provenance: import_types14.Provenance.EXTRACTED,
7138
- confidence: (0, import_types14.confidenceForExtracted)("structural"),
7275
+ type: import_types15.EdgeType.CONNECTS_TO,
7276
+ provenance: import_types15.Provenance.EXTRACTED,
7277
+ confidence: (0, import_types15.confidenceForExtracted)("structural"),
7139
7278
  evidence: { file: evidenceFile }
7140
7279
  };
7141
7280
  if (!graph.hasEdge(edge.id)) {
@@ -7147,11 +7286,11 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
7147
7286
  const primary = allConfigs[0];
7148
7287
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
7149
7288
  const relPath = import_node_path26.default.relative(scanPath, primary.sourceFile);
7150
- const cfgId = (0, import_types14.configId)(relPath);
7289
+ const cfgId = (0, import_types15.configId)(relPath);
7151
7290
  if (!graph.hasNode(cfgId)) {
7152
7291
  const cfgNode = {
7153
7292
  id: cfgId,
7154
- type: import_types14.NodeType.ConfigNode,
7293
+ type: import_types15.NodeType.ConfigNode,
7155
7294
  name: import_node_path26.default.basename(primary.sourceFile),
7156
7295
  path: relPath,
7157
7296
  fileType: isConfigFile(import_node_path26.default.basename(primary.sourceFile)).fileType || "config"
@@ -7160,12 +7299,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
7160
7299
  nodesAdded++;
7161
7300
  }
7162
7301
  const cfgEdge = {
7163
- id: (0, import_types2.extractedEdgeId)(service.node.id, cfgId, import_types14.EdgeType.CONFIGURED_BY),
7302
+ id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types15.EdgeType.CONFIGURED_BY),
7164
7303
  source: service.node.id,
7165
7304
  target: cfgId,
7166
- type: import_types14.EdgeType.CONFIGURED_BY,
7167
- provenance: import_types14.Provenance.EXTRACTED,
7168
- confidence: (0, import_types14.confidenceForExtracted)("structural"),
7305
+ type: import_types15.EdgeType.CONFIGURED_BY,
7306
+ provenance: import_types15.Provenance.EXTRACTED,
7307
+ confidence: (0, import_types15.confidenceForExtracted)("structural"),
7169
7308
  evidence: { file: toPosix(relPath) }
7170
7309
  };
7171
7310
  if (!graph.hasEdge(cfgEdge.id)) {
@@ -7197,7 +7336,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
7197
7336
  init_cjs_shims();
7198
7337
  var import_node_fs17 = require("fs");
7199
7338
  var import_node_path27 = __toESM(require("path"), 1);
7200
- var import_types15 = require("@neat.is/types");
7339
+ var import_types16 = require("@neat.is/types");
7201
7340
  async function walkConfigFiles(dir) {
7202
7341
  const out = [];
7203
7342
  async function walk6(current) {
@@ -7224,8 +7363,8 @@ async function addConfigNodes(graph, services, scanPath) {
7224
7363
  for (const file of configFiles) {
7225
7364
  const relPath = import_node_path27.default.relative(scanPath, file);
7226
7365
  const node = {
7227
- id: (0, import_types15.configId)(relPath),
7228
- type: import_types15.NodeType.ConfigNode,
7366
+ id: (0, import_types16.configId)(relPath),
7367
+ type: import_types16.NodeType.ConfigNode,
7229
7368
  name: import_node_path27.default.basename(file),
7230
7369
  path: relPath,
7231
7370
  fileType: isConfigFile(import_node_path27.default.basename(file)).fileType
@@ -7244,12 +7383,12 @@ async function addConfigNodes(graph, services, scanPath) {
7244
7383
  nodesAdded += fn;
7245
7384
  edgesAdded += fe;
7246
7385
  const edge = {
7247
- id: (0, import_types2.extractedEdgeId)(fileNodeId, node.id, import_types15.EdgeType.CONFIGURED_BY),
7386
+ id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types16.EdgeType.CONFIGURED_BY),
7248
7387
  source: fileNodeId,
7249
7388
  target: node.id,
7250
- type: import_types15.EdgeType.CONFIGURED_BY,
7251
- provenance: import_types15.Provenance.EXTRACTED,
7252
- confidence: (0, import_types15.confidenceForExtracted)("structural"),
7389
+ type: import_types16.EdgeType.CONFIGURED_BY,
7390
+ provenance: import_types16.Provenance.EXTRACTED,
7391
+ confidence: (0, import_types16.confidenceForExtracted)("structural"),
7253
7392
  evidence: { file: relPath.split(import_node_path27.default.sep).join("/") }
7254
7393
  };
7255
7394
  if (!graph.hasEdge(edge.id)) {
@@ -7265,7 +7404,7 @@ async function addConfigNodes(graph, services, scanPath) {
7265
7404
  init_cjs_shims();
7266
7405
  var import_node_fs18 = require("fs");
7267
7406
  var import_node_path28 = __toESM(require("path"), 1);
7268
- var import_types16 = require("@neat.is/types");
7407
+ var import_types17 = require("@neat.is/types");
7269
7408
  var PROTO_EXTENSION = ".proto";
7270
7409
  function packageOf(content) {
7271
7410
  const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
@@ -7343,11 +7482,11 @@ async function addGrpcMethods(graph, services) {
7343
7482
  }
7344
7483
  if (methods.length === 0) continue;
7345
7484
  for (const method of methods) {
7346
- const mid = (0, import_types16.grpcMethodId)(method.rpcService, method.rpcMethod);
7485
+ const mid = (0, import_types17.grpcMethodId)(method.rpcService, method.rpcMethod);
7347
7486
  if (!graph.hasNode(mid)) {
7348
7487
  const node = {
7349
7488
  id: mid,
7350
- type: import_types16.NodeType.GrpcMethodNode,
7489
+ type: import_types17.NodeType.GrpcMethodNode,
7351
7490
  name: `${method.rpcService}/${method.rpcMethod}`,
7352
7491
  rpcService: method.rpcService,
7353
7492
  rpcMethod: method.rpcMethod,
@@ -7358,15 +7497,15 @@ async function addGrpcMethods(graph, services) {
7358
7497
  graph.addNode(mid, node);
7359
7498
  nodesAdded++;
7360
7499
  }
7361
- const containsId = (0, import_types16.extractedEdgeId)(service.node.id, mid, import_types16.EdgeType.CONTAINS);
7500
+ const containsId = (0, import_types17.extractedEdgeId)(service.node.id, mid, import_types17.EdgeType.CONTAINS);
7362
7501
  if (!graph.hasEdge(containsId)) {
7363
7502
  const edge = {
7364
7503
  id: containsId,
7365
7504
  source: service.node.id,
7366
7505
  target: mid,
7367
- type: import_types16.EdgeType.CONTAINS,
7368
- provenance: import_types16.Provenance.EXTRACTED,
7369
- confidence: (0, import_types16.confidenceForExtracted)("structural"),
7506
+ type: import_types17.EdgeType.CONTAINS,
7507
+ provenance: import_types17.Provenance.EXTRACTED,
7508
+ confidence: (0, import_types17.confidenceForExtracted)("structural"),
7370
7509
  evidence: {
7371
7510
  file: relFile,
7372
7511
  line: method.line,
@@ -7384,7 +7523,7 @@ async function addGrpcMethods(graph, services) {
7384
7523
 
7385
7524
  // src/extract/calls/index.ts
7386
7525
  init_cjs_shims();
7387
- var import_types28 = require("@neat.is/types");
7526
+ var import_types30 = require("@neat.is/types");
7388
7527
 
7389
7528
  // src/extract/calls/http.ts
7390
7529
  init_cjs_shims();
@@ -7392,7 +7531,7 @@ var import_node_path29 = __toESM(require("path"), 1);
7392
7531
  var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
7393
7532
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
7394
7533
  var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
7395
- var import_types17 = require("@neat.is/types");
7534
+ var import_types18 = require("@neat.is/types");
7396
7535
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
7397
7536
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
7398
7537
  function isInsideJsxExternalLink(node) {
@@ -7478,7 +7617,7 @@ async function addHttpCallEdges(graph, services) {
7478
7617
  const dedupKey = `${relFile}|${targetId}`;
7479
7618
  if (seen.has(dedupKey)) continue;
7480
7619
  seen.add(dedupKey);
7481
- const confidence = (0, import_types17.confidenceForExtracted)("url-literal-service-target");
7620
+ const confidence = (0, import_types18.confidenceForExtracted)("url-literal-service-target");
7482
7621
  const ev = {
7483
7622
  file: relFile,
7484
7623
  line: site.line,
@@ -7492,25 +7631,25 @@ async function addHttpCallEdges(graph, services) {
7492
7631
  );
7493
7632
  nodesAdded += n;
7494
7633
  edgesAdded += e;
7495
- if (!(0, import_types17.passesExtractedFloor)(confidence)) {
7634
+ if (!(0, import_types18.passesExtractedFloor)(confidence)) {
7496
7635
  noteExtractedDropped({
7497
7636
  source: fileNodeId,
7498
7637
  target: targetId,
7499
- type: import_types17.EdgeType.CALLS,
7638
+ type: import_types18.EdgeType.CALLS,
7500
7639
  confidence,
7501
7640
  confidenceKind: "url-literal-service-target",
7502
7641
  evidence: ev
7503
7642
  });
7504
7643
  continue;
7505
7644
  }
7506
- const edgeId = (0, import_types2.extractedEdgeId)(fileNodeId, targetId, import_types17.EdgeType.CALLS);
7645
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types18.EdgeType.CALLS);
7507
7646
  if (!graph.hasEdge(edgeId)) {
7508
7647
  const edge = {
7509
7648
  id: edgeId,
7510
7649
  source: fileNodeId,
7511
7650
  target: targetId,
7512
- type: import_types17.EdgeType.CALLS,
7513
- provenance: import_types17.Provenance.EXTRACTED,
7651
+ type: import_types18.EdgeType.CALLS,
7652
+ provenance: import_types18.Provenance.EXTRACTED,
7514
7653
  confidence,
7515
7654
  evidence: ev
7516
7655
  };
@@ -7528,7 +7667,7 @@ init_cjs_shims();
7528
7667
  var import_node_path30 = __toESM(require("path"), 1);
7529
7668
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
7530
7669
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
7531
- var import_types18 = require("@neat.is/types");
7670
+ var import_types19 = require("@neat.is/types");
7532
7671
  var PARSE_CHUNK5 = 16384;
7533
7672
  function parseSource5(parser, source) {
7534
7673
  return parser.parse(
@@ -7693,9 +7832,9 @@ function buildRouteIndex(graph) {
7693
7832
  const index = /* @__PURE__ */ new Map();
7694
7833
  graph.forEachNode((_id, attrs) => {
7695
7834
  const node = attrs;
7696
- if (node.type !== import_types18.NodeType.RouteNode) return;
7835
+ if (node.type !== import_types19.NodeType.RouteNode) return;
7697
7836
  const route = attrs;
7698
- const owner = (0, import_types18.serviceId)(route.service);
7837
+ const owner = (0, import_types19.serviceId)(route.service);
7699
7838
  const entry = {
7700
7839
  method: route.method.toUpperCase(),
7701
7840
  normalizedPath: normalizePathTemplate(route.pathTemplate),
@@ -7753,7 +7892,7 @@ async function addRouteCallEdges(graph, services) {
7753
7892
  );
7754
7893
  nodesAdded += n;
7755
7894
  edgesAdded += e;
7756
- const confidence = (0, import_types18.confidenceForExtracted)("verified-call-site");
7895
+ const confidence = (0, import_types19.confidenceForExtracted)("verified-call-site");
7757
7896
  const ev = {
7758
7897
  file: relFile,
7759
7898
  line: site.line,
@@ -7761,25 +7900,25 @@ async function addRouteCallEdges(graph, services) {
7761
7900
  method: site.method ?? match.method,
7762
7901
  pathTemplate: site.pathTemplate
7763
7902
  };
7764
- if (!(0, import_types18.passesExtractedFloor)(confidence)) {
7903
+ if (!(0, import_types19.passesExtractedFloor)(confidence)) {
7765
7904
  noteExtractedDropped({
7766
7905
  source: fileNodeId,
7767
7906
  target: match.routeNodeId,
7768
- type: import_types18.EdgeType.CALLS,
7907
+ type: import_types19.EdgeType.CALLS,
7769
7908
  confidence,
7770
7909
  confidenceKind: "verified-call-site",
7771
7910
  evidence: ev
7772
7911
  });
7773
7912
  continue;
7774
7913
  }
7775
- const edgeId = (0, import_types2.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types18.EdgeType.CALLS);
7914
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types19.EdgeType.CALLS);
7776
7915
  if (!graph.hasEdge(edgeId)) {
7777
7916
  const edge = {
7778
7917
  id: edgeId,
7779
7918
  source: fileNodeId,
7780
7919
  target: match.routeNodeId,
7781
- type: import_types18.EdgeType.CALLS,
7782
- provenance: import_types18.Provenance.EXTRACTED,
7920
+ type: import_types19.EdgeType.CALLS,
7921
+ provenance: import_types19.Provenance.EXTRACTED,
7783
7922
  confidence,
7784
7923
  evidence: ev
7785
7924
  };
@@ -7795,7 +7934,7 @@ async function addRouteCallEdges(graph, services) {
7795
7934
  // src/extract/calls/kafka.ts
7796
7935
  init_cjs_shims();
7797
7936
  var import_node_path31 = __toESM(require("path"), 1);
7798
- var import_types19 = require("@neat.is/types");
7937
+ var import_types20 = require("@neat.is/types");
7799
7938
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
7800
7939
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
7801
7940
  function findAll(re, text) {
@@ -7816,7 +7955,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
7816
7955
  seen.add(key);
7817
7956
  const line = lineOf(file.content, topic);
7818
7957
  out.push({
7819
- infraId: (0, import_types19.infraId)("kafka-topic", topic),
7958
+ infraId: (0, import_types20.infraId)("kafka-topic", topic),
7820
7959
  name: topic,
7821
7960
  kind: "kafka-topic",
7822
7961
  edgeType,
@@ -7839,7 +7978,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
7839
7978
  // src/extract/calls/redis.ts
7840
7979
  init_cjs_shims();
7841
7980
  var import_node_path32 = __toESM(require("path"), 1);
7842
- var import_types20 = require("@neat.is/types");
7981
+ var import_types21 = require("@neat.is/types");
7843
7982
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
7844
7983
  function redisEndpointsFromFile(file, serviceDir) {
7845
7984
  const out = [];
@@ -7852,7 +7991,7 @@ function redisEndpointsFromFile(file, serviceDir) {
7852
7991
  seen.add(host);
7853
7992
  const line = lineOf(file.content, host);
7854
7993
  out.push({
7855
- infraId: (0, import_types20.infraId)("redis", host),
7994
+ infraId: (0, import_types21.infraId)("redis", host),
7856
7995
  name: host,
7857
7996
  kind: "redis",
7858
7997
  edgeType: "CALLS",
@@ -7873,7 +8012,7 @@ function redisEndpointsFromFile(file, serviceDir) {
7873
8012
  // src/extract/calls/aws.ts
7874
8013
  init_cjs_shims();
7875
8014
  var import_node_path33 = __toESM(require("path"), 1);
7876
- var import_types21 = require("@neat.is/types");
8015
+ var import_types22 = require("@neat.is/types");
7877
8016
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
7878
8017
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
7879
8018
  function hasMarker(text, markers) {
@@ -7897,7 +8036,7 @@ function awsEndpointsFromFile(file, serviceDir) {
7897
8036
  seen.add(key);
7898
8037
  const line = lineOf(file.content, name);
7899
8038
  out.push({
7900
- infraId: (0, import_types21.infraId)(kind, name),
8039
+ infraId: (0, import_types22.infraId)(kind, name),
7901
8040
  name,
7902
8041
  kind,
7903
8042
  edgeType: "CALLS",
@@ -7932,7 +8071,7 @@ function awsEndpointsFromFile(file, serviceDir) {
7932
8071
  // src/extract/calls/grpc.ts
7933
8072
  init_cjs_shims();
7934
8073
  var import_node_path34 = __toESM(require("path"), 1);
7935
- var import_types22 = require("@neat.is/types");
8074
+ var import_types23 = require("@neat.is/types");
7936
8075
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
7937
8076
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
7938
8077
  var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
@@ -7981,7 +8120,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
7981
8120
  const { kind } = classified;
7982
8121
  const line = lineOf(file.content, m[0]);
7983
8122
  out.push({
7984
- infraId: (0, import_types22.infraId)(kind, name),
8123
+ infraId: (0, import_types23.infraId)(kind, name),
7985
8124
  name,
7986
8125
  kind,
7987
8126
  edgeType: "CALLS",
@@ -8002,7 +8141,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
8002
8141
  // src/extract/calls/supabase.ts
8003
8142
  init_cjs_shims();
8004
8143
  var import_node_path35 = __toESM(require("path"), 1);
8005
- var import_types23 = require("@neat.is/types");
8144
+ var import_types24 = require("@neat.is/types");
8006
8145
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
8007
8146
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
8008
8147
  var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
@@ -8050,7 +8189,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
8050
8189
  seen.add(name);
8051
8190
  const line = lineOf(file.content, m[0]);
8052
8191
  out.push({
8053
- infraId: (0, import_types23.infraId)("supabase", name),
8192
+ infraId: (0, import_types24.infraId)("supabase", name),
8054
8193
  name,
8055
8194
  kind: "supabase",
8056
8195
  edgeType: "CALLS",
@@ -8081,7 +8220,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
8081
8220
  seen.add(key);
8082
8221
  const line = lineOf(file.content, am[0]);
8083
8222
  out.push({
8084
- infraId: (0, import_types23.infraId)(kind, resource),
8223
+ infraId: (0, import_types24.infraId)(kind, resource),
8085
8224
  name: resource,
8086
8225
  kind,
8087
8226
  edgeType: "CALLS",
@@ -8100,7 +8239,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
8100
8239
  // src/extract/calls/mongoose.ts
8101
8240
  init_cjs_shims();
8102
8241
  var import_node_path36 = __toESM(require("path"), 1);
8103
- var import_types24 = require("@neat.is/types");
8242
+ var import_types25 = require("@neat.is/types");
8104
8243
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
8105
8244
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
8106
8245
  var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
@@ -8245,7 +8384,7 @@ function collectModelDefs(content, pluralizeOn) {
8245
8384
  function endpoint(r, file, serviceDir, matchText) {
8246
8385
  const line = lineOf(file.content, matchText);
8247
8386
  return {
8248
- infraId: (0, import_types24.infraId)(r.kind, r.name),
8387
+ infraId: (0, import_types25.infraId)(r.kind, r.name),
8249
8388
  name: r.name,
8250
8389
  kind: r.kind,
8251
8390
  edgeType: "CALLS",
@@ -8398,7 +8537,7 @@ init_cjs_shims();
8398
8537
  var import_node_path37 = __toESM(require("path"), 1);
8399
8538
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
8400
8539
  var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
8401
- var import_types25 = require("@neat.is/types");
8540
+ var import_types26 = require("@neat.is/types");
8402
8541
  var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
8403
8542
  var PARSE_CHUNK6 = 16384;
8404
8543
  function makePyParser4() {
@@ -8458,20 +8597,57 @@ function walk3(node, visit) {
8458
8597
  visit(node);
8459
8598
  for (const c of namedChildren(node)) walk3(c, visit);
8460
8599
  }
8600
+ var COLUMN_BUILDERS = /* @__PURE__ */ new Set(["Column", "mapped_column"]);
8601
+ function isColumnBuilder(call) {
8602
+ const fn = call.childForFieldName("function");
8603
+ const t = fn?.text;
8604
+ if (!t) return false;
8605
+ const base = t.includes(".") ? t.slice(t.lastIndexOf(".") + 1) : t;
8606
+ return COLUMN_BUILDERS.has(base);
8607
+ }
8608
+ function firstPositionalString(call) {
8609
+ const args = call.childForFieldName("arguments");
8610
+ if (!args) return null;
8611
+ for (const arg of namedChildren(args)) {
8612
+ if (arg.type === "keyword_argument") continue;
8613
+ return arg.type === "string" ? pyStaticStringText2(arg) : null;
8614
+ }
8615
+ return null;
8616
+ }
8617
+ function columnsFromClassBody(body) {
8618
+ const out = [];
8619
+ const seen = /* @__PURE__ */ new Set();
8620
+ for (const stmt of namedChildren(body)) {
8621
+ if (stmt.type !== "expression_statement") continue;
8622
+ const assign = stmt.namedChild(0);
8623
+ if (assign?.type !== "assignment") continue;
8624
+ const left = assign.childForFieldName("left");
8625
+ if (left?.type !== "identifier") continue;
8626
+ const right = assign.childForFieldName("right");
8627
+ if (right?.type !== "call" || !isColumnBuilder(right)) continue;
8628
+ const name = firstPositionalString(right) ?? left.text;
8629
+ if (name && !seen.has(name)) {
8630
+ seen.add(name);
8631
+ out.push(name);
8632
+ }
8633
+ }
8634
+ return out;
8635
+ }
8461
8636
  function sqlalchemyEndpointsFromFile(file, serviceDir) {
8462
8637
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
8463
8638
  const tree = parseSource6(makePyParser4(), file.content);
8464
8639
  const out = [];
8465
8640
  const seen = /* @__PURE__ */ new Set();
8466
- const push = (name, line) => {
8641
+ const push = (name, line, columns) => {
8467
8642
  if (seen.has(name)) return;
8468
8643
  seen.add(name);
8469
8644
  out.push({
8470
- infraId: (0, import_types25.infraId)("sql-table", name),
8645
+ infraId: (0, import_types26.infraId)("sql-table", name),
8471
8646
  name,
8472
8647
  kind: "sql-table",
8473
8648
  edgeType: "CALLS",
8474
8649
  confidenceKind: "verified-call-site",
8650
+ ...columns && columns.length > 0 ? { columns } : {},
8475
8651
  evidence: {
8476
8652
  file: import_node_path37.default.relative(serviceDir, file.path),
8477
8653
  line,
@@ -8487,11 +8663,12 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
8487
8663
  const line = node.startPosition.row + 1;
8488
8664
  const explicit = explicitTablename(body);
8489
8665
  if (explicit === "computed") return;
8666
+ const columns = columnsFromClassBody(body);
8490
8667
  if (explicit) {
8491
- push(explicit.name, line);
8668
+ push(explicit.name, line, columns);
8492
8669
  return;
8493
8670
  }
8494
- if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
8671
+ if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line, columns);
8495
8672
  return;
8496
8673
  }
8497
8674
  if (node.type === "call") {
@@ -8573,7 +8750,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
8573
8750
  if (seen.has(key)) continue;
8574
8751
  seen.add(key);
8575
8752
  out.push({
8576
- infraId: (0, import_types25.infraId)("sql-table", t),
8753
+ infraId: (0, import_types26.infraId)("sql-table", t),
8577
8754
  name: t,
8578
8755
  kind: "sql-table",
8579
8756
  edgeType: "CALLS",
@@ -8594,7 +8771,7 @@ init_cjs_shims();
8594
8771
  var import_node_path38 = __toESM(require("path"), 1);
8595
8772
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
8596
8773
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
8597
- var import_types26 = require("@neat.is/types");
8774
+ var import_types27 = require("@neat.is/types");
8598
8775
  var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
8599
8776
  var PARSE_CHUNK7 = 16384;
8600
8777
  function makePyParser5() {
@@ -8674,7 +8851,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
8674
8851
  seen.add(table);
8675
8852
  const line = node.startPosition.row + 1;
8676
8853
  out.push({
8677
- infraId: (0, import_types26.infraId)("sql-table", table),
8854
+ infraId: (0, import_types27.infraId)("sql-table", table),
8678
8855
  name: table,
8679
8856
  kind: "sql-table",
8680
8857
  edgeType: "CALLS",
@@ -8685,12 +8862,129 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
8685
8862
  return out;
8686
8863
  }
8687
8864
 
8688
- // src/extract/calls/go.ts
8865
+ // src/extract/calls/drizzle.ts
8689
8866
  init_cjs_shims();
8690
- var import_node_path41 = __toESM(require("path"), 1);
8867
+ var import_node_path39 = __toESM(require("path"), 1);
8691
8868
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
8869
+ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
8870
+ var import_types28 = require("@neat.is/types");
8871
+ var DRIZZLE_IMPORT_RE = /drizzle-orm/;
8872
+ var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
8873
+ function parserForExt(ext) {
8874
+ const p = new import_tree_sitter9.default();
8875
+ p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
8876
+ return p;
8877
+ }
8878
+ function namedChildren3(node) {
8879
+ const out = [];
8880
+ for (let i = 0; i < node.namedChildCount; i++) {
8881
+ const c = node.namedChild(i);
8882
+ if (c) out.push(c);
8883
+ }
8884
+ return out;
8885
+ }
8886
+ function stringLiteralText2(node) {
8887
+ if (!node || node.type !== "string") return null;
8888
+ for (const child of namedChildren3(node)) {
8889
+ if (child.type === "string_fragment") return child.text;
8890
+ }
8891
+ return "";
8892
+ }
8893
+ function firstStringArg(call) {
8894
+ const args = call.childForFieldName("arguments");
8895
+ if (!args) return null;
8896
+ for (const arg of namedChildren3(args)) {
8897
+ if (arg.type === "string") return stringLiteralText2(arg);
8898
+ return null;
8899
+ }
8900
+ return null;
8901
+ }
8902
+ function builderColumnName(value) {
8903
+ let node = value;
8904
+ while (node) {
8905
+ if (node.type === "call_expression") {
8906
+ const fn = node.childForFieldName("function");
8907
+ if (fn?.type === "identifier") return firstStringArg(node);
8908
+ if (fn?.type === "member_expression") {
8909
+ node = fn.childForFieldName("object");
8910
+ continue;
8911
+ }
8912
+ return null;
8913
+ }
8914
+ if (node.type === "member_expression") {
8915
+ node = node.childForFieldName("object");
8916
+ continue;
8917
+ }
8918
+ return null;
8919
+ }
8920
+ return null;
8921
+ }
8922
+ function keyName(key) {
8923
+ if (!key) return null;
8924
+ if (key.type === "property_identifier") return key.text;
8925
+ if (key.type === "string") return stringLiteralText2(key);
8926
+ return null;
8927
+ }
8928
+ function columnsFromObject(obj) {
8929
+ const out = [];
8930
+ const seen = /* @__PURE__ */ new Set();
8931
+ for (const child of namedChildren3(obj)) {
8932
+ if (child.type !== "pair") continue;
8933
+ const value = child.childForFieldName("value");
8934
+ const key = keyName(child.childForFieldName("key"));
8935
+ const name = (value ? builderColumnName(value) : null) ?? key;
8936
+ if (name && !seen.has(name)) {
8937
+ seen.add(name);
8938
+ out.push(name);
8939
+ }
8940
+ }
8941
+ return out;
8942
+ }
8943
+ function drizzleEndpointsFromFile(file, serviceDir) {
8944
+ if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
8945
+ const tree = parseSource2(parserForExt(import_node_path39.default.extname(file.path)), file.content);
8946
+ const out = [];
8947
+ const seen = /* @__PURE__ */ new Set();
8948
+ const walk6 = (node) => {
8949
+ if (node.type === "call_expression") {
8950
+ const fn = node.childForFieldName("function");
8951
+ if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
8952
+ const args = node.childForFieldName("arguments");
8953
+ const argNodes = args ? namedChildren3(args) : [];
8954
+ const tableName = stringLiteralText2(argNodes[0] ?? null);
8955
+ const obj = argNodes[1];
8956
+ if (tableName && obj?.type === "object" && !seen.has(tableName)) {
8957
+ seen.add(tableName);
8958
+ const columns = columnsFromObject(obj);
8959
+ const line = node.startPosition.row + 1;
8960
+ out.push({
8961
+ infraId: (0, import_types28.infraId)("sql-table", tableName),
8962
+ name: tableName,
8963
+ kind: "sql-table",
8964
+ edgeType: "CALLS",
8965
+ confidenceKind: "structural",
8966
+ columns,
8967
+ evidence: {
8968
+ file: import_node_path39.default.relative(serviceDir, file.path),
8969
+ line,
8970
+ snippet: snippet(file.content, line)
8971
+ }
8972
+ });
8973
+ }
8974
+ }
8975
+ }
8976
+ for (const c of namedChildren3(node)) walk6(c);
8977
+ };
8978
+ walk6(tree.rootNode);
8979
+ return out;
8980
+ }
8981
+
8982
+ // src/extract/calls/go.ts
8983
+ init_cjs_shims();
8984
+ var import_node_path42 = __toESM(require("path"), 1);
8985
+ var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
8692
8986
  var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
8693
- var import_types27 = require("@neat.is/types");
8987
+ var import_types29 = require("@neat.is/types");
8694
8988
  init_otel();
8695
8989
  var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
8696
8990
  var PARSE_CHUNK8 = 16384;
@@ -8702,8 +8996,8 @@ function walk5(node, visit) {
8702
8996
  }
8703
8997
  }
8704
8998
  function goSqlEndpointsFromFile(file, serviceDir) {
8705
- if (import_node_path41.default.extname(file.path) !== ".go") return [];
8706
- const parser = new import_tree_sitter9.default();
8999
+ if (import_node_path42.default.extname(file.path) !== ".go") return [];
9000
+ const parser = new import_tree_sitter10.default();
8707
9001
  parser.setLanguage(import_tree_sitter_go3.default);
8708
9002
  const tree = parser.parse(
8709
9003
  (index) => index >= file.content.length ? "" : file.content.slice(index, index + PARSE_CHUNK8)
@@ -8722,12 +9016,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
8722
9016
  if (!table) return;
8723
9017
  const line = node.startPosition.row + 1;
8724
9018
  out.push({
8725
- infraId: (0, import_types27.infraId)("sql-table", table),
9019
+ infraId: (0, import_types29.infraId)("sql-table", table),
8726
9020
  name: table,
8727
9021
  kind: "sql-table",
8728
9022
  edgeType: "CALLS",
8729
9023
  confidenceKind: "verified-call-site",
8730
- evidence: { file: toPosix(import_node_path41.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
9024
+ evidence: { file: toPosix(import_node_path42.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
8731
9025
  });
8732
9026
  });
8733
9027
  return out;
@@ -8737,11 +9031,11 @@ function goSqlEndpointsFromFile(file, serviceDir) {
8737
9031
  function edgeTypeFromEndpoint(ep) {
8738
9032
  switch (ep.edgeType) {
8739
9033
  case "PUBLISHES_TO":
8740
- return import_types28.EdgeType.PUBLISHES_TO;
9034
+ return import_types30.EdgeType.PUBLISHES_TO;
8741
9035
  case "CONSUMES_FROM":
8742
- return import_types28.EdgeType.CONSUMES_FROM;
9036
+ return import_types30.EdgeType.CONSUMES_FROM;
8743
9037
  default:
8744
- return import_types28.EdgeType.CALLS;
9038
+ return import_types30.EdgeType.CALLS;
8745
9039
  }
8746
9040
  }
8747
9041
  function isAwsKind(kind) {
@@ -8767,6 +9061,7 @@ async function addExternalEndpointEdges(graph, services) {
8767
9061
  endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
8768
9062
  endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
8769
9063
  endpoints.push(...djangoOrmEndpointsFromFile(maskedFile, service.dir));
9064
+ endpoints.push(...drizzleEndpointsFromFile(maskedFile, service.dir));
8770
9065
  try {
8771
9066
  endpoints.push(...goSqlEndpointsFromFile(maskedFile, service.dir));
8772
9067
  } catch (err) {
@@ -8781,7 +9076,7 @@ async function addExternalEndpointEdges(graph, services) {
8781
9076
  if (!graph.hasNode(ep.infraId)) {
8782
9077
  const node = {
8783
9078
  id: ep.infraId,
8784
- type: import_types28.NodeType.InfraNode,
9079
+ type: import_types30.NodeType.InfraNode,
8785
9080
  name: ep.name,
8786
9081
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
8787
9082
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -8792,8 +9087,22 @@ async function addExternalEndpointEdges(graph, services) {
8792
9087
  graph.addNode(node.id, node);
8793
9088
  nodesAdded++;
8794
9089
  }
9090
+ if (ep.columns && ep.columns.length > 0) {
9091
+ const node = graph.getNodeAttributes(ep.infraId);
9092
+ if (node.type === import_types30.NodeType.InfraNode) {
9093
+ graph.replaceNodeAttributes(ep.infraId, {
9094
+ ...node,
9095
+ columns: foldColumns(
9096
+ node.columns,
9097
+ ep.columns,
9098
+ import_types30.Provenance.EXTRACTED,
9099
+ (0, import_types30.confidenceForExtracted)(ep.confidenceKind)
9100
+ )
9101
+ });
9102
+ }
9103
+ }
8795
9104
  const edgeType = edgeTypeFromEndpoint(ep);
8796
- const confidence = (0, import_types28.confidenceForExtracted)(ep.confidenceKind);
9105
+ const confidence = (0, import_types30.confidenceForExtracted)(ep.confidenceKind);
8797
9106
  const relFile = toPosix(ep.evidence.file);
8798
9107
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
8799
9108
  graph,
@@ -8803,7 +9112,7 @@ async function addExternalEndpointEdges(graph, services) {
8803
9112
  );
8804
9113
  nodesAdded += n;
8805
9114
  edgesAdded += e;
8806
- if (!(0, import_types28.passesExtractedFloor)(confidence)) {
9115
+ if (!(0, import_types30.passesExtractedFloor)(confidence)) {
8807
9116
  noteExtractedDropped({
8808
9117
  source: fileNodeId,
8809
9118
  target: ep.infraId,
@@ -8814,7 +9123,7 @@ async function addExternalEndpointEdges(graph, services) {
8814
9123
  });
8815
9124
  continue;
8816
9125
  }
8817
- const edgeId = (0, import_types2.extractedEdgeId)(fileNodeId, ep.infraId, edgeType);
9126
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, ep.infraId, edgeType);
8818
9127
  if (seenEdges.has(edgeId)) continue;
8819
9128
  seenEdges.add(edgeId);
8820
9129
  if (!graph.hasEdge(edgeId)) {
@@ -8823,7 +9132,7 @@ async function addExternalEndpointEdges(graph, services) {
8823
9132
  source: fileNodeId,
8824
9133
  target: ep.infraId,
8825
9134
  type: edgeType,
8826
- provenance: import_types28.Provenance.EXTRACTED,
9135
+ provenance: import_types30.Provenance.EXTRACTED,
8827
9136
  confidence,
8828
9137
  evidence: ep.evidence
8829
9138
  };
@@ -8849,16 +9158,16 @@ init_cjs_shims();
8849
9158
 
8850
9159
  // src/extract/infra/docker-compose.ts
8851
9160
  init_cjs_shims();
8852
- var import_node_path42 = __toESM(require("path"), 1);
8853
- var import_types30 = require("@neat.is/types");
9161
+ var import_node_path43 = __toESM(require("path"), 1);
9162
+ var import_types32 = require("@neat.is/types");
8854
9163
 
8855
9164
  // src/extract/infra/shared.ts
8856
9165
  init_cjs_shims();
8857
- var import_types29 = require("@neat.is/types");
9166
+ var import_types31 = require("@neat.is/types");
8858
9167
  function makeInfraNode(kind, name, provider = "self", extras) {
8859
9168
  return {
8860
- id: (0, import_types29.infraId)(kind, name),
8861
- type: import_types29.NodeType.InfraNode,
9169
+ id: (0, import_types31.infraId)(kind, name),
9170
+ type: import_types31.NodeType.InfraNode,
8862
9171
  name,
8863
9172
  provider,
8864
9173
  kind,
@@ -8895,15 +9204,15 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
8895
9204
  nodesAdded++;
8896
9205
  }
8897
9206
  if (node.id === anchorId) return { nodesAdded, edgesAdded };
8898
- const edgeId = (0, import_types2.extractedEdgeId)(anchorId, node.id, edgeType);
9207
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, node.id, edgeType);
8899
9208
  if (!graph.hasEdge(edgeId)) {
8900
9209
  const edge = {
8901
9210
  id: edgeId,
8902
9211
  source: anchorId,
8903
9212
  target: node.id,
8904
9213
  type: edgeType,
8905
- provenance: import_types29.Provenance.EXTRACTED,
8906
- confidence: (0, import_types29.confidenceForExtracted)("structural"),
9214
+ provenance: import_types31.Provenance.EXTRACTED,
9215
+ confidence: (0, import_types31.confidenceForExtracted)("structural"),
8907
9216
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
8908
9217
  };
8909
9218
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8920,7 +9229,7 @@ function dependsOnList(value) {
8920
9229
  }
8921
9230
  function serviceNameToServiceNode(name, services) {
8922
9231
  for (const s of services) {
8923
- if (s.node.name === name || import_node_path42.default.basename(s.dir) === name) return s.node.id;
9232
+ if (s.node.name === name || import_node_path43.default.basename(s.dir) === name) return s.node.id;
8924
9233
  }
8925
9234
  return null;
8926
9235
  }
@@ -8929,7 +9238,7 @@ async function addComposeInfra(graph, scanPath, services) {
8929
9238
  let edgesAdded = 0;
8930
9239
  let composePath = null;
8931
9240
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
8932
- const abs = import_node_path42.default.join(scanPath, name);
9241
+ const abs = import_node_path43.default.join(scanPath, name);
8933
9242
  if (await exists2(abs)) {
8934
9243
  composePath = abs;
8935
9244
  break;
@@ -8942,13 +9251,13 @@ async function addComposeInfra(graph, scanPath, services) {
8942
9251
  } catch (err) {
8943
9252
  recordExtractionError(
8944
9253
  "infra docker-compose",
8945
- import_node_path42.default.relative(scanPath, composePath),
9254
+ import_node_path43.default.relative(scanPath, composePath),
8946
9255
  err
8947
9256
  );
8948
9257
  return { nodesAdded, edgesAdded };
8949
9258
  }
8950
9259
  if (!compose?.services) return { nodesAdded, edgesAdded };
8951
- const evidenceFile = import_node_path42.default.relative(scanPath, composePath).split(import_node_path42.default.sep).join("/");
9260
+ const evidenceFile = import_node_path43.default.relative(scanPath, composePath).split(import_node_path43.default.sep).join("/");
8952
9261
  const composeNameToNodeId = /* @__PURE__ */ new Map();
8953
9262
  for (const [composeName, svc] of Object.entries(compose.services)) {
8954
9263
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -8970,15 +9279,15 @@ async function addComposeInfra(graph, scanPath, services) {
8970
9279
  for (const dep of dependsOnList(svc.depends_on)) {
8971
9280
  const targetId = composeNameToNodeId.get(dep);
8972
9281
  if (!targetId) continue;
8973
- const edgeId = (0, import_types2.extractedEdgeId)(sourceId, targetId, import_types30.EdgeType.DEPENDS_ON);
9282
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types32.EdgeType.DEPENDS_ON);
8974
9283
  if (graph.hasEdge(edgeId)) continue;
8975
9284
  const edge = {
8976
9285
  id: edgeId,
8977
9286
  source: sourceId,
8978
9287
  target: targetId,
8979
- type: import_types30.EdgeType.DEPENDS_ON,
8980
- provenance: import_types30.Provenance.EXTRACTED,
8981
- confidence: (0, import_types30.confidenceForExtracted)("structural"),
9288
+ type: import_types32.EdgeType.DEPENDS_ON,
9289
+ provenance: import_types32.Provenance.EXTRACTED,
9290
+ confidence: (0, import_types32.confidenceForExtracted)("structural"),
8982
9291
  evidence: { file: evidenceFile }
8983
9292
  };
8984
9293
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8990,9 +9299,9 @@ async function addComposeInfra(graph, scanPath, services) {
8990
9299
 
8991
9300
  // src/extract/infra/dockerfile.ts
8992
9301
  init_cjs_shims();
8993
- var import_node_path43 = __toESM(require("path"), 1);
9302
+ var import_node_path44 = __toESM(require("path"), 1);
8994
9303
  var import_node_fs19 = require("fs");
8995
- var import_types31 = require("@neat.is/types");
9304
+ var import_types33 = require("@neat.is/types");
8996
9305
  function readDockerfile(content) {
8997
9306
  let image = null;
8998
9307
  const ports = [];
@@ -9021,7 +9330,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
9021
9330
  let nodesAdded = 0;
9022
9331
  let edgesAdded = 0;
9023
9332
  for (const service of services) {
9024
- const dockerfilePath = import_node_path43.default.join(service.dir, "Dockerfile");
9333
+ const dockerfilePath = import_node_path44.default.join(service.dir, "Dockerfile");
9025
9334
  if (!await exists2(dockerfilePath)) continue;
9026
9335
  let content;
9027
9336
  try {
@@ -9029,7 +9338,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
9029
9338
  } catch (err) {
9030
9339
  recordExtractionError(
9031
9340
  "infra dockerfile",
9032
- import_node_path43.default.relative(scanPath, dockerfilePath),
9341
+ import_node_path44.default.relative(scanPath, dockerfilePath),
9033
9342
  err
9034
9343
  );
9035
9344
  continue;
@@ -9041,8 +9350,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
9041
9350
  graph.addNode(node.id, node);
9042
9351
  nodesAdded++;
9043
9352
  }
9044
- const relDockerfile = toPosix(import_node_path43.default.relative(service.dir, dockerfilePath));
9045
- const evidenceFile = toPosix(import_node_path43.default.relative(scanPath, dockerfilePath));
9353
+ const relDockerfile = toPosix(import_node_path44.default.relative(service.dir, dockerfilePath));
9354
+ const evidenceFile = toPosix(import_node_path44.default.relative(scanPath, dockerfilePath));
9046
9355
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9047
9356
  graph,
9048
9357
  service.pkg.name,
@@ -9051,15 +9360,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
9051
9360
  );
9052
9361
  nodesAdded += fn;
9053
9362
  edgesAdded += fe;
9054
- const edgeId = (0, import_types2.extractedEdgeId)(fileNodeId, node.id, import_types31.EdgeType.RUNS_ON);
9363
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types33.EdgeType.RUNS_ON);
9055
9364
  if (!graph.hasEdge(edgeId)) {
9056
9365
  const edge = {
9057
9366
  id: edgeId,
9058
9367
  source: fileNodeId,
9059
9368
  target: node.id,
9060
- type: import_types31.EdgeType.RUNS_ON,
9061
- provenance: import_types31.Provenance.EXTRACTED,
9062
- confidence: (0, import_types31.confidenceForExtracted)("structural"),
9369
+ type: import_types33.EdgeType.RUNS_ON,
9370
+ provenance: import_types33.Provenance.EXTRACTED,
9371
+ confidence: (0, import_types33.confidenceForExtracted)("structural"),
9063
9372
  evidence: {
9064
9373
  file: evidenceFile,
9065
9374
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -9074,15 +9383,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
9074
9383
  graph.addNode(portNode.id, portNode);
9075
9384
  nodesAdded++;
9076
9385
  }
9077
- const portEdgeId = (0, import_types2.extractedEdgeId)(fileNodeId, portNode.id, import_types31.EdgeType.CONNECTS_TO);
9386
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types33.EdgeType.CONNECTS_TO);
9078
9387
  if (graph.hasEdge(portEdgeId)) continue;
9079
9388
  const portEdge = {
9080
9389
  id: portEdgeId,
9081
9390
  source: fileNodeId,
9082
9391
  target: portNode.id,
9083
- type: import_types31.EdgeType.CONNECTS_TO,
9084
- provenance: import_types31.Provenance.EXTRACTED,
9085
- confidence: (0, import_types31.confidenceForExtracted)("structural"),
9392
+ type: import_types33.EdgeType.CONNECTS_TO,
9393
+ provenance: import_types33.Provenance.EXTRACTED,
9394
+ confidence: (0, import_types33.confidenceForExtracted)("structural"),
9086
9395
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
9087
9396
  };
9088
9397
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -9095,8 +9404,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
9095
9404
  // src/extract/infra/terraform.ts
9096
9405
  init_cjs_shims();
9097
9406
  var import_node_fs20 = require("fs");
9098
- var import_node_path44 = __toESM(require("path"), 1);
9099
- var import_types32 = require("@neat.is/types");
9407
+ var import_node_path45 = __toESM(require("path"), 1);
9408
+ var import_types34 = require("@neat.is/types");
9100
9409
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
9101
9410
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
9102
9411
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -9106,11 +9415,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
9106
9415
  for (const entry of entries) {
9107
9416
  if (entry.isDirectory()) {
9108
9417
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
9109
- const child = import_node_path44.default.join(start, entry.name);
9418
+ const child = import_node_path45.default.join(start, entry.name);
9110
9419
  if (await isPythonVenvDir(child)) continue;
9111
9420
  out.push(...await walkTfFiles(child, depth + 1, max));
9112
9421
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
9113
- out.push(import_node_path44.default.join(start, entry.name));
9422
+ out.push(import_node_path45.default.join(start, entry.name));
9114
9423
  }
9115
9424
  }
9116
9425
  return out;
@@ -9142,7 +9451,7 @@ async function addTerraformResources(graph, scanPath) {
9142
9451
  const files = await walkTfFiles(scanPath);
9143
9452
  for (const file of files) {
9144
9453
  const content = await import_node_fs20.promises.readFile(file, "utf8");
9145
- const evidenceFile = toPosix(import_node_path44.default.relative(scanPath, file));
9454
+ const evidenceFile = toPosix(import_node_path45.default.relative(scanPath, file));
9146
9455
  const resources = [];
9147
9456
  const byKey = /* @__PURE__ */ new Map();
9148
9457
  RESOURCE_RE.lastIndex = 0;
@@ -9177,16 +9486,16 @@ async function addTerraformResources(graph, scanPath) {
9177
9486
  if (!target) continue;
9178
9487
  if (seen.has(target.nodeId)) continue;
9179
9488
  seen.add(target.nodeId);
9180
- const edgeId = (0, import_types2.extractedEdgeId)(resource.nodeId, target.nodeId, import_types32.EdgeType.DEPENDS_ON);
9489
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types34.EdgeType.DEPENDS_ON);
9181
9490
  if (graph.hasEdge(edgeId)) continue;
9182
9491
  const line = lineAt2(content, resource.bodyOffset + ref.index);
9183
9492
  const edge = {
9184
9493
  id: edgeId,
9185
9494
  source: resource.nodeId,
9186
9495
  target: target.nodeId,
9187
- type: import_types32.EdgeType.DEPENDS_ON,
9188
- provenance: import_types32.Provenance.EXTRACTED,
9189
- confidence: (0, import_types32.confidenceForExtracted)("structural"),
9496
+ type: import_types34.EdgeType.DEPENDS_ON,
9497
+ provenance: import_types34.Provenance.EXTRACTED,
9498
+ confidence: (0, import_types34.confidenceForExtracted)("structural"),
9190
9499
  evidence: { file: evidenceFile, line, snippet: key }
9191
9500
  };
9192
9501
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -9200,7 +9509,7 @@ async function addTerraformResources(graph, scanPath) {
9200
9509
  // src/extract/infra/k8s.ts
9201
9510
  init_cjs_shims();
9202
9511
  var import_node_fs21 = require("fs");
9203
- var import_node_path45 = __toESM(require("path"), 1);
9512
+ var import_node_path46 = __toESM(require("path"), 1);
9204
9513
  var import_yaml3 = require("yaml");
9205
9514
  var K8S_KIND_TO_INFRA_KIND = {
9206
9515
  Service: "k8s-service",
@@ -9218,11 +9527,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
9218
9527
  for (const entry of entries) {
9219
9528
  if (entry.isDirectory()) {
9220
9529
  if (IGNORED_DIRS.has(entry.name)) continue;
9221
- const child = import_node_path45.default.join(start, entry.name);
9530
+ const child = import_node_path46.default.join(start, entry.name);
9222
9531
  if (await isPythonVenvDir(child)) continue;
9223
9532
  out.push(...await walkYamlFiles2(child, depth + 1, max));
9224
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path45.default.extname(entry.name))) {
9225
- out.push(import_node_path45.default.join(start, entry.name));
9533
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path46.default.extname(entry.name))) {
9534
+ out.push(import_node_path46.default.join(start, entry.name));
9226
9535
  }
9227
9536
  }
9228
9537
  return out;
@@ -9256,13 +9565,13 @@ async function addK8sResources(graph, scanPath) {
9256
9565
  // src/extract/infra/cloudflare.ts
9257
9566
  init_cjs_shims();
9258
9567
  var import_node_fs22 = require("fs");
9259
- var import_node_path46 = __toESM(require("path"), 1);
9568
+ var import_node_path47 = __toESM(require("path"), 1);
9260
9569
  var import_smol_toml2 = require("smol-toml");
9261
- var import_types33 = require("@neat.is/types");
9570
+ var import_types35 = require("@neat.is/types");
9262
9571
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
9263
9572
  async function readWranglerConfig(dir) {
9264
9573
  for (const filename of WRANGLER_FILENAMES) {
9265
- const abs = import_node_path46.default.join(dir, filename);
9574
+ const abs = import_node_path47.default.join(dir, filename);
9266
9575
  if (!await exists2(abs)) continue;
9267
9576
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
9268
9577
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -9299,15 +9608,15 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
9299
9608
  nodesAdded++;
9300
9609
  }
9301
9610
  if (node.id === anchorId) return { nodesAdded, edgesAdded };
9302
- const edgeId = (0, import_types2.extractedEdgeId)(anchorId, node.id, edgeType);
9611
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, node.id, edgeType);
9303
9612
  if (!graph.hasEdge(edgeId)) {
9304
9613
  const edge = {
9305
9614
  id: edgeId,
9306
9615
  source: anchorId,
9307
9616
  target: node.id,
9308
9617
  type: edgeType,
9309
- provenance: import_types33.Provenance.EXTRACTED,
9310
- confidence: (0, import_types33.confidenceForExtracted)("structural"),
9618
+ provenance: import_types35.Provenance.EXTRACTED,
9619
+ confidence: (0, import_types35.confidenceForExtracted)("structural"),
9311
9620
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
9312
9621
  };
9313
9622
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -9325,11 +9634,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9325
9634
  try {
9326
9635
  read = await readWranglerConfig(service.dir);
9327
9636
  } catch (err) {
9328
- recordExtractionError("infra cloudflare", import_node_path46.default.relative(scanPath, service.dir), err);
9637
+ recordExtractionError("infra cloudflare", import_node_path47.default.relative(scanPath, service.dir), err);
9329
9638
  continue;
9330
9639
  }
9331
9640
  if (!read || !read.config.name) continue;
9332
- const evidenceFile = toPosix(import_node_path46.default.relative(scanPath, import_node_path46.default.join(service.dir, read.relFile)));
9641
+ const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, read.relFile)));
9333
9642
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
9334
9643
  }
9335
9644
  for (const worker of discovered) {
@@ -9341,7 +9650,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9341
9650
  }
9342
9651
  let anchorId = service.node.id;
9343
9652
  if (config.main) {
9344
- const entryRelPath = toPosix(import_node_path46.default.normalize(config.main));
9653
+ const entryRelPath = toPosix(import_node_path47.default.normalize(config.main));
9345
9654
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9346
9655
  graph,
9347
9656
  service.pkg.name,
@@ -9368,15 +9677,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9368
9677
  nodesAdded++;
9369
9678
  }
9370
9679
  if (runtimeNode.id !== anchorId) {
9371
- const runsOnId = (0, import_types2.extractedEdgeId)(anchorId, runtimeNode.id, import_types33.EdgeType.RUNS_ON);
9680
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types35.EdgeType.RUNS_ON);
9372
9681
  if (!graph.hasEdge(runsOnId)) {
9373
9682
  const edge = {
9374
9683
  id: runsOnId,
9375
9684
  source: anchorId,
9376
9685
  target: runtimeNode.id,
9377
- type: import_types33.EdgeType.RUNS_ON,
9378
- provenance: import_types33.Provenance.EXTRACTED,
9379
- confidence: (0, import_types33.confidenceForExtracted)("structural"),
9686
+ type: import_types35.EdgeType.RUNS_ON,
9687
+ provenance: import_types35.Provenance.EXTRACTED,
9688
+ confidence: (0, import_types35.confidenceForExtracted)("structural"),
9380
9689
  evidence: {
9381
9690
  file: evidenceFile,
9382
9691
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -9390,7 +9699,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9390
9699
  const result = addResourceEdge(
9391
9700
  graph,
9392
9701
  anchorId,
9393
- import_types33.EdgeType.CONNECTS_TO,
9702
+ import_types35.EdgeType.CONNECTS_TO,
9394
9703
  "cloudflare-route",
9395
9704
  route,
9396
9705
  evidenceFile,
@@ -9414,7 +9723,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9414
9723
  const result = addResourceEdge(
9415
9724
  graph,
9416
9725
  anchorId,
9417
- import_types33.EdgeType.DEPENDS_ON,
9726
+ import_types35.EdgeType.DEPENDS_ON,
9418
9727
  group.kind,
9419
9728
  name,
9420
9729
  evidenceFile,
@@ -9428,7 +9737,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9428
9737
  const result = addResourceEdge(
9429
9738
  graph,
9430
9739
  anchorId,
9431
- import_types33.EdgeType.DEPENDS_ON,
9740
+ import_types35.EdgeType.DEPENDS_ON,
9432
9741
  "cloudflare-cron",
9433
9742
  cron,
9434
9743
  evidenceFile,
@@ -9441,7 +9750,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9441
9750
  const result = addResourceEdge(
9442
9751
  graph,
9443
9752
  anchorId,
9444
- import_types33.EdgeType.DEPENDS_ON,
9753
+ import_types35.EdgeType.DEPENDS_ON,
9445
9754
  "cloudflare-env-var",
9446
9755
  varName,
9447
9756
  evidenceFile,
@@ -9454,15 +9763,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9454
9763
  if (!svc.service) continue;
9455
9764
  const target = workerIndex.get(svc.service);
9456
9765
  if (target && target.anchorId !== anchorId) {
9457
- const edgeId = (0, import_types2.extractedEdgeId)(anchorId, target.anchorId, import_types33.EdgeType.CALLS);
9766
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types35.EdgeType.CALLS);
9458
9767
  if (!graph.hasEdge(edgeId)) {
9459
9768
  const edge = {
9460
9769
  id: edgeId,
9461
9770
  source: anchorId,
9462
9771
  target: target.anchorId,
9463
- type: import_types33.EdgeType.CALLS,
9464
- provenance: import_types33.Provenance.EXTRACTED,
9465
- confidence: (0, import_types33.confidenceForExtracted)("structural"),
9772
+ type: import_types35.EdgeType.CALLS,
9773
+ provenance: import_types35.Provenance.EXTRACTED,
9774
+ confidence: (0, import_types35.confidenceForExtracted)("structural"),
9466
9775
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
9467
9776
  };
9468
9777
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -9473,7 +9782,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9473
9782
  const result = addResourceEdge(
9474
9783
  graph,
9475
9784
  anchorId,
9476
- import_types33.EdgeType.DEPENDS_ON,
9785
+ import_types35.EdgeType.DEPENDS_ON,
9477
9786
  "cloudflare-service-binding",
9478
9787
  svc.service,
9479
9788
  evidenceFile,
@@ -9489,12 +9798,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
9489
9798
  // src/extract/infra/vercel.ts
9490
9799
  init_cjs_shims();
9491
9800
  var import_node_fs23 = require("fs");
9492
- var import_node_path47 = __toESM(require("path"), 1);
9493
- var import_types34 = require("@neat.is/types");
9801
+ var import_node_path48 = __toESM(require("path"), 1);
9802
+ var import_types36 = require("@neat.is/types");
9494
9803
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
9495
9804
  async function readVercelConfig(dir) {
9496
9805
  for (const filename of VERCEL_CONFIG_FILENAMES) {
9497
- const abs = import_node_path47.default.join(dir, filename);
9806
+ const abs = import_node_path48.default.join(dir, filename);
9498
9807
  if (!await exists2(abs)) continue;
9499
9808
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
9500
9809
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -9503,7 +9812,7 @@ async function readVercelConfig(dir) {
9503
9812
  return null;
9504
9813
  }
9505
9814
  async function readLinkedProjectName(dir) {
9506
- const abs = import_node_path47.default.join(dir, ".vercel", "project.json");
9815
+ const abs = import_node_path48.default.join(dir, ".vercel", "project.json");
9507
9816
  if (!await exists2(abs)) return void 0;
9508
9817
  const parsed = JSON.parse(await import_node_fs23.promises.readFile(abs, "utf8"));
9509
9818
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -9521,7 +9830,7 @@ async function addVercelServices(graph, services, scanPath) {
9521
9830
  read = await readVercelConfig(service.dir);
9522
9831
  projectName = await readLinkedProjectName(service.dir);
9523
9832
  } catch (err) {
9524
- recordExtractionError("infra vercel", import_node_path47.default.relative(scanPath, service.dir), err);
9833
+ recordExtractionError("infra vercel", import_node_path48.default.relative(scanPath, service.dir), err);
9525
9834
  continue;
9526
9835
  }
9527
9836
  if (!read && !projectName) continue;
@@ -9537,7 +9846,7 @@ async function addVercelServices(graph, services, scanPath) {
9537
9846
  const anchorId = service.node.id;
9538
9847
  if (!read) continue;
9539
9848
  const { config, relFile, raw } = read;
9540
- const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, relFile)));
9849
+ const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
9541
9850
  const add = (edgeType, kind, name) => {
9542
9851
  if (!name) return;
9543
9852
  const result = emitPlatformResourceEdge(
@@ -9553,12 +9862,12 @@ async function addVercelServices(graph, services, scanPath) {
9553
9862
  nodesAdded += result.nodesAdded;
9554
9863
  edgesAdded += result.edgesAdded;
9555
9864
  };
9556
- add(import_types34.EdgeType.RUNS_ON, "vercel", "vercel");
9557
- for (const cron of config.crons ?? []) add(import_types34.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
9558
- for (const varName of Object.keys(config.env ?? {})) add(import_types34.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9559
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types34.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9865
+ add(import_types36.EdgeType.RUNS_ON, "vercel", "vercel");
9866
+ for (const cron of config.crons ?? []) add(import_types36.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
9867
+ for (const varName of Object.keys(config.env ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9868
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9560
9869
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
9561
- add(import_types34.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
9870
+ add(import_types36.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
9562
9871
  }
9563
9872
  }
9564
9873
  return { nodesAdded, edgesAdded };
@@ -9567,13 +9876,13 @@ async function addVercelServices(graph, services, scanPath) {
9567
9876
  // src/extract/infra/railway.ts
9568
9877
  init_cjs_shims();
9569
9878
  var import_node_fs24 = require("fs");
9570
- var import_node_path48 = __toESM(require("path"), 1);
9879
+ var import_node_path49 = __toESM(require("path"), 1);
9571
9880
  var import_smol_toml3 = require("smol-toml");
9572
- var import_types35 = require("@neat.is/types");
9881
+ var import_types37 = require("@neat.is/types");
9573
9882
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
9574
9883
  async function readRailwayConfig(dir) {
9575
9884
  for (const filename of RAILWAY_FILENAMES) {
9576
- const abs = import_node_path48.default.join(dir, filename);
9885
+ const abs = import_node_path49.default.join(dir, filename);
9577
9886
  if (!await exists2(abs)) continue;
9578
9887
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
9579
9888
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -9589,7 +9898,7 @@ async function addRailwayServices(graph, services, scanPath) {
9589
9898
  try {
9590
9899
  read = await readRailwayConfig(service.dir);
9591
9900
  } catch (err) {
9592
- recordExtractionError("infra railway", import_node_path48.default.relative(scanPath, service.dir), err);
9901
+ recordExtractionError("infra railway", import_node_path49.default.relative(scanPath, service.dir), err);
9593
9902
  continue;
9594
9903
  }
9595
9904
  if (!read) continue;
@@ -9599,7 +9908,7 @@ async function addRailwayServices(graph, services, scanPath) {
9599
9908
  }
9600
9909
  const anchorId = service.node.id;
9601
9910
  const { config, relFile, raw } = read;
9602
- const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
9911
+ const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, import_node_path49.default.join(service.dir, relFile)));
9603
9912
  const add = (edgeType, kind, name) => {
9604
9913
  if (!name) return;
9605
9914
  const result = emitPlatformResourceEdge(
@@ -9615,9 +9924,9 @@ async function addRailwayServices(graph, services, scanPath) {
9615
9924
  nodesAdded += result.nodesAdded;
9616
9925
  edgesAdded += result.edgesAdded;
9617
9926
  };
9618
- add(import_types35.EdgeType.RUNS_ON, "railway", "railway");
9619
- add(import_types35.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
9620
- add(import_types35.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
9927
+ add(import_types37.EdgeType.RUNS_ON, "railway", "railway");
9928
+ add(import_types37.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
9929
+ add(import_types37.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
9621
9930
  }
9622
9931
  return { nodesAdded, edgesAdded };
9623
9932
  }
@@ -9625,12 +9934,12 @@ async function addRailwayServices(graph, services, scanPath) {
9625
9934
  // src/extract/infra/supabase.ts
9626
9935
  init_cjs_shims();
9627
9936
  var import_node_fs25 = require("fs");
9628
- var import_node_path49 = __toESM(require("path"), 1);
9937
+ var import_node_path50 = __toESM(require("path"), 1);
9629
9938
  var import_smol_toml4 = require("smol-toml");
9630
- var import_types36 = require("@neat.is/types");
9939
+ var import_types38 = require("@neat.is/types");
9631
9940
  async function readSupabaseConfig(dir) {
9632
- const relFile = import_node_path49.default.join("supabase", "config.toml");
9633
- const abs = import_node_path49.default.join(dir, relFile);
9941
+ const relFile = import_node_path50.default.join("supabase", "config.toml");
9942
+ const abs = import_node_path50.default.join(dir, relFile);
9634
9943
  if (!await exists2(abs)) return null;
9635
9944
  const raw = await import_node_fs25.promises.readFile(abs, "utf8");
9636
9945
  const config = (0, import_smol_toml4.parse)(raw);
@@ -9644,7 +9953,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
9644
9953
  try {
9645
9954
  read = await readSupabaseConfig(service.dir);
9646
9955
  } catch (err) {
9647
- recordExtractionError("infra supabase", import_node_path49.default.relative(scanPath, service.dir), err);
9956
+ recordExtractionError("infra supabase", import_node_path50.default.relative(scanPath, service.dir), err);
9648
9957
  continue;
9649
9958
  }
9650
9959
  if (!read) continue;
@@ -9659,7 +9968,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
9659
9968
  });
9660
9969
  }
9661
9970
  const anchorId = service.node.id;
9662
- const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, import_node_path49.default.join(service.dir, relFile)));
9971
+ const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, import_node_path50.default.join(service.dir, relFile)));
9663
9972
  const add = (edgeType, kind, name) => {
9664
9973
  if (!name) return;
9665
9974
  const result = emitPlatformResourceEdge(
@@ -9675,10 +9984,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
9675
9984
  nodesAdded += result.nodesAdded;
9676
9985
  edgesAdded += result.edgesAdded;
9677
9986
  };
9678
- add(import_types36.EdgeType.RUNS_ON, "supabase", "supabase");
9679
- for (const fn of Object.keys(config.functions ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "supabase-function", fn);
9680
- if (config.storage) add(import_types36.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
9681
- if (config.auth) add(import_types36.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
9987
+ add(import_types38.EdgeType.RUNS_ON, "supabase", "supabase");
9988
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types38.EdgeType.DEPENDS_ON, "supabase-function", fn);
9989
+ if (config.storage) add(import_types38.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
9990
+ if (config.auth) add(import_types38.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
9682
9991
  }
9683
9992
  return { nodesAdded, edgesAdded };
9684
9993
  }
@@ -9700,17 +10009,17 @@ async function addInfra(graph, scanPath, services) {
9700
10009
  }
9701
10010
 
9702
10011
  // src/extract/index.ts
9703
- var import_node_path51 = __toESM(require("path"), 1);
10012
+ var import_node_path52 = __toESM(require("path"), 1);
9704
10013
 
9705
10014
  // src/extract/retire.ts
9706
10015
  init_cjs_shims();
9707
10016
  var import_node_fs26 = require("fs");
9708
- var import_node_path50 = __toESM(require("path"), 1);
9709
- var import_types37 = require("@neat.is/types");
10017
+ var import_node_path51 = __toESM(require("path"), 1);
10018
+ var import_types39 = require("@neat.is/types");
9710
10019
  function dropOrphanedFileNodes(graph) {
9711
10020
  const orphans = [];
9712
10021
  graph.forEachNode((id, attrs) => {
9713
- if (attrs.type !== import_types37.NodeType.FileNode) return;
10022
+ if (attrs.type !== import_types39.NodeType.FileNode) return;
9714
10023
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
9715
10024
  orphans.push(id);
9716
10025
  }
@@ -9723,14 +10032,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
9723
10032
  const bases = [scanPath, ...serviceDirs];
9724
10033
  graph.forEachEdge((id, attrs) => {
9725
10034
  const edge = attrs;
9726
- if (edge.provenance !== import_types37.Provenance.EXTRACTED) return;
10035
+ if (edge.provenance !== import_types39.Provenance.EXTRACTED) return;
9727
10036
  const evidenceFile = edge.evidence?.file;
9728
10037
  if (!evidenceFile) return;
9729
- if (import_node_path50.default.isAbsolute(evidenceFile)) {
10038
+ if (import_node_path51.default.isAbsolute(evidenceFile)) {
9730
10039
  if (!(0, import_node_fs26.existsSync)(evidenceFile)) toDrop.push(id);
9731
10040
  return;
9732
10041
  }
9733
- const found = bases.some((base) => (0, import_node_fs26.existsSync)(import_node_path50.default.join(base, evidenceFile)));
10042
+ const found = bases.some((base) => (0, import_node_fs26.existsSync)(import_node_path51.default.join(base, evidenceFile)));
9734
10043
  if (!found) toDrop.push(id);
9735
10044
  });
9736
10045
  for (const id of toDrop) graph.dropEdge(id);
@@ -9783,7 +10092,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
9783
10092
  }
9784
10093
  const droppedEntries = drainDroppedExtracted();
9785
10094
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
9786
- const rejectedPath = import_node_path51.default.join(import_node_path51.default.dirname(opts.errorsPath), "rejected.ndjson");
10095
+ const rejectedPath = import_node_path52.default.join(import_node_path52.default.dirname(opts.errorsPath), "rejected.ndjson");
9787
10096
  try {
9788
10097
  await writeRejectedExtracted(droppedEntries, rejectedPath);
9789
10098
  } catch (err) {
@@ -9895,9 +10204,9 @@ function canonicalJson(value) {
9895
10204
  // src/persist.ts
9896
10205
  init_cjs_shims();
9897
10206
  var import_node_fs28 = require("fs");
9898
- var import_node_path52 = __toESM(require("path"), 1);
9899
- var import_types38 = require("@neat.is/types");
9900
- var SCHEMA_VERSION = 5;
10207
+ var import_node_path53 = __toESM(require("path"), 1);
10208
+ var import_types40 = require("@neat.is/types");
10209
+ var SCHEMA_VERSION = 6;
9901
10210
  function migrateV1ToV2(payload) {
9902
10211
  const nodes = payload.graph.nodes;
9903
10212
  if (Array.isArray(nodes)) {
@@ -9915,18 +10224,30 @@ function migrateV3ToV4(payload) {
9915
10224
  function migrateV4ToV5(payload) {
9916
10225
  return { ...payload, schemaVersion: 5 };
9917
10226
  }
10227
+ function migrateV5ToV6(payload) {
10228
+ const nodes = payload.graph.nodes;
10229
+ if (Array.isArray(nodes)) {
10230
+ for (const node of nodes) {
10231
+ const attrs = node.attributes;
10232
+ if (!attrs || attrs.type !== import_types40.NodeType.InfraNode) continue;
10233
+ if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
10234
+ if (!Array.isArray(attrs.columns)) attrs.columns = [];
10235
+ }
10236
+ }
10237
+ return { ...payload, schemaVersion: 6 };
10238
+ }
9918
10239
  function migrateV2ToV3(payload) {
9919
10240
  const edges = payload.graph.edges;
9920
10241
  if (Array.isArray(edges)) {
9921
10242
  for (const edge of edges) {
9922
10243
  const attrs = edge.attributes;
9923
10244
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
9924
- attrs.provenance = import_types38.Provenance.OBSERVED;
10245
+ attrs.provenance = import_types40.Provenance.OBSERVED;
9925
10246
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
9926
10247
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
9927
10248
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
9928
10249
  if (type && source && target) {
9929
- const newId = (0, import_types38.observedEdgeId)(source, target, type);
10250
+ const newId = (0, import_types40.observedEdgeId)(source, target, type);
9930
10251
  attrs.id = newId;
9931
10252
  if (edge.key) edge.key = newId;
9932
10253
  }
@@ -9935,7 +10256,7 @@ function migrateV2ToV3(payload) {
9935
10256
  return { ...payload, schemaVersion: 3 };
9936
10257
  }
9937
10258
  async function ensureDir(filePath) {
9938
- await import_node_fs28.promises.mkdir(import_node_path52.default.dirname(filePath), { recursive: true });
10259
+ await import_node_fs28.promises.mkdir(import_node_path53.default.dirname(filePath), { recursive: true });
9939
10260
  }
9940
10261
  async function saveGraphToDisk(graph, outPath) {
9941
10262
  await ensureDir(outPath);
@@ -9969,6 +10290,9 @@ async function loadGraphFromDisk(graph, outPath) {
9969
10290
  if (payload.schemaVersion === 4) {
9970
10291
  payload = migrateV4ToV5(payload);
9971
10292
  }
10293
+ if (payload.schemaVersion === 5) {
10294
+ payload = migrateV5ToV6(payload);
10295
+ }
9972
10296
  if (payload.schemaVersion !== SCHEMA_VERSION) {
9973
10297
  throw new Error(
9974
10298
  `persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
@@ -10019,23 +10343,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
10019
10343
 
10020
10344
  // src/projects.ts
10021
10345
  init_cjs_shims();
10022
- var import_node_path53 = __toESM(require("path"), 1);
10346
+ var import_node_path54 = __toESM(require("path"), 1);
10023
10347
  function pathsForProject(project, baseDir) {
10024
10348
  if (project === DEFAULT_PROJECT) {
10025
10349
  return {
10026
- snapshotPath: import_node_path53.default.join(baseDir, "graph.json"),
10027
- errorsPath: import_node_path53.default.join(baseDir, "errors.ndjson"),
10028
- staleEventsPath: import_node_path53.default.join(baseDir, "stale-events.ndjson"),
10029
- embeddingsCachePath: import_node_path53.default.join(baseDir, "embeddings.json"),
10030
- policyViolationsPath: import_node_path53.default.join(baseDir, "policy-violations.ndjson")
10350
+ snapshotPath: import_node_path54.default.join(baseDir, "graph.json"),
10351
+ errorsPath: import_node_path54.default.join(baseDir, "errors.ndjson"),
10352
+ staleEventsPath: import_node_path54.default.join(baseDir, "stale-events.ndjson"),
10353
+ embeddingsCachePath: import_node_path54.default.join(baseDir, "embeddings.json"),
10354
+ policyViolationsPath: import_node_path54.default.join(baseDir, "policy-violations.ndjson")
10031
10355
  };
10032
10356
  }
10033
10357
  return {
10034
- snapshotPath: import_node_path53.default.join(baseDir, `${project}.json`),
10035
- errorsPath: import_node_path53.default.join(baseDir, `errors.${project}.ndjson`),
10036
- staleEventsPath: import_node_path53.default.join(baseDir, `stale-events.${project}.ndjson`),
10037
- embeddingsCachePath: import_node_path53.default.join(baseDir, `embeddings.${project}.json`),
10038
- policyViolationsPath: import_node_path53.default.join(baseDir, `policy-violations.${project}.ndjson`)
10358
+ snapshotPath: import_node_path54.default.join(baseDir, `${project}.json`),
10359
+ errorsPath: import_node_path54.default.join(baseDir, `errors.${project}.ndjson`),
10360
+ staleEventsPath: import_node_path54.default.join(baseDir, `stale-events.${project}.ndjson`),
10361
+ embeddingsCachePath: import_node_path54.default.join(baseDir, `embeddings.${project}.json`),
10362
+ policyViolationsPath: import_node_path54.default.join(baseDir, `policy-violations.${project}.ndjson`)
10039
10363
  };
10040
10364
  }
10041
10365
  var Projects = class {
@@ -10077,18 +10401,18 @@ function parseExtraProjects(raw) {
10077
10401
  init_cjs_shims();
10078
10402
  var import_node_fs29 = require("fs");
10079
10403
  var import_node_os3 = __toESM(require("os"), 1);
10080
- var import_node_path54 = __toESM(require("path"), 1);
10081
- var import_types39 = require("@neat.is/types");
10404
+ var import_node_path55 = __toESM(require("path"), 1);
10405
+ var import_types41 = require("@neat.is/types");
10082
10406
  function neatHome() {
10083
10407
  const override = process.env.NEAT_HOME;
10084
- if (override && override.length > 0) return import_node_path54.default.resolve(override);
10085
- return import_node_path54.default.join(import_node_os3.default.homedir(), ".neat");
10408
+ if (override && override.length > 0) return import_node_path55.default.resolve(override);
10409
+ return import_node_path55.default.join(import_node_os3.default.homedir(), ".neat");
10086
10410
  }
10087
10411
  function registryPath() {
10088
- return import_node_path54.default.join(neatHome(), "projects.json");
10412
+ return import_node_path55.default.join(neatHome(), "projects.json");
10089
10413
  }
10090
10414
  function daemonsDir() {
10091
- return import_node_path54.default.join(neatHome(), "daemons");
10415
+ return import_node_path55.default.join(neatHome(), "daemons");
10092
10416
  }
10093
10417
  function isFiniteInt(v) {
10094
10418
  return typeof v === "number" && Number.isFinite(v);
@@ -10129,7 +10453,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
10129
10453
  const out = [];
10130
10454
  for (const name of names) {
10131
10455
  if (!name.endsWith(".json")) continue;
10132
- const file = import_node_path54.default.join(dir, name);
10456
+ const file = import_node_path55.default.join(dir, name);
10133
10457
  let raw;
10134
10458
  try {
10135
10459
  raw = await import_node_fs29.promises.readFile(file, "utf8");
@@ -10168,7 +10492,7 @@ async function readRegistry() {
10168
10492
  throw err;
10169
10493
  }
10170
10494
  const parsed = JSON.parse(raw);
10171
- return import_types39.RegistryFileSchema.parse(parsed);
10495
+ return import_types41.RegistryFileSchema.parse(parsed);
10172
10496
  }
10173
10497
  async function getProject(name) {
10174
10498
  const reg = await readRegistry();
@@ -10243,7 +10567,7 @@ init_auth();
10243
10567
  // src/connectors-config.ts
10244
10568
  init_cjs_shims();
10245
10569
  var import_node_os4 = __toESM(require("os"), 1);
10246
- var import_node_path55 = __toESM(require("path"), 1);
10570
+ var import_node_path56 = __toESM(require("path"), 1);
10247
10571
  var import_node_fs30 = require("fs");
10248
10572
  var CONNECTORS_CONFIG_VERSION = 1;
10249
10573
  var EnvRefUnsetError = class extends Error {
@@ -10258,11 +10582,11 @@ var EnvRefUnsetError = class extends Error {
10258
10582
  };
10259
10583
  function neatHome2() {
10260
10584
  const override = process.env.NEAT_HOME;
10261
- if (override && override.length > 0) return import_node_path55.default.resolve(override);
10262
- return import_node_path55.default.join(import_node_os4.default.homedir(), ".neat");
10585
+ if (override && override.length > 0) return import_node_path56.default.resolve(override);
10586
+ return import_node_path56.default.join(import_node_os4.default.homedir(), ".neat");
10263
10587
  }
10264
10588
  function connectorsConfigPath(home = neatHome2()) {
10265
- return import_node_path55.default.join(home, "connectors.json");
10589
+ return import_node_path56.default.join(home, "connectors.json");
10266
10590
  }
10267
10591
  var MODE_MASK_LOOSER_THAN_0600 = 63;
10268
10592
  async function warnIfModeLooserThan0600(file) {
@@ -10449,15 +10773,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
10449
10773
 
10450
10774
  // src/connectors/index.ts
10451
10775
  init_cjs_shims();
10452
- var import_types40 = require("@neat.is/types");
10776
+ var import_types42 = require("@neat.is/types");
10453
10777
  var NO_ENV = "unknown";
10454
10778
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
10455
10779
  if (!graph.hasNode(targetNodeId)) return void 0;
10456
10780
  const sites = [];
10457
10781
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
10458
10782
  const edge = graph.getEdgeAttributes(edgeId);
10459
- if (edge.provenance !== import_types40.Provenance.EXTRACTED) continue;
10460
- const parsed = (0, import_types40.parseFileId)(edge.source);
10783
+ if (edge.provenance !== import_types42.Provenance.EXTRACTED) continue;
10784
+ const parsed = (0, import_types42.parseFileId)(edge.source);
10461
10785
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
10462
10786
  const site = { relPath: edge.evidence.file };
10463
10787
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -10468,7 +10792,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
10468
10792
  function routeCallSiteFor(graph, targetNodeId) {
10469
10793
  if (!graph.hasNode(targetNodeId)) return void 0;
10470
10794
  const attrs = graph.getNodeAttributes(targetNodeId);
10471
- if (attrs.type !== import_types40.NodeType.RouteNode || !attrs.path) return void 0;
10795
+ if (attrs.type !== import_types42.NodeType.RouteNode || !attrs.path) return void 0;
10472
10796
  const site = { relPath: attrs.path };
10473
10797
  if (attrs.line !== void 0) site.line = attrs.line;
10474
10798
  return site;
@@ -10488,6 +10812,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
10488
10812
  const { kind, name, provider } = resolved.ensureInfraNode;
10489
10813
  ensureInfraNode(graph, kind, name, provider);
10490
10814
  }
10815
+ mergeObservedColumns(graph, resolved.targetNodeId, signal.columns);
10491
10816
  const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
10492
10817
  const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
10493
10818
  const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
@@ -10877,6 +11202,7 @@ async function fetchSupabaseEdgeLogs(config, token, startIso, endIso, fetchImpl
10877
11202
 
10878
11203
  // src/connectors/supabase/map.ts
10879
11204
  init_cjs_shims();
11205
+ init_otel();
10880
11206
 
10881
11207
  // src/connectors/supabase/types.ts
10882
11208
  init_cjs_shims();
@@ -10902,10 +11228,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
10902
11228
  // src/connectors/supabase/map.ts
10903
11229
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
10904
11230
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
10905
- function targetFromRestPath(path58) {
10906
- const rpcMatch = REST_RPC_PATH_RE.exec(path58);
11231
+ function targetFromRestPath(path59) {
11232
+ const rpcMatch = REST_RPC_PATH_RE.exec(path59);
10907
11233
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
10908
- const tableMatch = REST_TABLE_PATH_RE.exec(path58);
11234
+ const tableMatch = REST_TABLE_PATH_RE.exec(path59);
10909
11235
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
10910
11236
  return null;
10911
11237
  }
@@ -10969,12 +11295,14 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
10969
11295
  if (delta <= 0) continue;
10970
11296
  const table = tableNameFromQueryText(row.query);
10971
11297
  if (!table) continue;
11298
+ const columns = columnsFromSqlStatement(row.query);
10972
11299
  signals.push({
10973
11300
  targetKind: SUPABASE_TABLE_TARGET_KIND,
10974
11301
  targetName: table,
10975
11302
  callCount: delta,
10976
11303
  errorCount: 0,
10977
- lastObservedIso: nowIso2
11304
+ lastObservedIso: nowIso2,
11305
+ ...columns.length > 0 ? { columns } : {}
10978
11306
  });
10979
11307
  }
10980
11308
  for (const queryid of [...previous.keys()]) {
@@ -11014,23 +11342,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
11014
11342
 
11015
11343
  // src/connectors/supabase/resolve.ts
11016
11344
  init_cjs_shims();
11017
- var import_types42 = require("@neat.is/types");
11345
+ var import_types44 = require("@neat.is/types");
11018
11346
  function createSupabaseResolveTarget(graph, config) {
11019
11347
  return (signal, _ctx) => {
11020
11348
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
11021
11349
  return null;
11022
11350
  }
11023
- const subResourceId = (0, import_types42.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
11351
+ const subResourceId = (0, import_types44.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
11024
11352
  if (graph.hasNode(subResourceId)) {
11025
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types42.EdgeType.CALLS };
11353
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
11026
11354
  }
11027
- const bareResourceId = (0, import_types42.infraId)(signal.targetKind, signal.targetName);
11355
+ const bareResourceId = (0, import_types44.infraId)(signal.targetKind, signal.targetName);
11028
11356
  if (graph.hasNode(bareResourceId)) {
11029
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types42.EdgeType.CALLS };
11357
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
11030
11358
  }
11031
- const projectLevelId = (0, import_types42.infraId)("supabase", config.nodeRef);
11359
+ const projectLevelId = (0, import_types44.infraId)("supabase", config.nodeRef);
11032
11360
  if (graph.hasNode(projectLevelId)) {
11033
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types42.EdgeType.CALLS };
11361
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
11034
11362
  }
11035
11363
  return null;
11036
11364
  };
@@ -11123,7 +11451,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
11123
11451
 
11124
11452
  // src/connectors/railway/index.ts
11125
11453
  init_cjs_shims();
11126
- var import_types46 = require("@neat.is/types");
11454
+ var import_types48 = require("@neat.is/types");
11127
11455
 
11128
11456
  // src/connectors/railway/client.ts
11129
11457
  init_cjs_shims();
@@ -11274,7 +11602,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
11274
11602
  const out = [];
11275
11603
  graph.forEachNode((_id, attrs) => {
11276
11604
  const node = attrs;
11277
- if (node.type !== import_types46.NodeType.RouteNode) return;
11605
+ if (node.type !== import_types48.NodeType.RouteNode) return;
11278
11606
  const route = attrs;
11279
11607
  if (route.service !== serviceName) return;
11280
11608
  out.push({
@@ -11378,12 +11706,12 @@ function createRailwayResolveTarget(config) {
11378
11706
  const serviceName = config.serviceNameById[config.serviceId];
11379
11707
  if (!serviceName) return null;
11380
11708
  if (signal.targetKind === ROUTE_TARGET_KIND) {
11381
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types46.EdgeType.CALLS };
11709
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types48.EdgeType.CALLS };
11382
11710
  }
11383
11711
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
11384
11712
  const peerName = config.serviceNameById[signal.targetName];
11385
11713
  if (!peerName) return null;
11386
- return { targetNodeId: (0, import_types46.serviceId)(peerName), serviceName, edgeType: import_types46.EdgeType.CONNECTS_TO };
11714
+ return { targetNodeId: (0, import_types48.serviceId)(peerName), serviceName, edgeType: import_types48.EdgeType.CONNECTS_TO };
11387
11715
  }
11388
11716
  return null;
11389
11717
  };
@@ -11507,9 +11835,9 @@ function parseFirebaseTargetName(targetName) {
11507
11835
  const secondSep = rest.indexOf(FIELD_SEP);
11508
11836
  if (secondSep === -1) return null;
11509
11837
  const method = rest.slice(0, secondSep);
11510
- const path58 = rest.slice(secondSep + 1);
11511
- if (!resourceName || !method || !path58) return null;
11512
- return { resourceName, method, path: path58 };
11838
+ const path59 = rest.slice(secondSep + 1);
11839
+ if (!resourceName || !method || !path59) return null;
11840
+ return { resourceName, method, path: path59 };
11513
11841
  }
11514
11842
  function resourceNameFor(type, labels) {
11515
11843
  if (!labels) return null;
@@ -11547,14 +11875,14 @@ function mapLogEntryToSignal(entry) {
11547
11875
  if (!req) return null;
11548
11876
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
11549
11877
  const method = req.requestMethod.toUpperCase();
11550
- const path58 = pathFromRequestUrl(req.requestUrl);
11551
- if (path58 === null) return null;
11878
+ const path59 = pathFromRequestUrl(req.requestUrl);
11879
+ if (path59 === null) return null;
11552
11880
  const timestamp = entry.timestamp;
11553
11881
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
11554
11882
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
11555
11883
  return {
11556
11884
  targetKind: resourceType,
11557
- targetName: packFirebaseTargetName({ resourceName, method, path: path58 }),
11885
+ targetName: packFirebaseTargetName({ resourceName, method, path: path59 }),
11558
11886
  callCount: 1,
11559
11887
  errorCount: isError ? 1 : 0,
11560
11888
  lastObservedIso: timestamp
@@ -11571,7 +11899,7 @@ function mapLogEntriesToSignals(entries) {
11571
11899
 
11572
11900
  // src/connectors/firebase/resolve.ts
11573
11901
  init_cjs_shims();
11574
- var import_types47 = require("@neat.is/types");
11902
+ var import_types49 = require("@neat.is/types");
11575
11903
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
11576
11904
  switch (resourceType) {
11577
11905
  case "cloud_function":
@@ -11586,7 +11914,7 @@ function routeEntriesFor(graph, serviceName) {
11586
11914
  const entries = [];
11587
11915
  graph.forEachNode((_id, attrs) => {
11588
11916
  const node = attrs;
11589
- if (node.type !== import_types47.NodeType.RouteNode) return;
11917
+ if (node.type !== import_types49.NodeType.RouteNode) return;
11590
11918
  const route = attrs;
11591
11919
  if (route.service !== serviceName) return;
11592
11920
  entries.push({
@@ -11618,7 +11946,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
11618
11946
  return {
11619
11947
  targetNodeId: match.routeNodeId,
11620
11948
  serviceName,
11621
- edgeType: import_types47.EdgeType.CALLS
11949
+ edgeType: import_types49.EdgeType.CALLS
11622
11950
  };
11623
11951
  };
11624
11952
  }
@@ -11645,7 +11973,7 @@ init_cjs_shims();
11645
11973
 
11646
11974
  // src/connectors/cloudflare/connector.ts
11647
11975
  init_cjs_shims();
11648
- var import_types49 = require("@neat.is/types");
11976
+ var import_types51 = require("@neat.is/types");
11649
11977
 
11650
11978
  // src/connectors/cloudflare/client.ts
11651
11979
  init_cjs_shims();
@@ -11761,7 +12089,7 @@ function mapEventToSignal(event) {
11761
12089
  if (Number.isNaN(observedAt.getTime())) return null;
11762
12090
  const statusCode = metadata?.statusCode;
11763
12091
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
11764
- const path58 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
12092
+ const path59 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11765
12093
  return {
11766
12094
  targetKind: CLOUDFLARE_TARGET_KIND,
11767
12095
  targetName: scriptName,
@@ -11769,7 +12097,7 @@ function mapEventToSignal(event) {
11769
12097
  errorCount: isError ? 1 : 0,
11770
12098
  lastObservedIso: observedAt.toISOString(),
11771
12099
  method,
11772
- ...path58 ? { path: path58 } : {},
12100
+ ...path59 ? { path: path59 } : {},
11773
12101
  ...typeof statusCode === "number" ? { statusCode } : {},
11774
12102
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
11775
12103
  };
@@ -11809,19 +12137,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
11809
12137
  graph.forEachNode((id, attrs) => {
11810
12138
  if (found) return;
11811
12139
  const a = attrs;
11812
- if (a.type === import_types49.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
12140
+ if (a.type === import_types51.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11813
12141
  found = id;
11814
12142
  }
11815
12143
  });
11816
12144
  return found;
11817
12145
  }
11818
- function findMatchingRouteNode(graph, serviceName, method, path58) {
11819
- const normalizedPath = normalizePathTemplate(path58);
12146
+ function findMatchingRouteNode(graph, serviceName, method, path59) {
12147
+ const normalizedPath = normalizePathTemplate(path59);
11820
12148
  let found = null;
11821
12149
  graph.forEachNode((id, attrs) => {
11822
12150
  if (found) return;
11823
12151
  const a = attrs;
11824
- if (a.type !== import_types49.NodeType.RouteNode || a.service !== serviceName) return;
12152
+ if (a.type !== import_types51.NodeType.RouteNode || a.service !== serviceName) return;
11825
12153
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11826
12154
  const routeMethod = (a.method ?? "").toUpperCase();
11827
12155
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11833,18 +12161,18 @@ function createCloudflareResolveTarget(config, graph) {
11833
12161
  return (signal) => {
11834
12162
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
11835
12163
  const scriptName = signal.targetName;
11836
- const { method, path: path58 } = signal;
12164
+ const { method, path: path59 } = signal;
11837
12165
  const resolveRouteGrain = (serviceName, wholeFileId) => {
11838
- if (!method || !path58) return wholeFileId;
11839
- return findMatchingRouteNode(graph, serviceName, method, path58) ?? wholeFileId;
12166
+ if (!method || !path59) return wholeFileId;
12167
+ return findMatchingRouteNode(graph, serviceName, method, path59) ?? wholeFileId;
11840
12168
  };
11841
12169
  const mapping = config.workers?.[scriptName];
11842
12170
  if (mapping) {
11843
- const wholeFileId = (0, import_types49.fileId)(mapping.service, mapping.entryFile);
12171
+ const wholeFileId = (0, import_types51.fileId)(mapping.service, mapping.entryFile);
11844
12172
  return {
11845
12173
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11846
12174
  serviceName: mapping.service,
11847
- edgeType: import_types49.EdgeType.CALLS
12175
+ edgeType: import_types51.EdgeType.CALLS
11848
12176
  };
11849
12177
  }
11850
12178
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11853,13 +12181,13 @@ function createCloudflareResolveTarget(config, graph) {
11853
12181
  return {
11854
12182
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11855
12183
  serviceName: fileNode.service,
11856
- edgeType: import_types49.EdgeType.CALLS
12184
+ edgeType: import_types51.EdgeType.CALLS
11857
12185
  };
11858
12186
  }
11859
12187
  return {
11860
- targetNodeId: (0, import_types49.infraId)("cloudflare-worker", scriptName),
12188
+ targetNodeId: (0, import_types51.infraId)("cloudflare-worker", scriptName),
11861
12189
  serviceName: scriptName,
11862
- edgeType: import_types49.EdgeType.CALLS,
12190
+ edgeType: import_types51.EdgeType.CALLS,
11863
12191
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11864
12192
  };
11865
12193
  };
@@ -12037,12 +12365,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
12037
12365
  if (!prior || calls <= prior.calls) continue;
12038
12366
  const table = tableFromSqlStatement(row.query);
12039
12367
  if (!table) continue;
12368
+ const columns = columnsFromSqlStatement(row.query);
12040
12369
  signals.push({
12041
12370
  targetKind: NEON_SQL_TABLE_TARGET_KIND,
12042
12371
  targetName: table,
12043
12372
  callCount: calls - prior.calls,
12044
12373
  errorCount: 0,
12045
- lastObservedIso: observedAtIso
12374
+ lastObservedIso: observedAtIso,
12375
+ ...columns.length > 0 ? { columns } : {}
12046
12376
  });
12047
12377
  }
12048
12378
  for (const queryid of previous.keys()) {
@@ -12053,14 +12383,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
12053
12383
 
12054
12384
  // src/connectors/neon/resolve.ts
12055
12385
  init_cjs_shims();
12056
- var import_types53 = require("@neat.is/types");
12386
+ var import_types55 = require("@neat.is/types");
12057
12387
  function createNeonResolveTarget(config) {
12058
12388
  return (signal) => {
12059
12389
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
12060
12390
  return {
12061
- targetNodeId: (0, import_types53.infraId)("sql-table", signal.targetName),
12391
+ targetNodeId: (0, import_types55.infraId)("sql-table", signal.targetName),
12062
12392
  serviceName: config.serviceName,
12063
- edgeType: import_types53.EdgeType.CALLS,
12393
+ edgeType: import_types55.EdgeType.CALLS,
12064
12394
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
12065
12395
  };
12066
12396
  };
@@ -12582,11 +12912,11 @@ function registerRoutes(scope, ctx) {
12582
12912
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
12583
12913
  const parsed = [];
12584
12914
  for (const c of candidates) {
12585
- const r = import_types56.DivergenceTypeSchema.safeParse(c);
12915
+ const r = import_types58.DivergenceTypeSchema.safeParse(c);
12586
12916
  if (!r.success) {
12587
12917
  return reply.code(400).send({
12588
12918
  error: `unknown divergence type "${c}"`,
12589
- allowed: import_types56.DivergenceTypeSchema.options
12919
+ allowed: import_types58.DivergenceTypeSchema.options
12590
12920
  });
12591
12921
  }
12592
12922
  parsed.push(r.data);
@@ -12895,7 +13225,7 @@ function registerRoutes(scope, ctx) {
12895
13225
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
12896
13226
  let violations = await log.readAll();
12897
13227
  if (req.query.severity) {
12898
- const sev = import_types56.PolicySeveritySchema.safeParse(req.query.severity);
13228
+ const sev = import_types58.PolicySeveritySchema.safeParse(req.query.severity);
12899
13229
  if (!sev.success) {
12900
13230
  return reply.code(400).send({
12901
13231
  error: "invalid severity",
@@ -12934,7 +13264,7 @@ function registerRoutes(scope, ctx) {
12934
13264
  scope.post("/policies/check", async (req, reply) => {
12935
13265
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
12936
13266
  if (!proj) return;
12937
- const parsed = import_types56.PoliciesCheckBodySchema.safeParse(req.body ?? {});
13267
+ const parsed = import_types58.PoliciesCheckBodySchema.safeParse(req.body ?? {});
12938
13268
  if (!parsed.success) {
12939
13269
  return reply.code(400).send({
12940
13270
  error: "invalid /policies/check body",
@@ -13256,7 +13586,7 @@ init_otel_grpc();
13256
13586
  // src/search.ts
13257
13587
  init_cjs_shims();
13258
13588
  var import_node_fs31 = require("fs");
13259
- var import_node_path56 = __toESM(require("path"), 1);
13589
+ var import_node_path57 = __toESM(require("path"), 1);
13260
13590
  var import_node_crypto4 = require("crypto");
13261
13591
  var DEFAULT_LIMIT = 10;
13262
13592
  var NOMIC_DIM = 768;
@@ -13419,7 +13749,7 @@ async function readCache(cachePath) {
13419
13749
  }
13420
13750
  }
13421
13751
  async function writeCache(cachePath, cache) {
13422
- await import_node_fs31.promises.mkdir(import_node_path56.default.dirname(cachePath), { recursive: true });
13752
+ await import_node_fs31.promises.mkdir(import_node_path57.default.dirname(cachePath), { recursive: true });
13423
13753
  await import_node_fs31.promises.writeFile(cachePath, JSON.stringify(cache));
13424
13754
  }
13425
13755
  var VectorIndex = class {
@@ -13600,14 +13930,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
13600
13930
  async function main() {
13601
13931
  const baseDirEnv = process.env.NEAT_OUT_DIR;
13602
13932
  const legacyOutPath = process.env.NEAT_OUT_PATH;
13603
- const baseDir = baseDirEnv ? import_node_path57.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path57.default.resolve(import_node_path57.default.dirname(legacyOutPath)) : import_node_path57.default.resolve("./neat-out");
13604
- const defaultScanPath = import_node_path57.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
13933
+ const baseDir = baseDirEnv ? import_node_path58.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path58.default.resolve(import_node_path58.default.dirname(legacyOutPath)) : import_node_path58.default.resolve("./neat-out");
13934
+ const defaultScanPath = import_node_path58.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
13605
13935
  const registry = new Projects();
13606
13936
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
13607
13937
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
13608
13938
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
13609
13939
  const projectScan = process.env[envKey];
13610
- await bootProject(registry, name, projectScan ? import_node_path57.default.resolve(projectScan) : void 0, baseDir);
13940
+ await bootProject(registry, name, projectScan ? import_node_path58.default.resolve(projectScan) : void 0, baseDir);
13611
13941
  }
13612
13942
  const host = process.env.HOST ?? "0.0.0.0";
13613
13943
  const port = Number(process.env.PORT ?? 8080);