@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/cli.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req, reply, done) => {
64
- const path70 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path70) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path70)) {
64
+ const path71 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path71) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path71)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path38.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
- return import_node_path38.default.resolve(here, "..", "proto");
197
+ const here = import_node_path39.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
+ return import_node_path39.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url2, import_node_path38, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url2, import_node_path39, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url2 = require("url");
272
- import_node_path38 = __toESM(require("path"), 1);
272
+ import_node_path39 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -355,6 +355,46 @@ function tableFromSqlStatement(sql) {
355
355
  const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
356
356
  return m ? m[1] : null;
357
357
  }
358
+ function columnsFromSqlStatement(sql) {
359
+ if (typeof sql !== "string" || sql.length === 0) return [];
360
+ const s = sql.replace(/\s+/g, " ").trim();
361
+ if (/\bjoin\b/i.test(s)) return [];
362
+ if ((s.match(/\bfrom\b/gi) ?? []).length > 1) return [];
363
+ const bare = (raw) => {
364
+ let t = raw.trim().replace(/"/g, "");
365
+ if (/[()*]/.test(t)) return null;
366
+ t = t.split(/\s+as\s+/i)[0].trim();
367
+ t = t.split(".").pop();
368
+ return /^[a-z_][\w$]*$/i.test(t) ? t.toLowerCase() : null;
369
+ };
370
+ const isCol = (c) => c !== null;
371
+ const cols = (list) => [...new Set(list.split(",").map(bare).filter(isCol))];
372
+ const whereCols = (w) => w ? [
373
+ ...new Set(
374
+ [
375
+ ...w.matchAll(
376
+ /(?:"?[\w$]+"?\.)?"?([a-z_][\w$]*)"?\s*(?:=|<|>|<=|>=|<>|!=|\bis\b|\bin\b|\blike\b)/gi
377
+ )
378
+ ].map((match) => match[1].toLowerCase()).filter((c) => !/^(and|or|not|null)$/i.test(c))
379
+ )
380
+ ] : [];
381
+ let m;
382
+ if (m = /\binsert\s+into\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s*\(([^)]*)\)/i.exec(s)) {
383
+ return cols(m[1]);
384
+ }
385
+ if (m = /\bupdate\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s+set\s+(.+?)(?:\bwhere\b(.+))?$/i.exec(s)) {
386
+ const set = m[1].split(",").map((p) => bare(p.split("=")[0])).filter(isCol);
387
+ return [.../* @__PURE__ */ new Set([...set, ...whereCols(m[2] ?? void 0)])];
388
+ }
389
+ if (m = /\bdelete\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
390
+ return whereCols(m[1] ?? void 0);
391
+ }
392
+ if (m = /\bselect\s+(.+?)\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
393
+ if (m[1].trim() === "*") return [];
394
+ return [.../* @__PURE__ */ new Set([...cols(m[1]), ...whereCols(m[2] ?? void 0)])];
395
+ }
396
+ return [];
397
+ }
358
398
  function messagingDestinationOf(attrs) {
359
399
  for (const key of ["messaging.destination.name", "messaging.destination"]) {
360
400
  const v = attrs[key];
@@ -375,8 +415,8 @@ function websocketChannelPathOf(attrs) {
375
415
  const v = attrs[key];
376
416
  if (typeof v === "string" && v.length > 0) {
377
417
  const q = v.indexOf("?");
378
- const path70 = q === -1 ? v : v.slice(0, q);
379
- if (path70.length > 0) return path70;
418
+ const path71 = q === -1 ? v : v.slice(0, q);
419
+ if (path71.length > 0) return path71;
380
420
  }
381
421
  }
382
422
  return void 0;
@@ -413,6 +453,7 @@ function parseOtlpRequest(body) {
413
453
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
414
454
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
415
455
  dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
456
+ dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
416
457
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
417
458
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
418
459
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -434,10 +475,10 @@ function parseOtlpRequest(body) {
434
475
  return out;
435
476
  }
436
477
  function loadProtoRoot() {
437
- const here = import_node_path39.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
438
- const protoRoot = import_node_path39.default.resolve(here, "..", "proto");
478
+ const here = import_node_path40.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
479
+ const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
439
480
  const root = new import_protobufjs.default.Root();
440
- root.resolvePath = (_origin, target) => import_node_path39.default.resolve(protoRoot, target);
481
+ root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
441
482
  root.loadSync(
442
483
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
443
484
  { keepCase: true }
@@ -668,12 +709,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
668
709
  }
669
710
  }
670
711
  }
671
- var import_node_path39, import_node_url3, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
712
+ var import_node_path40, import_node_url3, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
672
713
  var init_otel = __esm({
673
714
  "src/otel.ts"() {
674
715
  "use strict";
675
716
  init_cjs_shims();
676
- import_node_path39 = __toESM(require("path"), 1);
717
+ import_node_path40 = __toESM(require("path"), 1);
677
718
  import_node_url3 = require("url");
678
719
  import_fastify = __toESM(require("fastify"), 1);
679
720
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -710,7 +751,7 @@ __export(cli_exports, {
710
751
  });
711
752
  module.exports = __toCommonJS(cli_exports);
712
753
  init_cjs_shims();
713
- var import_node_path69 = __toESM(require("path"), 1);
754
+ var import_node_path70 = __toESM(require("path"), 1);
714
755
  var import_node_os6 = __toESM(require("os"), 1);
715
756
  var import_node_fs43 = require("fs");
716
757
 
@@ -1280,19 +1321,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1280
1321
  function longestIncomingWalk(graph, start, maxDepth) {
1281
1322
  let best = { path: [start], edges: [] };
1282
1323
  const visited = /* @__PURE__ */ new Set([start]);
1283
- function step(node, path70, edges) {
1284
- if (path70.length > best.path.length) {
1285
- best = { path: [...path70], edges: [...edges] };
1324
+ function step(node, path71, edges) {
1325
+ if (path71.length > best.path.length) {
1326
+ best = { path: [...path71], edges: [...edges] };
1286
1327
  }
1287
- if (path70.length - 1 >= maxDepth) return;
1328
+ if (path71.length - 1 >= maxDepth) return;
1288
1329
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1289
1330
  for (const [srcId, edge] of incoming) {
1290
1331
  if (visited.has(srcId)) continue;
1291
1332
  visited.add(srcId);
1292
- path70.push(srcId);
1333
+ path71.push(srcId);
1293
1334
  edges.push(edge);
1294
- step(srcId, path70, edges);
1295
- path70.pop();
1335
+ step(srcId, path71, edges);
1336
+ path71.pop();
1296
1337
  edges.pop();
1297
1338
  visited.delete(srcId);
1298
1339
  }
@@ -1499,26 +1540,26 @@ function dominantFailingCall(graph, serviceId7, visited) {
1499
1540
  return best;
1500
1541
  }
1501
1542
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1502
- const path70 = [originServiceId];
1543
+ const path71 = [originServiceId];
1503
1544
  const edges = [];
1504
1545
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1505
1546
  let current = originServiceId;
1506
1547
  for (let depth = 0; depth < maxDepth; depth++) {
1507
1548
  const hop = dominantFailingCall(graph, current, visited);
1508
1549
  if (!hop) break;
1509
- path70.push(hop.nextService);
1550
+ path71.push(hop.nextService);
1510
1551
  edges.push(hop.edge);
1511
1552
  visited.add(hop.nextService);
1512
1553
  current = hop.nextService;
1513
1554
  }
1514
1555
  if (edges.length === 0) return null;
1515
- return { path: path70, edges, culprit: current };
1556
+ return { path: path71, edges, culprit: current };
1516
1557
  }
1517
1558
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1518
1559
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1519
1560
  if (!chain) return null;
1520
1561
  const culprit = chain.culprit;
1521
- const path70 = [...chain.path];
1562
+ const path71 = [...chain.path];
1522
1563
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1523
1564
  const baseConfidence = confidenceFromMix(chain.edges);
1524
1565
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1526,14 +1567,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1526
1567
  if (loc) {
1527
1568
  let rootCauseNode = culprit;
1528
1569
  if (loc.fileNode) {
1529
- path70.push(loc.fileNode);
1570
+ path71.push(loc.fileNode);
1530
1571
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1531
1572
  rootCauseNode = loc.fileNode;
1532
1573
  }
1533
1574
  return import_types.RootCauseResultSchema.parse({
1534
1575
  rootCauseNode,
1535
1576
  rootCauseReason: loc.rootCauseReason,
1536
- traversalPath: path70,
1577
+ traversalPath: path71,
1537
1578
  edgeProvenances,
1538
1579
  confidence,
1539
1580
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1545,7 +1586,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1545
1586
  return import_types.RootCauseResultSchema.parse({
1546
1587
  rootCauseNode: culprit,
1547
1588
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1548
- traversalPath: path70,
1589
+ traversalPath: path71,
1549
1590
  edgeProvenances,
1550
1591
  confidence,
1551
1592
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2133,7 +2174,7 @@ var PolicyViolationsLog = class {
2133
2174
  };
2134
2175
 
2135
2176
  // src/ingest.ts
2136
- var import_types6 = require("@neat.is/types");
2177
+ var import_types7 = require("@neat.is/types");
2137
2178
 
2138
2179
  // src/extract/routes.ts
2139
2180
  init_cjs_shims();
@@ -3191,6 +3232,42 @@ async function addRoutes(graph, services) {
3191
3232
  return { nodesAdded, edgesAdded };
3192
3233
  }
3193
3234
 
3235
+ // src/columns.ts
3236
+ init_cjs_shims();
3237
+ var import_types6 = require("@neat.is/types");
3238
+ var OBSERVED_COLUMN_CONFIDENCE = 0.9;
3239
+ function normalizeProvenances(provenances) {
3240
+ return [...new Set(provenances)].sort();
3241
+ }
3242
+ function foldColumns(existing, names, provenance, confidence) {
3243
+ const out = (existing ?? []).map((c) => ({
3244
+ ...c,
3245
+ provenances: [...c.provenances]
3246
+ }));
3247
+ const byName = new Map(out.map((c) => [c.name, c]));
3248
+ for (const raw of names) {
3249
+ const name = raw.toLowerCase();
3250
+ const prior = byName.get(name);
3251
+ if (!prior) {
3252
+ const col = { name, provenances: [provenance], confidence };
3253
+ byName.set(name, col);
3254
+ out.push(col);
3255
+ continue;
3256
+ }
3257
+ if (!prior.provenances.includes(provenance)) {
3258
+ prior.provenances = normalizeProvenances([...prior.provenances, provenance]);
3259
+ }
3260
+ if (confidence > prior.confidence) prior.confidence = confidence;
3261
+ }
3262
+ return out;
3263
+ }
3264
+ function columnIsDeclared(col) {
3265
+ return col.provenances.includes(import_types6.Provenance.EXTRACTED);
3266
+ }
3267
+ function columnIsObserved(col) {
3268
+ return col.provenances.includes(import_types6.Provenance.OBSERVED);
3269
+ }
3270
+
3194
3271
  // src/ingest.ts
3195
3272
  var HOUR_MS = 60 * 60 * 1e3;
3196
3273
  var DAY_MS = 24 * HOUR_MS;
@@ -3490,11 +3567,11 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
3490
3567
  };
3491
3568
  }
3492
3569
  function reconcileObservedRelPath(graph, serviceName, relPath) {
3493
- if (graph.hasNode((0, import_types6.fileId)(serviceName, relPath))) return relPath;
3570
+ if (graph.hasNode((0, import_types7.fileId)(serviceName, relPath))) return relPath;
3494
3571
  let best = null;
3495
3572
  graph.forEachNode((_id, attrs) => {
3496
3573
  const a = attrs;
3497
- if (a.type !== import_types6.NodeType.FileNode || a.service !== serviceName) return;
3574
+ if (a.type !== import_types7.NodeType.FileNode || a.service !== serviceName) return;
3498
3575
  if (a.discoveredVia === "otel") return;
3499
3576
  const p = a.path;
3500
3577
  if (!p) return;
@@ -3506,14 +3583,14 @@ function reconcileObservedRelPath(graph, serviceName, relPath) {
3506
3583
  }
3507
3584
  function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
3508
3585
  const svcAttrs = graph.hasNode(serviceNodeId) ? graph.getNodeAttributes(serviceNodeId) : void 0;
3509
- const canonicalService = svcAttrs && svcAttrs.type === import_types6.NodeType.ServiceNode && typeof svcAttrs.name === "string" ? svcAttrs.name : serviceName;
3586
+ const canonicalService = svcAttrs && svcAttrs.type === import_types7.NodeType.ServiceNode && typeof svcAttrs.name === "string" ? svcAttrs.name : serviceName;
3510
3587
  const relPath = reconcileObservedRelPath(graph, canonicalService, callSite.relPath);
3511
- const fileNodeId = (0, import_types6.fileId)(canonicalService, relPath);
3588
+ const fileNodeId = (0, import_types7.fileId)(canonicalService, relPath);
3512
3589
  if (!graph.hasNode(fileNodeId)) {
3513
3590
  const language = languageForExt(relPath);
3514
3591
  const node = {
3515
3592
  id: fileNodeId,
3516
- type: import_types6.NodeType.FileNode,
3593
+ type: import_types7.NodeType.FileNode,
3517
3594
  service: canonicalService,
3518
3595
  path: relPath,
3519
3596
  ...language ? { language } : {},
@@ -3522,14 +3599,14 @@ function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
3522
3599
  };
3523
3600
  graph.addNode(fileNodeId, node);
3524
3601
  }
3525
- const containsId = makeObservedEdgeId(import_types6.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
3602
+ const containsId = makeObservedEdgeId(import_types7.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
3526
3603
  if (!graph.hasEdge(containsId)) {
3527
3604
  const edge = {
3528
3605
  id: containsId,
3529
3606
  source: serviceNodeId,
3530
3607
  target: fileNodeId,
3531
- type: import_types6.EdgeType.CONTAINS,
3532
- provenance: import_types6.Provenance.OBSERVED
3608
+ type: import_types7.EdgeType.CONTAINS,
3609
+ provenance: import_types7.Provenance.OBSERVED
3533
3610
  };
3534
3611
  graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
3535
3612
  }
@@ -3553,11 +3630,11 @@ function pickContainingSymbol(candidates, fn) {
3553
3630
  return [...candidates].sort(bySpan)[0].id;
3554
3631
  }
3555
3632
  function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line) {
3556
- const sid = (0, import_types6.symbolId)(service, relPath, fn);
3633
+ const sid = (0, import_types7.symbolId)(service, relPath, fn);
3557
3634
  if (!graph.hasNode(sid)) {
3558
3635
  const node = {
3559
3636
  id: sid,
3560
- type: import_types6.NodeType.SymbolNode,
3637
+ type: import_types7.NodeType.SymbolNode,
3561
3638
  kind: "function",
3562
3639
  qualname: fn,
3563
3640
  span: { startLine: line, endLine: line },
@@ -3567,14 +3644,14 @@ function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line)
3567
3644
  };
3568
3645
  graph.addNode(sid, node);
3569
3646
  }
3570
- const containsId = makeObservedEdgeId(import_types6.EdgeType.CONTAINS, fileNodeId, sid);
3647
+ const containsId = makeObservedEdgeId(import_types7.EdgeType.CONTAINS, fileNodeId, sid);
3571
3648
  if (!graph.hasEdge(containsId)) {
3572
3649
  const edge = {
3573
3650
  id: containsId,
3574
3651
  source: fileNodeId,
3575
3652
  target: sid,
3576
- type: import_types6.EdgeType.CONTAINS,
3577
- provenance: import_types6.Provenance.OBSERVED
3653
+ type: import_types7.EdgeType.CONTAINS,
3654
+ provenance: import_types7.Provenance.OBSERVED
3578
3655
  };
3579
3656
  graph.addEdgeWithKey(containsId, fileNodeId, sid, edge);
3580
3657
  }
@@ -3586,9 +3663,9 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
3586
3663
  let sawSymbol = false;
3587
3664
  const candidates = [];
3588
3665
  graph.forEachOutboundEdge(fileNodeId, (_edge, edgeAttrs, _source, target) => {
3589
- if (edgeAttrs.type !== import_types6.EdgeType.CONTAINS) return;
3666
+ if (edgeAttrs.type !== import_types7.EdgeType.CONTAINS) return;
3590
3667
  const t = graph.getNodeAttributes(target);
3591
- if (t.type !== import_types6.NodeType.SymbolNode) return;
3668
+ if (t.type !== import_types7.NodeType.SymbolNode) return;
3592
3669
  sawSymbol = true;
3593
3670
  if (line >= t.span.startLine && line <= t.span.endLine) {
3594
3671
  candidates.push({ id: target, symbol: t });
@@ -3601,17 +3678,17 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
3601
3678
  return fileNodeId;
3602
3679
  }
3603
3680
  function makeObservedEdgeId(type, source, target) {
3604
- return (0, import_types6.observedEdgeId)(source, target, type);
3681
+ return (0, import_types7.observedEdgeId)(source, target, type);
3605
3682
  }
3606
3683
  function makeInferredEdgeId(type, source, target) {
3607
- return (0, import_types6.inferredEdgeId)(source, target, type);
3684
+ return (0, import_types7.inferredEdgeId)(source, target, type);
3608
3685
  }
3609
3686
  var INFERRED_CONFIDENCE = 0.6;
3610
3687
  var STITCH_MAX_DEPTH = 2;
3611
3688
  var STITCH_EDGE_TYPES = /* @__PURE__ */ new Set([
3612
- import_types6.EdgeType.CALLS,
3613
- import_types6.EdgeType.CONNECTS_TO,
3614
- import_types6.EdgeType.DEPENDS_ON
3689
+ import_types7.EdgeType.CALLS,
3690
+ import_types7.EdgeType.CONNECTS_TO,
3691
+ import_types7.EdgeType.DEPENDS_ON
3615
3692
  ]);
3616
3693
  var WIRE_SPAN_KIND_CLIENT = 3;
3617
3694
  var WIRE_SPAN_KIND_PRODUCER = 4;
@@ -3627,11 +3704,11 @@ function spanServesGraphqlOperation(kind) {
3627
3704
  return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
3628
3705
  }
3629
3706
  function ensureGraphqlOperationNode(graph, serviceName, operationType, operationName) {
3630
- const id = (0, import_types6.graphqlOperationId)(serviceName, operationType, operationName);
3707
+ const id = (0, import_types7.graphqlOperationId)(serviceName, operationType, operationName);
3631
3708
  if (graph.hasNode(id)) return id;
3632
3709
  const node = {
3633
3710
  id,
3634
- type: import_types6.NodeType.GraphQLOperationNode,
3711
+ type: import_types7.NodeType.GraphQLOperationNode,
3635
3712
  name: operationName,
3636
3713
  service: serviceName,
3637
3714
  operationType: operationType.toLowerCase(),
@@ -3645,11 +3722,11 @@ function spanServesGrpcMethod(kind) {
3645
3722
  return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
3646
3723
  }
3647
3724
  function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
3648
- const id = (0, import_types6.grpcMethodId)(rpcService, rpcMethod);
3725
+ const id = (0, import_types7.grpcMethodId)(rpcService, rpcMethod);
3649
3726
  if (graph.hasNode(id)) return id;
3650
3727
  const node = {
3651
3728
  id,
3652
- type: import_types6.NodeType.GrpcMethodNode,
3729
+ type: import_types7.NodeType.GrpcMethodNode,
3653
3730
  name: `${rpcService}/${rpcMethod}`,
3654
3731
  rpcService,
3655
3732
  rpcMethod,
@@ -3662,11 +3739,11 @@ function spanServesWebsocketChannel(kind) {
3662
3739
  return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
3663
3740
  }
3664
3741
  function ensureWebsocketChannelNode(graph, serviceName, channel) {
3665
- const id = (0, import_types6.websocketChannelId)(serviceName, channel);
3742
+ const id = (0, import_types7.websocketChannelId)(serviceName, channel);
3666
3743
  if (graph.hasNode(id)) return id;
3667
3744
  const node = {
3668
3745
  id,
3669
- type: import_types6.NodeType.WebSocketChannelNode,
3746
+ type: import_types7.NodeType.WebSocketChannelNode,
3670
3747
  name: channel,
3671
3748
  service: serviceName,
3672
3749
  channel,
@@ -3679,11 +3756,11 @@ function messagingDestinationKind(system) {
3679
3756
  return `${system}-topic`;
3680
3757
  }
3681
3758
  function ensureMessagingDestinationNode(graph, system, destination) {
3682
- const id = (0, import_types6.infraId)(messagingDestinationKind(system), destination);
3759
+ const id = (0, import_types7.infraId)(messagingDestinationKind(system), destination);
3683
3760
  if (graph.hasNode(id)) return id;
3684
3761
  const node = {
3685
3762
  id,
3686
- type: import_types6.NodeType.InfraNode,
3763
+ type: import_types7.NodeType.InfraNode,
3687
3764
  name: destination,
3688
3765
  provider: "self",
3689
3766
  kind: messagingDestinationKind(system)
@@ -3727,9 +3804,9 @@ function lookupParentSpan(traceId, parentSpanId, now) {
3727
3804
  };
3728
3805
  }
3729
3806
  function resolveServiceId(graph, host, env) {
3730
- const envTagged = (0, import_types6.serviceId)(host, env);
3807
+ const envTagged = (0, import_types7.serviceId)(host, env);
3731
3808
  if (graph.hasNode(envTagged)) return envTagged;
3732
- const envLess = (0, import_types6.serviceId)(host);
3809
+ const envLess = (0, import_types7.serviceId)(host);
3733
3810
  if (envLess !== envTagged && graph.hasNode(envLess)) return envLess;
3734
3811
  let sameEnv = null;
3735
3812
  let envLessMatch = null;
@@ -3737,7 +3814,7 @@ function resolveServiceId(graph, host, env) {
3737
3814
  graph.forEachNode((id, attrs) => {
3738
3815
  if (sameEnv) return;
3739
3816
  const a = attrs;
3740
- if (a.type !== import_types6.NodeType.ServiceNode) return;
3817
+ if (a.type !== import_types7.NodeType.ServiceNode) return;
3741
3818
  const matchesByName = a.name === host;
3742
3819
  const matchesByAlias = a.aliases ? a.aliases.includes(host) : false;
3743
3820
  if (!matchesByName && !matchesByAlias) return;
@@ -3752,14 +3829,14 @@ function resolveServiceId(graph, host, env) {
3752
3829
  return sameEnv ?? envLessMatch ?? anyMatch;
3753
3830
  }
3754
3831
  function frontierIdFor(host) {
3755
- return (0, import_types6.frontierId)(host);
3832
+ return (0, import_types7.frontierId)(host);
3756
3833
  }
3757
3834
  function ensureServiceNode(graph, serviceName, env) {
3758
- const id = (0, import_types6.serviceId)(serviceName, env);
3835
+ const id = (0, import_types7.serviceId)(serviceName, env);
3759
3836
  if (graph.hasNode(id)) return id;
3760
3837
  const wanted = serviceName.toLowerCase();
3761
3838
  const extractedId = graph.findNode((_nid, attrs) => {
3762
- if (attrs.type !== import_types6.NodeType.ServiceNode) return false;
3839
+ if (attrs.type !== import_types7.NodeType.ServiceNode) return false;
3763
3840
  const svc = attrs;
3764
3841
  if (svc.discoveredVia === "otel") return false;
3765
3842
  return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
@@ -3767,7 +3844,7 @@ function ensureServiceNode(graph, serviceName, env) {
3767
3844
  if (extractedId) return extractedId;
3768
3845
  const node = {
3769
3846
  id,
3770
- type: import_types6.NodeType.ServiceNode,
3847
+ type: import_types7.NodeType.ServiceNode,
3771
3848
  name: serviceName,
3772
3849
  language: "unknown",
3773
3850
  discoveredVia: "otel",
@@ -3777,11 +3854,11 @@ function ensureServiceNode(graph, serviceName, env) {
3777
3854
  return id;
3778
3855
  }
3779
3856
  function ensureInfraNode(graph, kind, name, provider) {
3780
- const id = (0, import_types6.infraId)(kind, name);
3857
+ const id = (0, import_types7.infraId)(kind, name);
3781
3858
  if (graph.hasNode(id)) return id;
3782
3859
  const node = {
3783
3860
  id,
3784
- type: import_types6.NodeType.InfraNode,
3861
+ type: import_types7.NodeType.InfraNode,
3785
3862
  name,
3786
3863
  provider,
3787
3864
  kind
@@ -3789,12 +3866,27 @@ function ensureInfraNode(graph, kind, name, provider) {
3789
3866
  graph.addNode(id, node);
3790
3867
  return id;
3791
3868
  }
3869
+ var COLUMN_BEARING_INFRA_KINDS = /* @__PURE__ */ new Set(["sql-table", "supabase-table"]);
3870
+ function mergeColumnsAt(graph, tableNodeId, columns, provenance, confidence) {
3871
+ if (!columns || columns.length === 0 || !graph.hasNode(tableNodeId)) return;
3872
+ const node = graph.getNodeAttributes(tableNodeId);
3873
+ if (node.type !== import_types7.NodeType.InfraNode || !node.kind || !COLUMN_BEARING_INFRA_KINDS.has(node.kind)) {
3874
+ return;
3875
+ }
3876
+ graph.replaceNodeAttributes(tableNodeId, {
3877
+ ...node,
3878
+ columns: foldColumns(node.columns, columns, provenance, confidence)
3879
+ });
3880
+ }
3881
+ function mergeObservedColumns(graph, tableNodeId, columns) {
3882
+ mergeColumnsAt(graph, tableNodeId, columns, import_types7.Provenance.OBSERVED, OBSERVED_COLUMN_CONFIDENCE);
3883
+ }
3792
3884
  function ensureDatabaseNode(graph, host, engine) {
3793
- const id = (0, import_types6.databaseId)(host);
3885
+ const id = (0, import_types7.databaseId)(host);
3794
3886
  if (graph.hasNode(id)) return id;
3795
3887
  const node = {
3796
3888
  id,
3797
- type: import_types6.NodeType.DatabaseNode,
3889
+ type: import_types7.NodeType.DatabaseNode,
3798
3890
  name: host,
3799
3891
  engine,
3800
3892
  engineVersion: "unknown",
@@ -3806,11 +3898,11 @@ function ensureDatabaseNode(graph, host, engine) {
3806
3898
  return id;
3807
3899
  }
3808
3900
  function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
3809
- const id = (0, import_types6.localDatabaseId)(serviceName, name);
3901
+ const id = (0, import_types7.localDatabaseId)(serviceName, name);
3810
3902
  if (graph.hasNode(id)) return id;
3811
3903
  const node = {
3812
3904
  id,
3813
- type: import_types6.NodeType.DatabaseNode,
3905
+ type: import_types7.NodeType.DatabaseNode,
3814
3906
  name,
3815
3907
  engine,
3816
3908
  engineVersion: "unknown",
@@ -3825,17 +3917,17 @@ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
3825
3917
  const sources = [serviceNodeId];
3826
3918
  for (const edgeId of graph.outboundEdges(serviceNodeId)) {
3827
3919
  const e = graph.getEdgeAttributes(edgeId);
3828
- if (e.type === import_types6.EdgeType.CONTAINS) sources.push(e.target);
3920
+ if (e.type === import_types7.EdgeType.CONTAINS) sources.push(e.target);
3829
3921
  }
3830
3922
  const matches = /* @__PURE__ */ new Set();
3831
3923
  for (const src of sources) {
3832
3924
  if (!graph.hasNode(src)) continue;
3833
3925
  for (const edgeId of graph.outboundEdges(src)) {
3834
3926
  const edge = graph.getEdgeAttributes(edgeId);
3835
- if (edge.type !== import_types6.EdgeType.CONNECTS_TO || edge.provenance !== import_types6.Provenance.EXTRACTED) continue;
3927
+ if (edge.type !== import_types7.EdgeType.CONNECTS_TO || edge.provenance !== import_types7.Provenance.EXTRACTED) continue;
3836
3928
  if (!graph.hasNode(edge.target)) continue;
3837
3929
  const target = graph.getNodeAttributes(edge.target);
3838
- if (target.type !== import_types6.NodeType.DatabaseNode || target.engine !== engine) continue;
3930
+ if (target.type !== import_types7.NodeType.DatabaseNode || target.engine !== engine) continue;
3839
3931
  matches.add(edge.target);
3840
3932
  }
3841
3933
  }
@@ -3850,7 +3942,7 @@ function ensureFrontierNode(graph, host, ts) {
3850
3942
  }
3851
3943
  const node = {
3852
3944
  id,
3853
- type: import_types6.NodeType.FrontierNode,
3945
+ type: import_types7.NodeType.FrontierNode,
3854
3946
  name: host,
3855
3947
  host,
3856
3948
  firstObserved: ts,
@@ -3874,11 +3966,11 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
3874
3966
  };
3875
3967
  const updated = {
3876
3968
  ...existing,
3877
- provenance: import_types6.Provenance.OBSERVED,
3969
+ provenance: import_types7.Provenance.OBSERVED,
3878
3970
  lastObserved: ts,
3879
3971
  callCount: newSpanCount,
3880
3972
  signal: newSignal,
3881
- confidence: (0, import_types6.confidenceForObservedSignal)(newSignal),
3973
+ confidence: (0, import_types7.confidenceForObservedSignal)(newSignal),
3882
3974
  grain
3883
3975
  // backfills legacy edges that predate ADR-142
3884
3976
  };
@@ -3895,8 +3987,8 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
3895
3987
  source,
3896
3988
  target,
3897
3989
  type,
3898
- provenance: import_types6.Provenance.OBSERVED,
3899
- confidence: (0, import_types6.confidenceForObservedSignal)(signal),
3990
+ provenance: import_types7.Provenance.OBSERVED,
3991
+ confidence: (0, import_types7.confidenceForObservedSignal)(signal),
3900
3992
  lastObserved: ts,
3901
3993
  callCount: 1,
3902
3994
  signal,
@@ -3918,9 +4010,9 @@ function stitchTrace(graph, sourceServiceId, ts) {
3918
4010
  const outbound = graph.outboundEdges(nodeId);
3919
4011
  for (const edgeId of outbound) {
3920
4012
  const edge = graph.getEdgeAttributes(edgeId);
3921
- if (edge.provenance !== import_types6.Provenance.EXTRACTED) continue;
4013
+ if (edge.provenance !== import_types7.Provenance.EXTRACTED) continue;
3922
4014
  if (!STITCH_EDGE_TYPES.has(edge.type)) continue;
3923
- if (graph.hasEdge((0, import_types6.observedEdgeId)(edge.source, edge.target, edge.type))) continue;
4015
+ if (graph.hasEdge((0, import_types7.observedEdgeId)(edge.source, edge.target, edge.type))) continue;
3924
4016
  upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts);
3925
4017
  if (!visited.has(edge.target)) {
3926
4018
  visited.add(edge.target);
@@ -3942,7 +4034,7 @@ function upsertInferredEdge(graph, type, source, target, ts) {
3942
4034
  source,
3943
4035
  target,
3944
4036
  type,
3945
- provenance: import_types6.Provenance.INFERRED,
4037
+ provenance: import_types7.Provenance.INFERRED,
3946
4038
  confidence: INFERRED_CONFIDENCE,
3947
4039
  lastObserved: ts
3948
4040
  };
@@ -3953,12 +4045,12 @@ async function appendErrorEvent(ctx, ev) {
3953
4045
  await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
3954
4046
  }
3955
4047
  function incidentAffectedNode(span, graph, scanPath) {
3956
- const sid = (0, import_types6.serviceId)(span.service, span.env);
4048
+ const sid = (0, import_types7.serviceId)(span.service, span.env);
3957
4049
  const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
3958
4050
  const callSite = callSiteFromSpan(span, serviceNode, scanPath);
3959
4051
  if (callSite) {
3960
4052
  const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
3961
- return (0, import_types6.fileId)(span.service, relPath);
4053
+ return (0, import_types7.fileId)(span.service, relPath);
3962
4054
  }
3963
4055
  return sid;
3964
4056
  }
@@ -4083,7 +4175,7 @@ function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
4083
4175
  graph.forEachNode((id, attrs) => {
4084
4176
  if (found) return;
4085
4177
  const a = attrs;
4086
- if (a.type !== import_types6.NodeType.RouteNode || a.service !== serviceName) return;
4178
+ if (a.type !== import_types7.NodeType.RouteNode || a.service !== serviceName) return;
4087
4179
  if (m && a.method !== "ALL" && a.method !== m) return;
4088
4180
  if (normalizePathTemplate(a.pathTemplate) === target) found = id;
4089
4181
  });
@@ -4114,7 +4206,7 @@ async function handleSpan(ctx, span) {
4114
4206
  let targetId;
4115
4207
  if (host) {
4116
4208
  ensureDatabaseNode(ctx.graph, host, span.dbSystem);
4117
- targetId = (0, import_types6.databaseId)(host);
4209
+ targetId = (0, import_types7.databaseId)(host);
4118
4210
  } else {
4119
4211
  const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
4120
4212
  if (declared) {
@@ -4131,7 +4223,7 @@ async function handleSpan(ctx, span) {
4131
4223
  }
4132
4224
  const result = upsertObservedEdge(
4133
4225
  ctx.graph,
4134
- import_types6.EdgeType.CONNECTS_TO,
4226
+ import_types7.EdgeType.CONNECTS_TO,
4135
4227
  observedSource(),
4136
4228
  targetId,
4137
4229
  ts,
@@ -4143,7 +4235,7 @@ async function handleSpan(ctx, span) {
4143
4235
  const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
4144
4236
  upsertObservedEdge(
4145
4237
  ctx.graph,
4146
- import_types6.EdgeType.CALLS,
4238
+ import_types7.EdgeType.CALLS,
4147
4239
  observedSource(),
4148
4240
  collectionId,
4149
4241
  ts,
@@ -4155,13 +4247,14 @@ async function handleSpan(ctx, span) {
4155
4247
  const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
4156
4248
  upsertObservedEdge(
4157
4249
  ctx.graph,
4158
- import_types6.EdgeType.CALLS,
4250
+ import_types7.EdgeType.CALLS,
4159
4251
  observedSource(),
4160
4252
  tableId,
4161
4253
  ts,
4162
4254
  isError,
4163
4255
  callSiteEvidence
4164
4256
  );
4257
+ mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
4165
4258
  }
4166
4259
  }
4167
4260
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
@@ -4170,7 +4263,7 @@ async function handleSpan(ctx, span) {
4170
4263
  span.messagingSystem,
4171
4264
  span.messagingDestination
4172
4265
  );
4173
- const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types6.EdgeType.CONSUMES_FROM : import_types6.EdgeType.PUBLISHES_TO;
4266
+ const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types7.EdgeType.CONSUMES_FROM : import_types7.EdgeType.PUBLISHES_TO;
4174
4267
  const result = upsertObservedEdge(
4175
4268
  ctx.graph,
4176
4269
  edgeType,
@@ -4190,7 +4283,7 @@ async function handleSpan(ctx, span) {
4190
4283
  );
4191
4284
  const result = upsertObservedEdge(
4192
4285
  ctx.graph,
4193
- import_types6.EdgeType.CONTAINS,
4286
+ import_types7.EdgeType.CONTAINS,
4194
4287
  observedSource(),
4195
4288
  targetId,
4196
4289
  ts,
@@ -4202,7 +4295,7 @@ async function handleSpan(ctx, span) {
4202
4295
  const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
4203
4296
  const result = upsertObservedEdge(
4204
4297
  ctx.graph,
4205
- import_types6.EdgeType.CONTAINS,
4298
+ import_types7.EdgeType.CONTAINS,
4206
4299
  observedSource(),
4207
4300
  targetId,
4208
4301
  ts,
@@ -4218,7 +4311,7 @@ async function handleSpan(ctx, span) {
4218
4311
  );
4219
4312
  const result = upsertObservedEdge(
4220
4313
  ctx.graph,
4221
- import_types6.EdgeType.CONNECTS_TO,
4314
+ import_types7.EdgeType.CONNECTS_TO,
4222
4315
  observedSource(),
4223
4316
  targetId,
4224
4317
  ts,
@@ -4234,7 +4327,7 @@ async function handleSpan(ctx, span) {
4234
4327
  if (targetId && targetId !== sourceId) {
4235
4328
  upsertObservedEdge(
4236
4329
  ctx.graph,
4237
- import_types6.EdgeType.CALLS,
4330
+ import_types7.EdgeType.CALLS,
4238
4331
  observedSource(),
4239
4332
  targetId,
4240
4333
  ts,
@@ -4247,7 +4340,7 @@ async function handleSpan(ctx, span) {
4247
4340
  const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
4248
4341
  upsertObservedEdge(
4249
4342
  ctx.graph,
4250
- import_types6.EdgeType.CALLS,
4343
+ import_types7.EdgeType.CALLS,
4251
4344
  observedSource(),
4252
4345
  frontierNodeId,
4253
4346
  ts,
@@ -4273,7 +4366,7 @@ async function handleSpan(ctx, span) {
4273
4366
  } : void 0;
4274
4367
  upsertObservedEdge(
4275
4368
  ctx.graph,
4276
- import_types6.EdgeType.CALLS,
4369
+ import_types7.EdgeType.CALLS,
4277
4370
  fallbackSource,
4278
4371
  sourceId,
4279
4372
  ts,
@@ -4283,7 +4376,7 @@ async function handleSpan(ctx, span) {
4283
4376
  }
4284
4377
  }
4285
4378
  }
4286
- if (span.httpRoute && (span.kind === 2 || span.kind === 0 || span.kind === void 0)) {
4379
+ if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
4287
4380
  const routeNodeId = findRouteNodeByHttpRoute(
4288
4381
  ctx.graph,
4289
4382
  span.service,
@@ -4292,7 +4385,7 @@ async function handleSpan(ctx, span) {
4292
4385
  );
4293
4386
  if (routeNodeId) {
4294
4387
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
4295
- upsertObservedEdge(ctx.graph, import_types6.EdgeType.CONTAINS, (0, import_types6.serviceId)(routeSvc), routeNodeId, ts, isError);
4388
+ upsertObservedEdge(ctx.graph, import_types7.EdgeType.CONTAINS, (0, import_types7.serviceId)(routeSvc), routeNodeId, ts, isError);
4296
4389
  }
4297
4390
  }
4298
4391
  if (span.statusCode === 2) {
@@ -4331,7 +4424,7 @@ function promoteFrontierNodes(graph, opts = {}) {
4331
4424
  const aliasIndex = /* @__PURE__ */ new Map();
4332
4425
  graph.forEachNode((id, attrs) => {
4333
4426
  const a = attrs;
4334
- if (a.type !== import_types6.NodeType.ServiceNode) return;
4427
+ if (a.type !== import_types7.NodeType.ServiceNode) return;
4335
4428
  aliasIndex.set(a.name, id);
4336
4429
  if (a.aliases) {
4337
4430
  for (const alias of a.aliases) aliasIndex.set(alias, id);
@@ -4340,7 +4433,7 @@ function promoteFrontierNodes(graph, opts = {}) {
4340
4433
  const toPromote = [];
4341
4434
  graph.forEachNode((id, attrs) => {
4342
4435
  const a = attrs;
4343
- if (a.type !== import_types6.NodeType.FrontierNode) return;
4436
+ if (a.type !== import_types7.NodeType.FrontierNode) return;
4344
4437
  const target = aliasIndex.get(a.host);
4345
4438
  if (!target) return;
4346
4439
  if (target === id) return;
@@ -4374,7 +4467,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId7) {
4374
4467
  }
4375
4468
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
4376
4469
  graph.dropEdge(oldEdgeId);
4377
- 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);
4470
+ const newId = edge.provenance === import_types7.Provenance.OBSERVED ? (0, import_types7.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types7.Provenance.INFERRED ? (0, import_types7.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types7.extractedEdgeId)(newSource, newTarget, edge.type);
4378
4471
  if (graph.hasEdge(newId)) {
4379
4472
  const existing = graph.getEdgeAttributes(newId);
4380
4473
  const merged = {
@@ -4408,12 +4501,12 @@ async function markStaleEdges(graph, options = {}) {
4408
4501
  const project = options.project ?? DEFAULT_PROJECT;
4409
4502
  graph.forEachEdge((id, attrs) => {
4410
4503
  const e = attrs;
4411
- if (e.provenance !== import_types6.Provenance.OBSERVED) return;
4504
+ if (e.provenance !== import_types7.Provenance.OBSERVED) return;
4412
4505
  if (!e.lastObserved) return;
4413
4506
  const threshold = thresholdForEdgeType(e.type, thresholds);
4414
4507
  const age = now - new Date(e.lastObserved).getTime();
4415
4508
  if (age > threshold) {
4416
- const updated = { ...e, provenance: import_types6.Provenance.STALE, confidence: 0.3 };
4509
+ const updated = { ...e, provenance: import_types7.Provenance.STALE, confidence: 0.3 };
4417
4510
  graph.replaceEdgeAttributes(id, updated);
4418
4511
  events.push({
4419
4512
  edgeId: id,
@@ -4430,8 +4523,8 @@ async function markStaleEdges(graph, options = {}) {
4430
4523
  project,
4431
4524
  payload: {
4432
4525
  edgeId: id,
4433
- from: import_types6.Provenance.OBSERVED,
4434
- to: import_types6.Provenance.STALE
4526
+ from: import_types7.Provenance.OBSERVED,
4527
+ to: import_types7.Provenance.STALE
4435
4528
  }
4436
4529
  });
4437
4530
  }
@@ -4540,7 +4633,7 @@ function mergeSnapshot(graph, snapshot) {
4540
4633
  const validEdges = [];
4541
4634
  for (const node of incomingNodes) {
4542
4635
  if (node.attributes === void 0) continue;
4543
- const parsed = import_types6.GraphNodeSchema.safeParse(node.attributes);
4636
+ const parsed = import_types7.GraphNodeSchema.safeParse(node.attributes);
4544
4637
  if (!parsed.success) {
4545
4638
  issues.push(`node "${node.key}": ${describeZodIssues(parsed.error)}`);
4546
4639
  continue;
@@ -4549,7 +4642,7 @@ function mergeSnapshot(graph, snapshot) {
4549
4642
  }
4550
4643
  for (const edge of incomingEdges) {
4551
4644
  if (edge.attributes === void 0) continue;
4552
- const parsed = import_types6.GraphEdgeSchema.safeParse(edge.attributes);
4645
+ const parsed = import_types7.GraphEdgeSchema.safeParse(edge.attributes);
4553
4646
  if (!parsed.success) {
4554
4647
  const label = edge.key ?? `${edge.source}->${edge.target}`;
4555
4648
  issues.push(`edge "${label}": ${describeZodIssues(parsed.error)}`);
@@ -4583,7 +4676,7 @@ var import_node_fs11 = require("fs");
4583
4676
  var import_node_path12 = __toESM(require("path"), 1);
4584
4677
  var import_ignore = __toESM(require("ignore"), 1);
4585
4678
  var import_minimatch2 = require("minimatch");
4586
- var import_types8 = require("@neat.is/types");
4679
+ var import_types9 = require("@neat.is/types");
4587
4680
 
4588
4681
  // src/extract/python.ts
4589
4682
  init_cjs_shims();
@@ -4656,7 +4749,7 @@ function pythonToPackage(service) {
4656
4749
  init_cjs_shims();
4657
4750
  var import_node_fs9 = require("fs");
4658
4751
  var import_node_path10 = __toESM(require("path"), 1);
4659
- var import_types7 = require("@neat.is/types");
4752
+ var import_types8 = require("@neat.is/types");
4660
4753
  function parseGoMod(source) {
4661
4754
  const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
4662
4755
  if (!module2) return null;
@@ -4684,8 +4777,8 @@ async function discoverGoService(scanPath, dir) {
4684
4777
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
4685
4778
  const pkg = { name, dependencies: mod.dependencies };
4686
4779
  const node = {
4687
- id: (0, import_types7.serviceId)(name),
4688
- type: import_types7.NodeType.ServiceNode,
4780
+ id: (0, import_types8.serviceId)(name),
4781
+ type: import_types8.NodeType.ServiceNode,
4689
4782
  name,
4690
4783
  language: "go",
4691
4784
  dependencies: mod.dependencies,
@@ -4871,8 +4964,8 @@ async function discoverNodeService(scanPath, dir) {
4871
4964
  const framework = detectJsFramework(pkg);
4872
4965
  const language = await detectJsServiceLanguage(dir, pkg);
4873
4966
  const node = {
4874
- id: (0, import_types8.serviceId)(pkg.name),
4875
- type: import_types8.NodeType.ServiceNode,
4967
+ id: (0, import_types9.serviceId)(pkg.name),
4968
+ type: import_types9.NodeType.ServiceNode,
4876
4969
  name: pkg.name,
4877
4970
  language,
4878
4971
  version: pkg.version,
@@ -4888,8 +4981,8 @@ async function discoverPyService(scanPath, dir) {
4888
4981
  if (!py) return null;
4889
4982
  const pkg = pythonToPackage(py);
4890
4983
  const node = {
4891
- id: (0, import_types8.serviceId)(py.name),
4892
- type: import_types8.NodeType.ServiceNode,
4984
+ id: (0, import_types9.serviceId)(py.name),
4985
+ type: import_types9.NodeType.ServiceNode,
4893
4986
  name: py.name,
4894
4987
  language: "python",
4895
4988
  version: py.version,
@@ -4985,7 +5078,7 @@ init_cjs_shims();
4985
5078
  var import_node_path13 = __toESM(require("path"), 1);
4986
5079
  var import_node_fs12 = require("fs");
4987
5080
  var import_yaml2 = require("yaml");
4988
- var import_types9 = require("@neat.is/types");
5081
+ var import_types10 = require("@neat.is/types");
4989
5082
  var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
4990
5083
  "Service",
4991
5084
  "Deployment",
@@ -4995,7 +5088,7 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
4995
5088
  function addAliases(graph, serviceId7, candidates) {
4996
5089
  if (!graph.hasNode(serviceId7)) return;
4997
5090
  const node = graph.getNodeAttributes(serviceId7);
4998
- if (node.type !== import_types9.NodeType.ServiceNode) return;
5091
+ if (node.type !== import_types10.NodeType.ServiceNode) return;
4999
5092
  const set = new Set(node.aliases ?? []);
5000
5093
  for (const c of candidates) {
5001
5094
  if (!c) continue;
@@ -5176,7 +5269,7 @@ var import_node_path15 = __toESM(require("path"), 1);
5176
5269
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
5177
5270
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
5178
5271
  var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
5179
- var import_types10 = require("@neat.is/types");
5272
+ var import_types11 = require("@neat.is/types");
5180
5273
  var PARSE_CHUNK2 = 16384;
5181
5274
  var GRAMMAR_BY_EXT = {
5182
5275
  ".ts": import_tree_sitter_typescript.default.typescript,
@@ -5267,7 +5360,7 @@ function disambiguate(defs) {
5267
5360
  }
5268
5361
  async function addSymbols(graph, services) {
5269
5362
  const parsers = /* @__PURE__ */ new Map();
5270
- const parserForExt = (ext) => {
5363
+ const parserForExt2 = (ext) => {
5271
5364
  const grammar = GRAMMAR_BY_EXT[ext];
5272
5365
  if (!grammar) return null;
5273
5366
  let parser = parsers.get(ext);
@@ -5283,7 +5376,7 @@ async function addSymbols(graph, services) {
5283
5376
  for (const service of services) {
5284
5377
  const files = await loadSourceFiles(service.dir);
5285
5378
  for (const file of files) {
5286
- const parser = parserForExt(import_node_path15.default.extname(file.path));
5379
+ const parser = parserForExt2(import_node_path15.default.extname(file.path));
5287
5380
  if (!parser) continue;
5288
5381
  const relPath = toPosix(import_node_path15.default.relative(service.dir, file.path));
5289
5382
  let defs;
@@ -5304,11 +5397,11 @@ async function addSymbols(graph, services) {
5304
5397
  nodesAdded += fn;
5305
5398
  edgesAdded += fe;
5306
5399
  for (const { def, disambiguator } of disambiguate(defs)) {
5307
- const sid = (0, import_types10.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
5400
+ const sid = (0, import_types11.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
5308
5401
  if (!graph.hasNode(sid)) {
5309
5402
  const node = {
5310
5403
  id: sid,
5311
- type: import_types10.NodeType.SymbolNode,
5404
+ type: import_types11.NodeType.SymbolNode,
5312
5405
  kind: def.kind,
5313
5406
  qualname: def.qualname,
5314
5407
  span: { startLine: def.startLine, endLine: def.endLine },
@@ -5319,15 +5412,15 @@ async function addSymbols(graph, services) {
5319
5412
  graph.addNode(sid, node);
5320
5413
  nodesAdded++;
5321
5414
  }
5322
- const containsId = (0, import_types10.extractedEdgeId)(fileNodeId, sid, import_types10.EdgeType.CONTAINS);
5415
+ const containsId = (0, import_types11.extractedEdgeId)(fileNodeId, sid, import_types11.EdgeType.CONTAINS);
5323
5416
  if (!graph.hasEdge(containsId)) {
5324
5417
  const edge = {
5325
5418
  id: containsId,
5326
5419
  source: fileNodeId,
5327
5420
  target: sid,
5328
- type: import_types10.EdgeType.CONTAINS,
5329
- provenance: import_types10.Provenance.EXTRACTED,
5330
- confidence: (0, import_types10.confidenceForExtracted)("structural"),
5421
+ type: import_types11.EdgeType.CONTAINS,
5422
+ provenance: import_types11.Provenance.EXTRACTED,
5423
+ confidence: (0, import_types11.confidenceForExtracted)("structural"),
5331
5424
  evidence: {
5332
5425
  file: relPath,
5333
5426
  line: def.startLine,
@@ -5347,7 +5440,7 @@ async function addSymbols(graph, services) {
5347
5440
  init_cjs_shims();
5348
5441
  var import_node_path17 = __toESM(require("path"), 1);
5349
5442
  var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
5350
- var import_types12 = require("@neat.is/types");
5443
+ var import_types13 = require("@neat.is/types");
5351
5444
 
5352
5445
  // src/extract/imports.ts
5353
5446
  init_cjs_shims();
@@ -5357,7 +5450,7 @@ var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
5357
5450
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
5358
5451
  var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
5359
5452
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
5360
- var import_types11 = require("@neat.is/types");
5453
+ var import_types12 = require("@neat.is/types");
5361
5454
  var PARSE_CHUNK3 = 16384;
5362
5455
  function parseSource3(parser, source) {
5363
5456
  return parser.parse(
@@ -5631,17 +5724,17 @@ async function resolveGoImport(specifier, modulePath, serviceDir) {
5631
5724
  return toPosix(import_node_path16.default.relative(serviceDir, candidates[0]));
5632
5725
  }
5633
5726
  function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
5634
- const importeeFileId = (0, import_types11.fileId)(serviceName, importeeRelPath);
5727
+ const importeeFileId = (0, import_types12.fileId)(serviceName, importeeRelPath);
5635
5728
  if (!graph.hasNode(importeeFileId)) return 0;
5636
- const edgeId = (0, import_types11.extractedEdgeId)(importerFileId, importeeFileId, import_types11.EdgeType.IMPORTS);
5729
+ const edgeId = (0, import_types12.extractedEdgeId)(importerFileId, importeeFileId, import_types12.EdgeType.IMPORTS);
5637
5730
  if (graph.hasEdge(edgeId)) return 0;
5638
5731
  const edge = {
5639
5732
  id: edgeId,
5640
5733
  source: importerFileId,
5641
5734
  target: importeeFileId,
5642
- type: import_types11.EdgeType.IMPORTS,
5643
- provenance: import_types11.Provenance.EXTRACTED,
5644
- confidence: (0, import_types11.confidenceForExtracted)("structural"),
5735
+ type: import_types12.EdgeType.IMPORTS,
5736
+ provenance: import_types12.Provenance.EXTRACTED,
5737
+ confidence: (0, import_types12.confidenceForExtracted)("structural"),
5645
5738
  evidence: { file: importerRelPath, line, snippet: snippet2 }
5646
5739
  };
5647
5740
  graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
@@ -5658,7 +5751,7 @@ async function addImports(graph, services) {
5658
5751
  for (const file of files) {
5659
5752
  if (isTestPath(file.path)) continue;
5660
5753
  const relFile = toPosix(import_node_path16.default.relative(service.dir, file.path));
5661
- const importerFileId = (0, import_types11.fileId)(service.pkg.name, relFile);
5754
+ const importerFileId = (0, import_types12.fileId)(service.pkg.name, relFile);
5662
5755
  const isPython = import_node_path16.default.extname(file.path) === ".py";
5663
5756
  const isGo = import_node_path16.default.extname(file.path) === ".go";
5664
5757
  if (isGo) {
@@ -5819,7 +5912,7 @@ function stringInner(node) {
5819
5912
  }
5820
5913
  async function addSymbolEdges(graph, services) {
5821
5914
  const parsers = /* @__PURE__ */ new Map();
5822
- const parserForExt = (ext) => {
5915
+ const parserForExt2 = (ext) => {
5823
5916
  const grammar = GRAMMAR_BY_EXT[ext];
5824
5917
  if (!grammar) return null;
5825
5918
  let parser = parsers.get(ext);
@@ -5836,7 +5929,7 @@ async function addSymbolEdges(graph, services) {
5836
5929
  const tsPaths = await loadTsPathConfig(service.dir);
5837
5930
  const files = await loadSourceFiles(service.dir);
5838
5931
  for (const file of files) {
5839
- const parser = parserForExt(import_node_path17.default.extname(file.path));
5932
+ const parser = parserForExt2(import_node_path17.default.extname(file.path));
5840
5933
  if (!parser) continue;
5841
5934
  const relPath = toPosix(import_node_path17.default.relative(service.dir, file.path));
5842
5935
  const fileDir = import_node_path17.default.dirname(file.path);
@@ -5849,7 +5942,7 @@ async function addSymbolEdges(graph, services) {
5849
5942
  }
5850
5943
  const disambiguated = disambiguate(collectSymbolDefs(root));
5851
5944
  const locals = disambiguated.map(({ def, disambiguator }) => ({
5852
- sid: (0, import_types12.symbolId)(serviceName, relPath, def.qualname, disambiguator),
5945
+ sid: (0, import_types13.symbolId)(serviceName, relPath, def.qualname, disambiguator),
5853
5946
  qualname: def.qualname,
5854
5947
  kind: def.kind,
5855
5948
  startLine: def.startLine,
@@ -5878,10 +5971,10 @@ async function addSymbolEdges(graph, services) {
5878
5971
  if (local) return local.kind === wantKind ? local.sid : null;
5879
5972
  const imp = resolvedImports.get(name);
5880
5973
  if (imp) {
5881
- const candidate = (0, import_types12.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
5974
+ const candidate = (0, import_types13.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
5882
5975
  if (graph.hasNode(candidate)) {
5883
5976
  const node = graph.getNodeAttributes(candidate);
5884
- if (node.type === import_types12.NodeType.SymbolNode && node.kind === wantKind) return candidate;
5977
+ if (node.type === import_types13.NodeType.SymbolNode && node.kind === wantKind) return candidate;
5885
5978
  }
5886
5979
  }
5887
5980
  return null;
@@ -5908,7 +6001,7 @@ async function addSymbolEdges(graph, services) {
5908
6001
  sourceSid: self.sid,
5909
6002
  targetName: ext.name,
5910
6003
  wantKind: "class",
5911
- edgeType: import_types12.EdgeType.INHERITS,
6004
+ edgeType: import_types13.EdgeType.INHERITS,
5912
6005
  line: ext.line
5913
6006
  });
5914
6007
  }
@@ -5917,7 +6010,7 @@ async function addSymbolEdges(graph, services) {
5917
6010
  sourceSid: self.sid,
5918
6011
  targetName: impl.name,
5919
6012
  wantKind: "class",
5920
- edgeType: import_types12.EdgeType.IMPLEMENTS,
6013
+ edgeType: import_types13.EdgeType.IMPLEMENTS,
5921
6014
  line: impl.line
5922
6015
  });
5923
6016
  }
@@ -5933,7 +6026,7 @@ async function addSymbolEdges(graph, services) {
5933
6026
  sourceSid: caller.sid,
5934
6027
  targetName: fn.text,
5935
6028
  wantKind: "function",
5936
- edgeType: import_types12.EdgeType.CALLS,
6029
+ edgeType: import_types13.EdgeType.CALLS,
5937
6030
  line
5938
6031
  });
5939
6032
  }
@@ -5949,15 +6042,15 @@ async function addSymbolEdges(graph, services) {
5949
6042
  const targetSid = resolveTarget(req.targetName, req.wantKind);
5950
6043
  if (!targetSid) continue;
5951
6044
  if (targetSid === req.sourceSid) continue;
5952
- const edgeId = (0, import_types12.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
6045
+ const edgeId = (0, import_types13.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
5953
6046
  if (graph.hasEdge(edgeId)) continue;
5954
6047
  const edge = {
5955
6048
  id: edgeId,
5956
6049
  source: req.sourceSid,
5957
6050
  target: targetSid,
5958
6051
  type: req.edgeType,
5959
- provenance: import_types12.Provenance.EXTRACTED,
5960
- confidence: (0, import_types12.confidenceForExtracted)("structural"),
6052
+ provenance: import_types13.Provenance.EXTRACTED,
6053
+ confidence: (0, import_types13.confidenceForExtracted)("structural"),
5961
6054
  evidence: {
5962
6055
  file: relPath,
5963
6056
  line: req.line,
@@ -5975,7 +6068,7 @@ async function addSymbolEdges(graph, services) {
5975
6068
  // src/extract/databases/index.ts
5976
6069
  init_cjs_shims();
5977
6070
  var import_node_path25 = __toESM(require("path"), 1);
5978
- var import_types13 = require("@neat.is/types");
6071
+ var import_types14 = require("@neat.is/types");
5979
6072
 
5980
6073
  // src/extract/databases/db-config-yaml.ts
5981
6074
  init_cjs_shims();
@@ -6479,8 +6572,8 @@ function compatibleDriversFor(engine) {
6479
6572
  }
6480
6573
  function toDatabaseNode(config) {
6481
6574
  return {
6482
- id: (0, import_types13.databaseId)(config.host),
6483
- type: import_types13.NodeType.DatabaseNode,
6575
+ id: (0, import_types14.databaseId)(config.host),
6576
+ type: import_types14.NodeType.DatabaseNode,
6484
6577
  name: config.database || config.host,
6485
6578
  engine: config.engine,
6486
6579
  engineVersion: config.engineVersion,
@@ -6621,12 +6714,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
6621
6714
  edgesAdded += fe;
6622
6715
  const evidenceFile = toPosix(import_node_path25.default.relative(scanPath, config.sourceFile));
6623
6716
  const edge = {
6624
- id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types13.EdgeType.CONNECTS_TO),
6717
+ id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types14.EdgeType.CONNECTS_TO),
6625
6718
  source: fileNodeId,
6626
6719
  target: dbNode.id,
6627
- type: import_types13.EdgeType.CONNECTS_TO,
6628
- provenance: import_types13.Provenance.EXTRACTED,
6629
- confidence: (0, import_types13.confidenceForExtracted)("structural"),
6720
+ type: import_types14.EdgeType.CONNECTS_TO,
6721
+ provenance: import_types14.Provenance.EXTRACTED,
6722
+ confidence: (0, import_types14.confidenceForExtracted)("structural"),
6630
6723
  evidence: { file: evidenceFile }
6631
6724
  };
6632
6725
  if (!graph.hasEdge(edge.id)) {
@@ -6638,11 +6731,11 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
6638
6731
  const primary = allConfigs[0];
6639
6732
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
6640
6733
  const relPath = import_node_path25.default.relative(scanPath, primary.sourceFile);
6641
- const cfgId = (0, import_types13.configId)(relPath);
6734
+ const cfgId = (0, import_types14.configId)(relPath);
6642
6735
  if (!graph.hasNode(cfgId)) {
6643
6736
  const cfgNode = {
6644
6737
  id: cfgId,
6645
- type: import_types13.NodeType.ConfigNode,
6738
+ type: import_types14.NodeType.ConfigNode,
6646
6739
  name: import_node_path25.default.basename(primary.sourceFile),
6647
6740
  path: relPath,
6648
6741
  fileType: isConfigFile(import_node_path25.default.basename(primary.sourceFile)).fileType || "config"
@@ -6651,12 +6744,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
6651
6744
  nodesAdded++;
6652
6745
  }
6653
6746
  const cfgEdge = {
6654
- id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types13.EdgeType.CONFIGURED_BY),
6747
+ id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types14.EdgeType.CONFIGURED_BY),
6655
6748
  source: service.node.id,
6656
6749
  target: cfgId,
6657
- type: import_types13.EdgeType.CONFIGURED_BY,
6658
- provenance: import_types13.Provenance.EXTRACTED,
6659
- confidence: (0, import_types13.confidenceForExtracted)("structural"),
6750
+ type: import_types14.EdgeType.CONFIGURED_BY,
6751
+ provenance: import_types14.Provenance.EXTRACTED,
6752
+ confidence: (0, import_types14.confidenceForExtracted)("structural"),
6660
6753
  evidence: { file: toPosix(relPath) }
6661
6754
  };
6662
6755
  if (!graph.hasEdge(cfgEdge.id)) {
@@ -6688,7 +6781,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
6688
6781
  init_cjs_shims();
6689
6782
  var import_node_fs16 = require("fs");
6690
6783
  var import_node_path26 = __toESM(require("path"), 1);
6691
- var import_types14 = require("@neat.is/types");
6784
+ var import_types15 = require("@neat.is/types");
6692
6785
  async function walkConfigFiles(dir) {
6693
6786
  const out = [];
6694
6787
  async function walk6(current) {
@@ -6715,8 +6808,8 @@ async function addConfigNodes(graph, services, scanPath) {
6715
6808
  for (const file of configFiles) {
6716
6809
  const relPath = import_node_path26.default.relative(scanPath, file);
6717
6810
  const node = {
6718
- id: (0, import_types14.configId)(relPath),
6719
- type: import_types14.NodeType.ConfigNode,
6811
+ id: (0, import_types15.configId)(relPath),
6812
+ type: import_types15.NodeType.ConfigNode,
6720
6813
  name: import_node_path26.default.basename(file),
6721
6814
  path: relPath,
6722
6815
  fileType: isConfigFile(import_node_path26.default.basename(file)).fileType
@@ -6735,12 +6828,12 @@ async function addConfigNodes(graph, services, scanPath) {
6735
6828
  nodesAdded += fn;
6736
6829
  edgesAdded += fe;
6737
6830
  const edge = {
6738
- id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types14.EdgeType.CONFIGURED_BY),
6831
+ id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types15.EdgeType.CONFIGURED_BY),
6739
6832
  source: fileNodeId,
6740
6833
  target: node.id,
6741
- type: import_types14.EdgeType.CONFIGURED_BY,
6742
- provenance: import_types14.Provenance.EXTRACTED,
6743
- confidence: (0, import_types14.confidenceForExtracted)("structural"),
6834
+ type: import_types15.EdgeType.CONFIGURED_BY,
6835
+ provenance: import_types15.Provenance.EXTRACTED,
6836
+ confidence: (0, import_types15.confidenceForExtracted)("structural"),
6744
6837
  evidence: { file: relPath.split(import_node_path26.default.sep).join("/") }
6745
6838
  };
6746
6839
  if (!graph.hasEdge(edge.id)) {
@@ -6756,7 +6849,7 @@ async function addConfigNodes(graph, services, scanPath) {
6756
6849
  init_cjs_shims();
6757
6850
  var import_node_fs17 = require("fs");
6758
6851
  var import_node_path27 = __toESM(require("path"), 1);
6759
- var import_types15 = require("@neat.is/types");
6852
+ var import_types16 = require("@neat.is/types");
6760
6853
  var PROTO_EXTENSION = ".proto";
6761
6854
  function packageOf(content) {
6762
6855
  const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
@@ -6834,11 +6927,11 @@ async function addGrpcMethods(graph, services) {
6834
6927
  }
6835
6928
  if (methods.length === 0) continue;
6836
6929
  for (const method of methods) {
6837
- const mid = (0, import_types15.grpcMethodId)(method.rpcService, method.rpcMethod);
6930
+ const mid = (0, import_types16.grpcMethodId)(method.rpcService, method.rpcMethod);
6838
6931
  if (!graph.hasNode(mid)) {
6839
6932
  const node = {
6840
6933
  id: mid,
6841
- type: import_types15.NodeType.GrpcMethodNode,
6934
+ type: import_types16.NodeType.GrpcMethodNode,
6842
6935
  name: `${method.rpcService}/${method.rpcMethod}`,
6843
6936
  rpcService: method.rpcService,
6844
6937
  rpcMethod: method.rpcMethod,
@@ -6849,15 +6942,15 @@ async function addGrpcMethods(graph, services) {
6849
6942
  graph.addNode(mid, node);
6850
6943
  nodesAdded++;
6851
6944
  }
6852
- const containsId = (0, import_types15.extractedEdgeId)(service.node.id, mid, import_types15.EdgeType.CONTAINS);
6945
+ const containsId = (0, import_types16.extractedEdgeId)(service.node.id, mid, import_types16.EdgeType.CONTAINS);
6853
6946
  if (!graph.hasEdge(containsId)) {
6854
6947
  const edge = {
6855
6948
  id: containsId,
6856
6949
  source: service.node.id,
6857
6950
  target: mid,
6858
- type: import_types15.EdgeType.CONTAINS,
6859
- provenance: import_types15.Provenance.EXTRACTED,
6860
- confidence: (0, import_types15.confidenceForExtracted)("structural"),
6951
+ type: import_types16.EdgeType.CONTAINS,
6952
+ provenance: import_types16.Provenance.EXTRACTED,
6953
+ confidence: (0, import_types16.confidenceForExtracted)("structural"),
6861
6954
  evidence: {
6862
6955
  file: relFile,
6863
6956
  line: method.line,
@@ -6875,7 +6968,7 @@ async function addGrpcMethods(graph, services) {
6875
6968
 
6876
6969
  // src/extract/calls/index.ts
6877
6970
  init_cjs_shims();
6878
- var import_types27 = require("@neat.is/types");
6971
+ var import_types29 = require("@neat.is/types");
6879
6972
 
6880
6973
  // src/extract/calls/http.ts
6881
6974
  init_cjs_shims();
@@ -6883,7 +6976,7 @@ var import_node_path28 = __toESM(require("path"), 1);
6883
6976
  var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
6884
6977
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
6885
6978
  var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
6886
- var import_types16 = require("@neat.is/types");
6979
+ var import_types17 = require("@neat.is/types");
6887
6980
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
6888
6981
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
6889
6982
  function isInsideJsxExternalLink(node) {
@@ -6969,7 +7062,7 @@ async function addHttpCallEdges(graph, services) {
6969
7062
  const dedupKey = `${relFile}|${targetId}`;
6970
7063
  if (seen.has(dedupKey)) continue;
6971
7064
  seen.add(dedupKey);
6972
- const confidence = (0, import_types16.confidenceForExtracted)("url-literal-service-target");
7065
+ const confidence = (0, import_types17.confidenceForExtracted)("url-literal-service-target");
6973
7066
  const ev = {
6974
7067
  file: relFile,
6975
7068
  line: site.line,
@@ -6983,25 +7076,25 @@ async function addHttpCallEdges(graph, services) {
6983
7076
  );
6984
7077
  nodesAdded += n;
6985
7078
  edgesAdded += e;
6986
- if (!(0, import_types16.passesExtractedFloor)(confidence)) {
7079
+ if (!(0, import_types17.passesExtractedFloor)(confidence)) {
6987
7080
  noteExtractedDropped({
6988
7081
  source: fileNodeId,
6989
7082
  target: targetId,
6990
- type: import_types16.EdgeType.CALLS,
7083
+ type: import_types17.EdgeType.CALLS,
6991
7084
  confidence,
6992
7085
  confidenceKind: "url-literal-service-target",
6993
7086
  evidence: ev
6994
7087
  });
6995
7088
  continue;
6996
7089
  }
6997
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types16.EdgeType.CALLS);
7090
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types17.EdgeType.CALLS);
6998
7091
  if (!graph.hasEdge(edgeId)) {
6999
7092
  const edge = {
7000
7093
  id: edgeId,
7001
7094
  source: fileNodeId,
7002
7095
  target: targetId,
7003
- type: import_types16.EdgeType.CALLS,
7004
- provenance: import_types16.Provenance.EXTRACTED,
7096
+ type: import_types17.EdgeType.CALLS,
7097
+ provenance: import_types17.Provenance.EXTRACTED,
7005
7098
  confidence,
7006
7099
  evidence: ev
7007
7100
  };
@@ -7019,7 +7112,7 @@ init_cjs_shims();
7019
7112
  var import_node_path29 = __toESM(require("path"), 1);
7020
7113
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
7021
7114
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
7022
- var import_types17 = require("@neat.is/types");
7115
+ var import_types18 = require("@neat.is/types");
7023
7116
  var PARSE_CHUNK5 = 16384;
7024
7117
  function parseSource5(parser, source) {
7025
7118
  return parser.parse(
@@ -7184,9 +7277,9 @@ function buildRouteIndex(graph) {
7184
7277
  const index = /* @__PURE__ */ new Map();
7185
7278
  graph.forEachNode((_id, attrs) => {
7186
7279
  const node = attrs;
7187
- if (node.type !== import_types17.NodeType.RouteNode) return;
7280
+ if (node.type !== import_types18.NodeType.RouteNode) return;
7188
7281
  const route = attrs;
7189
- const owner = (0, import_types17.serviceId)(route.service);
7282
+ const owner = (0, import_types18.serviceId)(route.service);
7190
7283
  const entry2 = {
7191
7284
  method: route.method.toUpperCase(),
7192
7285
  normalizedPath: normalizePathTemplate(route.pathTemplate),
@@ -7244,7 +7337,7 @@ async function addRouteCallEdges(graph, services) {
7244
7337
  );
7245
7338
  nodesAdded += n;
7246
7339
  edgesAdded += e;
7247
- const confidence = (0, import_types17.confidenceForExtracted)("verified-call-site");
7340
+ const confidence = (0, import_types18.confidenceForExtracted)("verified-call-site");
7248
7341
  const ev = {
7249
7342
  file: relFile,
7250
7343
  line: site.line,
@@ -7252,25 +7345,25 @@ async function addRouteCallEdges(graph, services) {
7252
7345
  method: site.method ?? match.method,
7253
7346
  pathTemplate: site.pathTemplate
7254
7347
  };
7255
- if (!(0, import_types17.passesExtractedFloor)(confidence)) {
7348
+ if (!(0, import_types18.passesExtractedFloor)(confidence)) {
7256
7349
  noteExtractedDropped({
7257
7350
  source: fileNodeId,
7258
7351
  target: match.routeNodeId,
7259
- type: import_types17.EdgeType.CALLS,
7352
+ type: import_types18.EdgeType.CALLS,
7260
7353
  confidence,
7261
7354
  confidenceKind: "verified-call-site",
7262
7355
  evidence: ev
7263
7356
  });
7264
7357
  continue;
7265
7358
  }
7266
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types17.EdgeType.CALLS);
7359
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types18.EdgeType.CALLS);
7267
7360
  if (!graph.hasEdge(edgeId)) {
7268
7361
  const edge = {
7269
7362
  id: edgeId,
7270
7363
  source: fileNodeId,
7271
7364
  target: match.routeNodeId,
7272
- type: import_types17.EdgeType.CALLS,
7273
- provenance: import_types17.Provenance.EXTRACTED,
7365
+ type: import_types18.EdgeType.CALLS,
7366
+ provenance: import_types18.Provenance.EXTRACTED,
7274
7367
  confidence,
7275
7368
  evidence: ev
7276
7369
  };
@@ -7286,7 +7379,7 @@ async function addRouteCallEdges(graph, services) {
7286
7379
  // src/extract/calls/kafka.ts
7287
7380
  init_cjs_shims();
7288
7381
  var import_node_path30 = __toESM(require("path"), 1);
7289
- var import_types18 = require("@neat.is/types");
7382
+ var import_types19 = require("@neat.is/types");
7290
7383
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
7291
7384
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
7292
7385
  function findAll(re, text) {
@@ -7307,7 +7400,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
7307
7400
  seen.add(key);
7308
7401
  const line = lineOf(file.content, topic);
7309
7402
  out.push({
7310
- infraId: (0, import_types18.infraId)("kafka-topic", topic),
7403
+ infraId: (0, import_types19.infraId)("kafka-topic", topic),
7311
7404
  name: topic,
7312
7405
  kind: "kafka-topic",
7313
7406
  edgeType,
@@ -7330,7 +7423,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
7330
7423
  // src/extract/calls/redis.ts
7331
7424
  init_cjs_shims();
7332
7425
  var import_node_path31 = __toESM(require("path"), 1);
7333
- var import_types19 = require("@neat.is/types");
7426
+ var import_types20 = require("@neat.is/types");
7334
7427
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
7335
7428
  function redisEndpointsFromFile(file, serviceDir) {
7336
7429
  const out = [];
@@ -7343,7 +7436,7 @@ function redisEndpointsFromFile(file, serviceDir) {
7343
7436
  seen.add(host);
7344
7437
  const line = lineOf(file.content, host);
7345
7438
  out.push({
7346
- infraId: (0, import_types19.infraId)("redis", host),
7439
+ infraId: (0, import_types20.infraId)("redis", host),
7347
7440
  name: host,
7348
7441
  kind: "redis",
7349
7442
  edgeType: "CALLS",
@@ -7364,7 +7457,7 @@ function redisEndpointsFromFile(file, serviceDir) {
7364
7457
  // src/extract/calls/aws.ts
7365
7458
  init_cjs_shims();
7366
7459
  var import_node_path32 = __toESM(require("path"), 1);
7367
- var import_types20 = require("@neat.is/types");
7460
+ var import_types21 = require("@neat.is/types");
7368
7461
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
7369
7462
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
7370
7463
  function hasMarker(text, markers) {
@@ -7388,7 +7481,7 @@ function awsEndpointsFromFile(file, serviceDir) {
7388
7481
  seen.add(key);
7389
7482
  const line = lineOf(file.content, name);
7390
7483
  out.push({
7391
- infraId: (0, import_types20.infraId)(kind, name),
7484
+ infraId: (0, import_types21.infraId)(kind, name),
7392
7485
  name,
7393
7486
  kind,
7394
7487
  edgeType: "CALLS",
@@ -7423,7 +7516,7 @@ function awsEndpointsFromFile(file, serviceDir) {
7423
7516
  // src/extract/calls/grpc.ts
7424
7517
  init_cjs_shims();
7425
7518
  var import_node_path33 = __toESM(require("path"), 1);
7426
- var import_types21 = require("@neat.is/types");
7519
+ var import_types22 = require("@neat.is/types");
7427
7520
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
7428
7521
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
7429
7522
  var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
@@ -7472,7 +7565,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
7472
7565
  const { kind } = classified;
7473
7566
  const line = lineOf(file.content, m[0]);
7474
7567
  out.push({
7475
- infraId: (0, import_types21.infraId)(kind, name),
7568
+ infraId: (0, import_types22.infraId)(kind, name),
7476
7569
  name,
7477
7570
  kind,
7478
7571
  edgeType: "CALLS",
@@ -7493,7 +7586,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
7493
7586
  // src/extract/calls/supabase.ts
7494
7587
  init_cjs_shims();
7495
7588
  var import_node_path34 = __toESM(require("path"), 1);
7496
- var import_types22 = require("@neat.is/types");
7589
+ var import_types23 = require("@neat.is/types");
7497
7590
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
7498
7591
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
7499
7592
  var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
@@ -7541,7 +7634,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
7541
7634
  seen.add(name);
7542
7635
  const line = lineOf(file.content, m[0]);
7543
7636
  out.push({
7544
- infraId: (0, import_types22.infraId)("supabase", name),
7637
+ infraId: (0, import_types23.infraId)("supabase", name),
7545
7638
  name,
7546
7639
  kind: "supabase",
7547
7640
  edgeType: "CALLS",
@@ -7572,7 +7665,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
7572
7665
  seen.add(key);
7573
7666
  const line = lineOf(file.content, am[0]);
7574
7667
  out.push({
7575
- infraId: (0, import_types22.infraId)(kind, resource),
7668
+ infraId: (0, import_types23.infraId)(kind, resource),
7576
7669
  name: resource,
7577
7670
  kind,
7578
7671
  edgeType: "CALLS",
@@ -7591,7 +7684,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
7591
7684
  // src/extract/calls/mongoose.ts
7592
7685
  init_cjs_shims();
7593
7686
  var import_node_path35 = __toESM(require("path"), 1);
7594
- var import_types23 = require("@neat.is/types");
7687
+ var import_types24 = require("@neat.is/types");
7595
7688
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
7596
7689
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
7597
7690
  var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
@@ -7736,7 +7829,7 @@ function collectModelDefs(content, pluralizeOn) {
7736
7829
  function endpoint(r, file, serviceDir, matchText) {
7737
7830
  const line = lineOf(file.content, matchText);
7738
7831
  return {
7739
- infraId: (0, import_types23.infraId)(r.kind, r.name),
7832
+ infraId: (0, import_types24.infraId)(r.kind, r.name),
7740
7833
  name: r.name,
7741
7834
  kind: r.kind,
7742
7835
  edgeType: "CALLS",
@@ -7889,7 +7982,7 @@ init_cjs_shims();
7889
7982
  var import_node_path36 = __toESM(require("path"), 1);
7890
7983
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
7891
7984
  var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
7892
- var import_types24 = require("@neat.is/types");
7985
+ var import_types25 = require("@neat.is/types");
7893
7986
  var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
7894
7987
  var PARSE_CHUNK6 = 16384;
7895
7988
  function makePyParser4() {
@@ -7949,20 +8042,57 @@ function walk3(node, visit) {
7949
8042
  visit(node);
7950
8043
  for (const c of namedChildren(node)) walk3(c, visit);
7951
8044
  }
8045
+ var COLUMN_BUILDERS = /* @__PURE__ */ new Set(["Column", "mapped_column"]);
8046
+ function isColumnBuilder(call) {
8047
+ const fn = call.childForFieldName("function");
8048
+ const t = fn?.text;
8049
+ if (!t) return false;
8050
+ const base = t.includes(".") ? t.slice(t.lastIndexOf(".") + 1) : t;
8051
+ return COLUMN_BUILDERS.has(base);
8052
+ }
8053
+ function firstPositionalString(call) {
8054
+ const args = call.childForFieldName("arguments");
8055
+ if (!args) return null;
8056
+ for (const arg of namedChildren(args)) {
8057
+ if (arg.type === "keyword_argument") continue;
8058
+ return arg.type === "string" ? pyStaticStringText2(arg) : null;
8059
+ }
8060
+ return null;
8061
+ }
8062
+ function columnsFromClassBody(body) {
8063
+ const out = [];
8064
+ const seen = /* @__PURE__ */ new Set();
8065
+ for (const stmt of namedChildren(body)) {
8066
+ if (stmt.type !== "expression_statement") continue;
8067
+ const assign = stmt.namedChild(0);
8068
+ if (assign?.type !== "assignment") continue;
8069
+ const left = assign.childForFieldName("left");
8070
+ if (left?.type !== "identifier") continue;
8071
+ const right = assign.childForFieldName("right");
8072
+ if (right?.type !== "call" || !isColumnBuilder(right)) continue;
8073
+ const name = firstPositionalString(right) ?? left.text;
8074
+ if (name && !seen.has(name)) {
8075
+ seen.add(name);
8076
+ out.push(name);
8077
+ }
8078
+ }
8079
+ return out;
8080
+ }
7952
8081
  function sqlalchemyEndpointsFromFile(file, serviceDir) {
7953
8082
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
7954
8083
  const tree = parseSource6(makePyParser4(), file.content);
7955
8084
  const out = [];
7956
8085
  const seen = /* @__PURE__ */ new Set();
7957
- const push = (name, line) => {
8086
+ const push = (name, line, columns) => {
7958
8087
  if (seen.has(name)) return;
7959
8088
  seen.add(name);
7960
8089
  out.push({
7961
- infraId: (0, import_types24.infraId)("sql-table", name),
8090
+ infraId: (0, import_types25.infraId)("sql-table", name),
7962
8091
  name,
7963
8092
  kind: "sql-table",
7964
8093
  edgeType: "CALLS",
7965
8094
  confidenceKind: "verified-call-site",
8095
+ ...columns && columns.length > 0 ? { columns } : {},
7966
8096
  evidence: {
7967
8097
  file: import_node_path36.default.relative(serviceDir, file.path),
7968
8098
  line,
@@ -7978,11 +8108,12 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
7978
8108
  const line = node.startPosition.row + 1;
7979
8109
  const explicit = explicitTablename(body);
7980
8110
  if (explicit === "computed") return;
8111
+ const columns = columnsFromClassBody(body);
7981
8112
  if (explicit) {
7982
- push(explicit.name, line);
8113
+ push(explicit.name, line, columns);
7983
8114
  return;
7984
8115
  }
7985
- if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
8116
+ if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line, columns);
7986
8117
  return;
7987
8118
  }
7988
8119
  if (node.type === "call") {
@@ -8064,7 +8195,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
8064
8195
  if (seen.has(key)) continue;
8065
8196
  seen.add(key);
8066
8197
  out.push({
8067
- infraId: (0, import_types24.infraId)("sql-table", t),
8198
+ infraId: (0, import_types25.infraId)("sql-table", t),
8068
8199
  name: t,
8069
8200
  kind: "sql-table",
8070
8201
  edgeType: "CALLS",
@@ -8085,7 +8216,7 @@ init_cjs_shims();
8085
8216
  var import_node_path37 = __toESM(require("path"), 1);
8086
8217
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
8087
8218
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
8088
- var import_types25 = require("@neat.is/types");
8219
+ var import_types26 = require("@neat.is/types");
8089
8220
  var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
8090
8221
  var PARSE_CHUNK7 = 16384;
8091
8222
  function makePyParser5() {
@@ -8165,7 +8296,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
8165
8296
  seen.add(table);
8166
8297
  const line = node.startPosition.row + 1;
8167
8298
  out.push({
8168
- infraId: (0, import_types25.infraId)("sql-table", table),
8299
+ infraId: (0, import_types26.infraId)("sql-table", table),
8169
8300
  name: table,
8170
8301
  kind: "sql-table",
8171
8302
  edgeType: "CALLS",
@@ -8176,12 +8307,129 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
8176
8307
  return out;
8177
8308
  }
8178
8309
 
8179
- // src/extract/calls/go.ts
8310
+ // src/extract/calls/drizzle.ts
8180
8311
  init_cjs_shims();
8181
- var import_node_path40 = __toESM(require("path"), 1);
8312
+ var import_node_path38 = __toESM(require("path"), 1);
8182
8313
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
8314
+ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
8315
+ var import_types27 = require("@neat.is/types");
8316
+ var DRIZZLE_IMPORT_RE = /drizzle-orm/;
8317
+ var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
8318
+ function parserForExt(ext) {
8319
+ const p = new import_tree_sitter9.default();
8320
+ p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
8321
+ return p;
8322
+ }
8323
+ function namedChildren3(node) {
8324
+ const out = [];
8325
+ for (let i = 0; i < node.namedChildCount; i++) {
8326
+ const c = node.namedChild(i);
8327
+ if (c) out.push(c);
8328
+ }
8329
+ return out;
8330
+ }
8331
+ function stringLiteralText2(node) {
8332
+ if (!node || node.type !== "string") return null;
8333
+ for (const child of namedChildren3(node)) {
8334
+ if (child.type === "string_fragment") return child.text;
8335
+ }
8336
+ return "";
8337
+ }
8338
+ function firstStringArg(call) {
8339
+ const args = call.childForFieldName("arguments");
8340
+ if (!args) return null;
8341
+ for (const arg of namedChildren3(args)) {
8342
+ if (arg.type === "string") return stringLiteralText2(arg);
8343
+ return null;
8344
+ }
8345
+ return null;
8346
+ }
8347
+ function builderColumnName(value) {
8348
+ let node = value;
8349
+ while (node) {
8350
+ if (node.type === "call_expression") {
8351
+ const fn = node.childForFieldName("function");
8352
+ if (fn?.type === "identifier") return firstStringArg(node);
8353
+ if (fn?.type === "member_expression") {
8354
+ node = fn.childForFieldName("object");
8355
+ continue;
8356
+ }
8357
+ return null;
8358
+ }
8359
+ if (node.type === "member_expression") {
8360
+ node = node.childForFieldName("object");
8361
+ continue;
8362
+ }
8363
+ return null;
8364
+ }
8365
+ return null;
8366
+ }
8367
+ function keyName(key) {
8368
+ if (!key) return null;
8369
+ if (key.type === "property_identifier") return key.text;
8370
+ if (key.type === "string") return stringLiteralText2(key);
8371
+ return null;
8372
+ }
8373
+ function columnsFromObject(obj) {
8374
+ const out = [];
8375
+ const seen = /* @__PURE__ */ new Set();
8376
+ for (const child of namedChildren3(obj)) {
8377
+ if (child.type !== "pair") continue;
8378
+ const value = child.childForFieldName("value");
8379
+ const key = keyName(child.childForFieldName("key"));
8380
+ const name = (value ? builderColumnName(value) : null) ?? key;
8381
+ if (name && !seen.has(name)) {
8382
+ seen.add(name);
8383
+ out.push(name);
8384
+ }
8385
+ }
8386
+ return out;
8387
+ }
8388
+ function drizzleEndpointsFromFile(file, serviceDir) {
8389
+ if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
8390
+ const tree = parseSource2(parserForExt(import_node_path38.default.extname(file.path)), file.content);
8391
+ const out = [];
8392
+ const seen = /* @__PURE__ */ new Set();
8393
+ const walk6 = (node) => {
8394
+ if (node.type === "call_expression") {
8395
+ const fn = node.childForFieldName("function");
8396
+ if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
8397
+ const args = node.childForFieldName("arguments");
8398
+ const argNodes = args ? namedChildren3(args) : [];
8399
+ const tableName = stringLiteralText2(argNodes[0] ?? null);
8400
+ const obj = argNodes[1];
8401
+ if (tableName && obj?.type === "object" && !seen.has(tableName)) {
8402
+ seen.add(tableName);
8403
+ const columns = columnsFromObject(obj);
8404
+ const line = node.startPosition.row + 1;
8405
+ out.push({
8406
+ infraId: (0, import_types27.infraId)("sql-table", tableName),
8407
+ name: tableName,
8408
+ kind: "sql-table",
8409
+ edgeType: "CALLS",
8410
+ confidenceKind: "structural",
8411
+ columns,
8412
+ evidence: {
8413
+ file: import_node_path38.default.relative(serviceDir, file.path),
8414
+ line,
8415
+ snippet: snippet(file.content, line)
8416
+ }
8417
+ });
8418
+ }
8419
+ }
8420
+ }
8421
+ for (const c of namedChildren3(node)) walk6(c);
8422
+ };
8423
+ walk6(tree.rootNode);
8424
+ return out;
8425
+ }
8426
+
8427
+ // src/extract/calls/go.ts
8428
+ init_cjs_shims();
8429
+ var import_node_path41 = __toESM(require("path"), 1);
8430
+ var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
8183
8431
  var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
8184
- var import_types26 = require("@neat.is/types");
8432
+ var import_types28 = require("@neat.is/types");
8185
8433
  init_otel();
8186
8434
  var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
8187
8435
  var PARSE_CHUNK8 = 16384;
@@ -8193,8 +8441,8 @@ function walk5(node, visit) {
8193
8441
  }
8194
8442
  }
8195
8443
  function goSqlEndpointsFromFile(file, serviceDir) {
8196
- if (import_node_path40.default.extname(file.path) !== ".go") return [];
8197
- const parser = new import_tree_sitter9.default();
8444
+ if (import_node_path41.default.extname(file.path) !== ".go") return [];
8445
+ const parser = new import_tree_sitter10.default();
8198
8446
  parser.setLanguage(import_tree_sitter_go3.default);
8199
8447
  const tree = parser.parse(
8200
8448
  (index) => index >= file.content.length ? "" : file.content.slice(index, index + PARSE_CHUNK8)
@@ -8213,12 +8461,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
8213
8461
  if (!table) return;
8214
8462
  const line = node.startPosition.row + 1;
8215
8463
  out.push({
8216
- infraId: (0, import_types26.infraId)("sql-table", table),
8464
+ infraId: (0, import_types28.infraId)("sql-table", table),
8217
8465
  name: table,
8218
8466
  kind: "sql-table",
8219
8467
  edgeType: "CALLS",
8220
8468
  confidenceKind: "verified-call-site",
8221
- evidence: { file: toPosix(import_node_path40.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
8469
+ evidence: { file: toPosix(import_node_path41.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
8222
8470
  });
8223
8471
  });
8224
8472
  return out;
@@ -8228,11 +8476,11 @@ function goSqlEndpointsFromFile(file, serviceDir) {
8228
8476
  function edgeTypeFromEndpoint(ep) {
8229
8477
  switch (ep.edgeType) {
8230
8478
  case "PUBLISHES_TO":
8231
- return import_types27.EdgeType.PUBLISHES_TO;
8479
+ return import_types29.EdgeType.PUBLISHES_TO;
8232
8480
  case "CONSUMES_FROM":
8233
- return import_types27.EdgeType.CONSUMES_FROM;
8481
+ return import_types29.EdgeType.CONSUMES_FROM;
8234
8482
  default:
8235
- return import_types27.EdgeType.CALLS;
8483
+ return import_types29.EdgeType.CALLS;
8236
8484
  }
8237
8485
  }
8238
8486
  function isAwsKind(kind) {
@@ -8258,6 +8506,7 @@ async function addExternalEndpointEdges(graph, services) {
8258
8506
  endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
8259
8507
  endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
8260
8508
  endpoints.push(...djangoOrmEndpointsFromFile(maskedFile, service.dir));
8509
+ endpoints.push(...drizzleEndpointsFromFile(maskedFile, service.dir));
8261
8510
  try {
8262
8511
  endpoints.push(...goSqlEndpointsFromFile(maskedFile, service.dir));
8263
8512
  } catch (err) {
@@ -8272,7 +8521,7 @@ async function addExternalEndpointEdges(graph, services) {
8272
8521
  if (!graph.hasNode(ep.infraId)) {
8273
8522
  const node = {
8274
8523
  id: ep.infraId,
8275
- type: import_types27.NodeType.InfraNode,
8524
+ type: import_types29.NodeType.InfraNode,
8276
8525
  name: ep.name,
8277
8526
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
8278
8527
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -8283,8 +8532,22 @@ async function addExternalEndpointEdges(graph, services) {
8283
8532
  graph.addNode(node.id, node);
8284
8533
  nodesAdded++;
8285
8534
  }
8535
+ if (ep.columns && ep.columns.length > 0) {
8536
+ const node = graph.getNodeAttributes(ep.infraId);
8537
+ if (node.type === import_types29.NodeType.InfraNode) {
8538
+ graph.replaceNodeAttributes(ep.infraId, {
8539
+ ...node,
8540
+ columns: foldColumns(
8541
+ node.columns,
8542
+ ep.columns,
8543
+ import_types29.Provenance.EXTRACTED,
8544
+ (0, import_types29.confidenceForExtracted)(ep.confidenceKind)
8545
+ )
8546
+ });
8547
+ }
8548
+ }
8286
8549
  const edgeType = edgeTypeFromEndpoint(ep);
8287
- const confidence = (0, import_types27.confidenceForExtracted)(ep.confidenceKind);
8550
+ const confidence = (0, import_types29.confidenceForExtracted)(ep.confidenceKind);
8288
8551
  const relFile = toPosix(ep.evidence.file);
8289
8552
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
8290
8553
  graph,
@@ -8294,7 +8557,7 @@ async function addExternalEndpointEdges(graph, services) {
8294
8557
  );
8295
8558
  nodesAdded += n;
8296
8559
  edgesAdded += e;
8297
- if (!(0, import_types27.passesExtractedFloor)(confidence)) {
8560
+ if (!(0, import_types29.passesExtractedFloor)(confidence)) {
8298
8561
  noteExtractedDropped({
8299
8562
  source: fileNodeId,
8300
8563
  target: ep.infraId,
@@ -8314,7 +8577,7 @@ async function addExternalEndpointEdges(graph, services) {
8314
8577
  source: fileNodeId,
8315
8578
  target: ep.infraId,
8316
8579
  type: edgeType,
8317
- provenance: import_types27.Provenance.EXTRACTED,
8580
+ provenance: import_types29.Provenance.EXTRACTED,
8318
8581
  confidence,
8319
8582
  evidence: ep.evidence
8320
8583
  };
@@ -8340,16 +8603,16 @@ init_cjs_shims();
8340
8603
 
8341
8604
  // src/extract/infra/docker-compose.ts
8342
8605
  init_cjs_shims();
8343
- var import_node_path41 = __toESM(require("path"), 1);
8344
- var import_types29 = require("@neat.is/types");
8606
+ var import_node_path42 = __toESM(require("path"), 1);
8607
+ var import_types31 = require("@neat.is/types");
8345
8608
 
8346
8609
  // src/extract/infra/shared.ts
8347
8610
  init_cjs_shims();
8348
- var import_types28 = require("@neat.is/types");
8611
+ var import_types30 = require("@neat.is/types");
8349
8612
  function makeInfraNode(kind, name, provider = "self", extras) {
8350
8613
  return {
8351
- id: (0, import_types28.infraId)(kind, name),
8352
- type: import_types28.NodeType.InfraNode,
8614
+ id: (0, import_types30.infraId)(kind, name),
8615
+ type: import_types30.NodeType.InfraNode,
8353
8616
  name,
8354
8617
  provider,
8355
8618
  kind,
@@ -8393,8 +8656,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
8393
8656
  source: anchorId,
8394
8657
  target: node.id,
8395
8658
  type: edgeType,
8396
- provenance: import_types28.Provenance.EXTRACTED,
8397
- confidence: (0, import_types28.confidenceForExtracted)("structural"),
8659
+ provenance: import_types30.Provenance.EXTRACTED,
8660
+ confidence: (0, import_types30.confidenceForExtracted)("structural"),
8398
8661
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
8399
8662
  };
8400
8663
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8411,7 +8674,7 @@ function dependsOnList(value) {
8411
8674
  }
8412
8675
  function serviceNameToServiceNode(name, services) {
8413
8676
  for (const s of services) {
8414
- if (s.node.name === name || import_node_path41.default.basename(s.dir) === name) return s.node.id;
8677
+ if (s.node.name === name || import_node_path42.default.basename(s.dir) === name) return s.node.id;
8415
8678
  }
8416
8679
  return null;
8417
8680
  }
@@ -8420,7 +8683,7 @@ async function addComposeInfra(graph, scanPath, services) {
8420
8683
  let edgesAdded = 0;
8421
8684
  let composePath = null;
8422
8685
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
8423
- const abs = import_node_path41.default.join(scanPath, name);
8686
+ const abs = import_node_path42.default.join(scanPath, name);
8424
8687
  if (await exists(abs)) {
8425
8688
  composePath = abs;
8426
8689
  break;
@@ -8433,13 +8696,13 @@ async function addComposeInfra(graph, scanPath, services) {
8433
8696
  } catch (err) {
8434
8697
  recordExtractionError(
8435
8698
  "infra docker-compose",
8436
- import_node_path41.default.relative(scanPath, composePath),
8699
+ import_node_path42.default.relative(scanPath, composePath),
8437
8700
  err
8438
8701
  );
8439
8702
  return { nodesAdded, edgesAdded };
8440
8703
  }
8441
8704
  if (!compose?.services) return { nodesAdded, edgesAdded };
8442
- const evidenceFile = import_node_path41.default.relative(scanPath, composePath).split(import_node_path41.default.sep).join("/");
8705
+ const evidenceFile = import_node_path42.default.relative(scanPath, composePath).split(import_node_path42.default.sep).join("/");
8443
8706
  const composeNameToNodeId = /* @__PURE__ */ new Map();
8444
8707
  for (const [composeName, svc] of Object.entries(compose.services)) {
8445
8708
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -8461,15 +8724,15 @@ async function addComposeInfra(graph, scanPath, services) {
8461
8724
  for (const dep of dependsOnList(svc.depends_on)) {
8462
8725
  const targetId = composeNameToNodeId.get(dep);
8463
8726
  if (!targetId) continue;
8464
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types29.EdgeType.DEPENDS_ON);
8727
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types31.EdgeType.DEPENDS_ON);
8465
8728
  if (graph.hasEdge(edgeId)) continue;
8466
8729
  const edge = {
8467
8730
  id: edgeId,
8468
8731
  source: sourceId,
8469
8732
  target: targetId,
8470
- type: import_types29.EdgeType.DEPENDS_ON,
8471
- provenance: import_types29.Provenance.EXTRACTED,
8472
- confidence: (0, import_types29.confidenceForExtracted)("structural"),
8733
+ type: import_types31.EdgeType.DEPENDS_ON,
8734
+ provenance: import_types31.Provenance.EXTRACTED,
8735
+ confidence: (0, import_types31.confidenceForExtracted)("structural"),
8473
8736
  evidence: { file: evidenceFile }
8474
8737
  };
8475
8738
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8481,9 +8744,9 @@ async function addComposeInfra(graph, scanPath, services) {
8481
8744
 
8482
8745
  // src/extract/infra/dockerfile.ts
8483
8746
  init_cjs_shims();
8484
- var import_node_path42 = __toESM(require("path"), 1);
8747
+ var import_node_path43 = __toESM(require("path"), 1);
8485
8748
  var import_node_fs18 = require("fs");
8486
- var import_types30 = require("@neat.is/types");
8749
+ var import_types32 = require("@neat.is/types");
8487
8750
  function readDockerfile(content) {
8488
8751
  let image = null;
8489
8752
  const ports = [];
@@ -8512,7 +8775,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
8512
8775
  let nodesAdded = 0;
8513
8776
  let edgesAdded = 0;
8514
8777
  for (const service of services) {
8515
- const dockerfilePath = import_node_path42.default.join(service.dir, "Dockerfile");
8778
+ const dockerfilePath = import_node_path43.default.join(service.dir, "Dockerfile");
8516
8779
  if (!await exists(dockerfilePath)) continue;
8517
8780
  let content;
8518
8781
  try {
@@ -8520,7 +8783,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
8520
8783
  } catch (err) {
8521
8784
  recordExtractionError(
8522
8785
  "infra dockerfile",
8523
- import_node_path42.default.relative(scanPath, dockerfilePath),
8786
+ import_node_path43.default.relative(scanPath, dockerfilePath),
8524
8787
  err
8525
8788
  );
8526
8789
  continue;
@@ -8532,8 +8795,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
8532
8795
  graph.addNode(node.id, node);
8533
8796
  nodesAdded++;
8534
8797
  }
8535
- const relDockerfile = toPosix(import_node_path42.default.relative(service.dir, dockerfilePath));
8536
- const evidenceFile = toPosix(import_node_path42.default.relative(scanPath, dockerfilePath));
8798
+ const relDockerfile = toPosix(import_node_path43.default.relative(service.dir, dockerfilePath));
8799
+ const evidenceFile = toPosix(import_node_path43.default.relative(scanPath, dockerfilePath));
8537
8800
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
8538
8801
  graph,
8539
8802
  service.pkg.name,
@@ -8542,15 +8805,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
8542
8805
  );
8543
8806
  nodesAdded += fn;
8544
8807
  edgesAdded += fe;
8545
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types30.EdgeType.RUNS_ON);
8808
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types32.EdgeType.RUNS_ON);
8546
8809
  if (!graph.hasEdge(edgeId)) {
8547
8810
  const edge = {
8548
8811
  id: edgeId,
8549
8812
  source: fileNodeId,
8550
8813
  target: node.id,
8551
- type: import_types30.EdgeType.RUNS_ON,
8552
- provenance: import_types30.Provenance.EXTRACTED,
8553
- confidence: (0, import_types30.confidenceForExtracted)("structural"),
8814
+ type: import_types32.EdgeType.RUNS_ON,
8815
+ provenance: import_types32.Provenance.EXTRACTED,
8816
+ confidence: (0, import_types32.confidenceForExtracted)("structural"),
8554
8817
  evidence: {
8555
8818
  file: evidenceFile,
8556
8819
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -8565,15 +8828,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
8565
8828
  graph.addNode(portNode.id, portNode);
8566
8829
  nodesAdded++;
8567
8830
  }
8568
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types30.EdgeType.CONNECTS_TO);
8831
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types32.EdgeType.CONNECTS_TO);
8569
8832
  if (graph.hasEdge(portEdgeId)) continue;
8570
8833
  const portEdge = {
8571
8834
  id: portEdgeId,
8572
8835
  source: fileNodeId,
8573
8836
  target: portNode.id,
8574
- type: import_types30.EdgeType.CONNECTS_TO,
8575
- provenance: import_types30.Provenance.EXTRACTED,
8576
- confidence: (0, import_types30.confidenceForExtracted)("structural"),
8837
+ type: import_types32.EdgeType.CONNECTS_TO,
8838
+ provenance: import_types32.Provenance.EXTRACTED,
8839
+ confidence: (0, import_types32.confidenceForExtracted)("structural"),
8577
8840
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
8578
8841
  };
8579
8842
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -8586,8 +8849,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
8586
8849
  // src/extract/infra/terraform.ts
8587
8850
  init_cjs_shims();
8588
8851
  var import_node_fs19 = require("fs");
8589
- var import_node_path43 = __toESM(require("path"), 1);
8590
- var import_types31 = require("@neat.is/types");
8852
+ var import_node_path44 = __toESM(require("path"), 1);
8853
+ var import_types33 = require("@neat.is/types");
8591
8854
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
8592
8855
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
8593
8856
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -8597,11 +8860,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
8597
8860
  for (const entry2 of entries) {
8598
8861
  if (entry2.isDirectory()) {
8599
8862
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
8600
- const child = import_node_path43.default.join(start, entry2.name);
8863
+ const child = import_node_path44.default.join(start, entry2.name);
8601
8864
  if (await isPythonVenvDir(child)) continue;
8602
8865
  out.push(...await walkTfFiles(child, depth + 1, max));
8603
8866
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
8604
- out.push(import_node_path43.default.join(start, entry2.name));
8867
+ out.push(import_node_path44.default.join(start, entry2.name));
8605
8868
  }
8606
8869
  }
8607
8870
  return out;
@@ -8633,7 +8896,7 @@ async function addTerraformResources(graph, scanPath) {
8633
8896
  const files = await walkTfFiles(scanPath);
8634
8897
  for (const file of files) {
8635
8898
  const content = await import_node_fs19.promises.readFile(file, "utf8");
8636
- const evidenceFile = toPosix(import_node_path43.default.relative(scanPath, file));
8899
+ const evidenceFile = toPosix(import_node_path44.default.relative(scanPath, file));
8637
8900
  const resources = [];
8638
8901
  const byKey = /* @__PURE__ */ new Map();
8639
8902
  RESOURCE_RE.lastIndex = 0;
@@ -8668,16 +8931,16 @@ async function addTerraformResources(graph, scanPath) {
8668
8931
  if (!target) continue;
8669
8932
  if (seen.has(target.nodeId)) continue;
8670
8933
  seen.add(target.nodeId);
8671
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types31.EdgeType.DEPENDS_ON);
8934
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types33.EdgeType.DEPENDS_ON);
8672
8935
  if (graph.hasEdge(edgeId)) continue;
8673
8936
  const line = lineAt2(content, resource.bodyOffset + ref.index);
8674
8937
  const edge = {
8675
8938
  id: edgeId,
8676
8939
  source: resource.nodeId,
8677
8940
  target: target.nodeId,
8678
- type: import_types31.EdgeType.DEPENDS_ON,
8679
- provenance: import_types31.Provenance.EXTRACTED,
8680
- confidence: (0, import_types31.confidenceForExtracted)("structural"),
8941
+ type: import_types33.EdgeType.DEPENDS_ON,
8942
+ provenance: import_types33.Provenance.EXTRACTED,
8943
+ confidence: (0, import_types33.confidenceForExtracted)("structural"),
8681
8944
  evidence: { file: evidenceFile, line, snippet: key }
8682
8945
  };
8683
8946
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8691,7 +8954,7 @@ async function addTerraformResources(graph, scanPath) {
8691
8954
  // src/extract/infra/k8s.ts
8692
8955
  init_cjs_shims();
8693
8956
  var import_node_fs20 = require("fs");
8694
- var import_node_path44 = __toESM(require("path"), 1);
8957
+ var import_node_path45 = __toESM(require("path"), 1);
8695
8958
  var import_yaml3 = require("yaml");
8696
8959
  var K8S_KIND_TO_INFRA_KIND = {
8697
8960
  Service: "k8s-service",
@@ -8709,11 +8972,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
8709
8972
  for (const entry2 of entries) {
8710
8973
  if (entry2.isDirectory()) {
8711
8974
  if (IGNORED_DIRS.has(entry2.name)) continue;
8712
- const child = import_node_path44.default.join(start, entry2.name);
8975
+ const child = import_node_path45.default.join(start, entry2.name);
8713
8976
  if (await isPythonVenvDir(child)) continue;
8714
8977
  out.push(...await walkYamlFiles2(child, depth + 1, max));
8715
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path44.default.extname(entry2.name))) {
8716
- out.push(import_node_path44.default.join(start, entry2.name));
8978
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path45.default.extname(entry2.name))) {
8979
+ out.push(import_node_path45.default.join(start, entry2.name));
8717
8980
  }
8718
8981
  }
8719
8982
  return out;
@@ -8747,13 +9010,13 @@ async function addK8sResources(graph, scanPath) {
8747
9010
  // src/extract/infra/cloudflare.ts
8748
9011
  init_cjs_shims();
8749
9012
  var import_node_fs21 = require("fs");
8750
- var import_node_path45 = __toESM(require("path"), 1);
9013
+ var import_node_path46 = __toESM(require("path"), 1);
8751
9014
  var import_smol_toml2 = require("smol-toml");
8752
- var import_types32 = require("@neat.is/types");
9015
+ var import_types34 = require("@neat.is/types");
8753
9016
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
8754
9017
  async function readWranglerConfig(dir) {
8755
9018
  for (const filename of WRANGLER_FILENAMES) {
8756
- const abs = import_node_path45.default.join(dir, filename);
9019
+ const abs = import_node_path46.default.join(dir, filename);
8757
9020
  if (!await exists(abs)) continue;
8758
9021
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
8759
9022
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -8797,8 +9060,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
8797
9060
  source: anchorId,
8798
9061
  target: node.id,
8799
9062
  type: edgeType,
8800
- provenance: import_types32.Provenance.EXTRACTED,
8801
- confidence: (0, import_types32.confidenceForExtracted)("structural"),
9063
+ provenance: import_types34.Provenance.EXTRACTED,
9064
+ confidence: (0, import_types34.confidenceForExtracted)("structural"),
8802
9065
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
8803
9066
  };
8804
9067
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8816,11 +9079,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8816
9079
  try {
8817
9080
  read = await readWranglerConfig(service.dir);
8818
9081
  } catch (err) {
8819
- recordExtractionError("infra cloudflare", import_node_path45.default.relative(scanPath, service.dir), err);
9082
+ recordExtractionError("infra cloudflare", import_node_path46.default.relative(scanPath, service.dir), err);
8820
9083
  continue;
8821
9084
  }
8822
9085
  if (!read || !read.config.name) continue;
8823
- const evidenceFile = toPosix(import_node_path45.default.relative(scanPath, import_node_path45.default.join(service.dir, read.relFile)));
9086
+ const evidenceFile = toPosix(import_node_path46.default.relative(scanPath, import_node_path46.default.join(service.dir, read.relFile)));
8824
9087
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
8825
9088
  }
8826
9089
  for (const worker of discovered) {
@@ -8832,7 +9095,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8832
9095
  }
8833
9096
  let anchorId = service.node.id;
8834
9097
  if (config.main) {
8835
- const entryRelPath = toPosix(import_node_path45.default.normalize(config.main));
9098
+ const entryRelPath = toPosix(import_node_path46.default.normalize(config.main));
8836
9099
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
8837
9100
  graph,
8838
9101
  service.pkg.name,
@@ -8859,15 +9122,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8859
9122
  nodesAdded++;
8860
9123
  }
8861
9124
  if (runtimeNode.id !== anchorId) {
8862
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types32.EdgeType.RUNS_ON);
9125
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types34.EdgeType.RUNS_ON);
8863
9126
  if (!graph.hasEdge(runsOnId)) {
8864
9127
  const edge = {
8865
9128
  id: runsOnId,
8866
9129
  source: anchorId,
8867
9130
  target: runtimeNode.id,
8868
- type: import_types32.EdgeType.RUNS_ON,
8869
- provenance: import_types32.Provenance.EXTRACTED,
8870
- confidence: (0, import_types32.confidenceForExtracted)("structural"),
9131
+ type: import_types34.EdgeType.RUNS_ON,
9132
+ provenance: import_types34.Provenance.EXTRACTED,
9133
+ confidence: (0, import_types34.confidenceForExtracted)("structural"),
8871
9134
  evidence: {
8872
9135
  file: evidenceFile,
8873
9136
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -8881,7 +9144,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8881
9144
  const result = addResourceEdge(
8882
9145
  graph,
8883
9146
  anchorId,
8884
- import_types32.EdgeType.CONNECTS_TO,
9147
+ import_types34.EdgeType.CONNECTS_TO,
8885
9148
  "cloudflare-route",
8886
9149
  route,
8887
9150
  evidenceFile,
@@ -8905,7 +9168,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8905
9168
  const result = addResourceEdge(
8906
9169
  graph,
8907
9170
  anchorId,
8908
- import_types32.EdgeType.DEPENDS_ON,
9171
+ import_types34.EdgeType.DEPENDS_ON,
8909
9172
  group.kind,
8910
9173
  name,
8911
9174
  evidenceFile,
@@ -8919,7 +9182,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8919
9182
  const result = addResourceEdge(
8920
9183
  graph,
8921
9184
  anchorId,
8922
- import_types32.EdgeType.DEPENDS_ON,
9185
+ import_types34.EdgeType.DEPENDS_ON,
8923
9186
  "cloudflare-cron",
8924
9187
  cron,
8925
9188
  evidenceFile,
@@ -8932,7 +9195,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8932
9195
  const result = addResourceEdge(
8933
9196
  graph,
8934
9197
  anchorId,
8935
- import_types32.EdgeType.DEPENDS_ON,
9198
+ import_types34.EdgeType.DEPENDS_ON,
8936
9199
  "cloudflare-env-var",
8937
9200
  varName,
8938
9201
  evidenceFile,
@@ -8945,15 +9208,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8945
9208
  if (!svc.service) continue;
8946
9209
  const target = workerIndex.get(svc.service);
8947
9210
  if (target && target.anchorId !== anchorId) {
8948
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types32.EdgeType.CALLS);
9211
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types34.EdgeType.CALLS);
8949
9212
  if (!graph.hasEdge(edgeId)) {
8950
9213
  const edge = {
8951
9214
  id: edgeId,
8952
9215
  source: anchorId,
8953
9216
  target: target.anchorId,
8954
- type: import_types32.EdgeType.CALLS,
8955
- provenance: import_types32.Provenance.EXTRACTED,
8956
- confidence: (0, import_types32.confidenceForExtracted)("structural"),
9217
+ type: import_types34.EdgeType.CALLS,
9218
+ provenance: import_types34.Provenance.EXTRACTED,
9219
+ confidence: (0, import_types34.confidenceForExtracted)("structural"),
8957
9220
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
8958
9221
  };
8959
9222
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -8964,7 +9227,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8964
9227
  const result = addResourceEdge(
8965
9228
  graph,
8966
9229
  anchorId,
8967
- import_types32.EdgeType.DEPENDS_ON,
9230
+ import_types34.EdgeType.DEPENDS_ON,
8968
9231
  "cloudflare-service-binding",
8969
9232
  svc.service,
8970
9233
  evidenceFile,
@@ -8980,12 +9243,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
8980
9243
  // src/extract/infra/vercel.ts
8981
9244
  init_cjs_shims();
8982
9245
  var import_node_fs22 = require("fs");
8983
- var import_node_path46 = __toESM(require("path"), 1);
8984
- var import_types33 = require("@neat.is/types");
9246
+ var import_node_path47 = __toESM(require("path"), 1);
9247
+ var import_types35 = require("@neat.is/types");
8985
9248
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
8986
9249
  async function readVercelConfig(dir) {
8987
9250
  for (const filename of VERCEL_CONFIG_FILENAMES) {
8988
- const abs = import_node_path46.default.join(dir, filename);
9251
+ const abs = import_node_path47.default.join(dir, filename);
8989
9252
  if (!await exists(abs)) continue;
8990
9253
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
8991
9254
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -8994,7 +9257,7 @@ async function readVercelConfig(dir) {
8994
9257
  return null;
8995
9258
  }
8996
9259
  async function readLinkedProjectName(dir) {
8997
- const abs = import_node_path46.default.join(dir, ".vercel", "project.json");
9260
+ const abs = import_node_path47.default.join(dir, ".vercel", "project.json");
8998
9261
  if (!await exists(abs)) return void 0;
8999
9262
  const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
9000
9263
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -9012,7 +9275,7 @@ async function addVercelServices(graph, services, scanPath) {
9012
9275
  read = await readVercelConfig(service.dir);
9013
9276
  projectName = await readLinkedProjectName(service.dir);
9014
9277
  } catch (err) {
9015
- recordExtractionError("infra vercel", import_node_path46.default.relative(scanPath, service.dir), err);
9278
+ recordExtractionError("infra vercel", import_node_path47.default.relative(scanPath, service.dir), err);
9016
9279
  continue;
9017
9280
  }
9018
9281
  if (!read && !projectName) continue;
@@ -9028,7 +9291,7 @@ async function addVercelServices(graph, services, scanPath) {
9028
9291
  const anchorId = service.node.id;
9029
9292
  if (!read) continue;
9030
9293
  const { config, relFile, raw } = read;
9031
- const evidenceFile = toPosix(import_node_path46.default.relative(scanPath, import_node_path46.default.join(service.dir, relFile)));
9294
+ const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, relFile)));
9032
9295
  const add = (edgeType, kind, name) => {
9033
9296
  if (!name) return;
9034
9297
  const result = emitPlatformResourceEdge(
@@ -9044,12 +9307,12 @@ async function addVercelServices(graph, services, scanPath) {
9044
9307
  nodesAdded += result.nodesAdded;
9045
9308
  edgesAdded += result.edgesAdded;
9046
9309
  };
9047
- add(import_types33.EdgeType.RUNS_ON, "vercel", "vercel");
9048
- for (const cron of config.crons ?? []) add(import_types33.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
9049
- for (const varName of Object.keys(config.env ?? {})) add(import_types33.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9050
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types33.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9310
+ add(import_types35.EdgeType.RUNS_ON, "vercel", "vercel");
9311
+ for (const cron of config.crons ?? []) add(import_types35.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
9312
+ for (const varName of Object.keys(config.env ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9313
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9051
9314
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
9052
- add(import_types33.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
9315
+ add(import_types35.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
9053
9316
  }
9054
9317
  }
9055
9318
  return { nodesAdded, edgesAdded };
@@ -9058,13 +9321,13 @@ async function addVercelServices(graph, services, scanPath) {
9058
9321
  // src/extract/infra/railway.ts
9059
9322
  init_cjs_shims();
9060
9323
  var import_node_fs23 = require("fs");
9061
- var import_node_path47 = __toESM(require("path"), 1);
9324
+ var import_node_path48 = __toESM(require("path"), 1);
9062
9325
  var import_smol_toml3 = require("smol-toml");
9063
- var import_types34 = require("@neat.is/types");
9326
+ var import_types36 = require("@neat.is/types");
9064
9327
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
9065
9328
  async function readRailwayConfig(dir) {
9066
9329
  for (const filename of RAILWAY_FILENAMES) {
9067
- const abs = import_node_path47.default.join(dir, filename);
9330
+ const abs = import_node_path48.default.join(dir, filename);
9068
9331
  if (!await exists(abs)) continue;
9069
9332
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
9070
9333
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -9080,7 +9343,7 @@ async function addRailwayServices(graph, services, scanPath) {
9080
9343
  try {
9081
9344
  read = await readRailwayConfig(service.dir);
9082
9345
  } catch (err) {
9083
- recordExtractionError("infra railway", import_node_path47.default.relative(scanPath, service.dir), err);
9346
+ recordExtractionError("infra railway", import_node_path48.default.relative(scanPath, service.dir), err);
9084
9347
  continue;
9085
9348
  }
9086
9349
  if (!read) continue;
@@ -9090,7 +9353,7 @@ async function addRailwayServices(graph, services, scanPath) {
9090
9353
  }
9091
9354
  const anchorId = service.node.id;
9092
9355
  const { config, relFile, raw } = read;
9093
- const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, relFile)));
9356
+ const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
9094
9357
  const add = (edgeType, kind, name) => {
9095
9358
  if (!name) return;
9096
9359
  const result = emitPlatformResourceEdge(
@@ -9106,9 +9369,9 @@ async function addRailwayServices(graph, services, scanPath) {
9106
9369
  nodesAdded += result.nodesAdded;
9107
9370
  edgesAdded += result.edgesAdded;
9108
9371
  };
9109
- add(import_types34.EdgeType.RUNS_ON, "railway", "railway");
9110
- add(import_types34.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
9111
- add(import_types34.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
9372
+ add(import_types36.EdgeType.RUNS_ON, "railway", "railway");
9373
+ add(import_types36.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
9374
+ add(import_types36.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
9112
9375
  }
9113
9376
  return { nodesAdded, edgesAdded };
9114
9377
  }
@@ -9116,12 +9379,12 @@ async function addRailwayServices(graph, services, scanPath) {
9116
9379
  // src/extract/infra/supabase.ts
9117
9380
  init_cjs_shims();
9118
9381
  var import_node_fs24 = require("fs");
9119
- var import_node_path48 = __toESM(require("path"), 1);
9382
+ var import_node_path49 = __toESM(require("path"), 1);
9120
9383
  var import_smol_toml4 = require("smol-toml");
9121
- var import_types35 = require("@neat.is/types");
9384
+ var import_types37 = require("@neat.is/types");
9122
9385
  async function readSupabaseConfig(dir) {
9123
- const relFile = import_node_path48.default.join("supabase", "config.toml");
9124
- const abs = import_node_path48.default.join(dir, relFile);
9386
+ const relFile = import_node_path49.default.join("supabase", "config.toml");
9387
+ const abs = import_node_path49.default.join(dir, relFile);
9125
9388
  if (!await exists(abs)) return null;
9126
9389
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
9127
9390
  const config = (0, import_smol_toml4.parse)(raw);
@@ -9135,7 +9398,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
9135
9398
  try {
9136
9399
  read = await readSupabaseConfig(service.dir);
9137
9400
  } catch (err) {
9138
- recordExtractionError("infra supabase", import_node_path48.default.relative(scanPath, service.dir), err);
9401
+ recordExtractionError("infra supabase", import_node_path49.default.relative(scanPath, service.dir), err);
9139
9402
  continue;
9140
9403
  }
9141
9404
  if (!read) continue;
@@ -9150,7 +9413,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
9150
9413
  });
9151
9414
  }
9152
9415
  const anchorId = service.node.id;
9153
- const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
9416
+ const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, import_node_path49.default.join(service.dir, relFile)));
9154
9417
  const add = (edgeType, kind, name) => {
9155
9418
  if (!name) return;
9156
9419
  const result = emitPlatformResourceEdge(
@@ -9166,10 +9429,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
9166
9429
  nodesAdded += result.nodesAdded;
9167
9430
  edgesAdded += result.edgesAdded;
9168
9431
  };
9169
- add(import_types35.EdgeType.RUNS_ON, "supabase", "supabase");
9170
- for (const fn of Object.keys(config.functions ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "supabase-function", fn);
9171
- if (config.storage) add(import_types35.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
9172
- if (config.auth) add(import_types35.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
9432
+ add(import_types37.EdgeType.RUNS_ON, "supabase", "supabase");
9433
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types37.EdgeType.DEPENDS_ON, "supabase-function", fn);
9434
+ if (config.storage) add(import_types37.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
9435
+ if (config.auth) add(import_types37.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
9173
9436
  }
9174
9437
  return { nodesAdded, edgesAdded };
9175
9438
  }
@@ -9191,17 +9454,17 @@ async function addInfra(graph, scanPath, services) {
9191
9454
  }
9192
9455
 
9193
9456
  // src/extract/index.ts
9194
- var import_node_path50 = __toESM(require("path"), 1);
9457
+ var import_node_path51 = __toESM(require("path"), 1);
9195
9458
 
9196
9459
  // src/extract/retire.ts
9197
9460
  init_cjs_shims();
9198
9461
  var import_node_fs25 = require("fs");
9199
- var import_node_path49 = __toESM(require("path"), 1);
9200
- var import_types36 = require("@neat.is/types");
9462
+ var import_node_path50 = __toESM(require("path"), 1);
9463
+ var import_types38 = require("@neat.is/types");
9201
9464
  function dropOrphanedFileNodes(graph) {
9202
9465
  const orphans = [];
9203
9466
  graph.forEachNode((id, attrs) => {
9204
- if (attrs.type !== import_types36.NodeType.FileNode) return;
9467
+ if (attrs.type !== import_types38.NodeType.FileNode) return;
9205
9468
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
9206
9469
  orphans.push(id);
9207
9470
  }
@@ -9214,7 +9477,7 @@ function retireEdgesByFile(graph, file) {
9214
9477
  const toDrop = [];
9215
9478
  graph.forEachEdge((id, attrs) => {
9216
9479
  const edge = attrs;
9217
- if (edge.provenance !== import_types36.Provenance.EXTRACTED) return;
9480
+ if (edge.provenance !== import_types38.Provenance.EXTRACTED) return;
9218
9481
  if (!edge.evidence?.file) return;
9219
9482
  if (edge.evidence.file === normalized) toDrop.push(id);
9220
9483
  });
@@ -9227,14 +9490,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
9227
9490
  const bases = [scanPath, ...serviceDirs];
9228
9491
  graph.forEachEdge((id, attrs) => {
9229
9492
  const edge = attrs;
9230
- if (edge.provenance !== import_types36.Provenance.EXTRACTED) return;
9493
+ if (edge.provenance !== import_types38.Provenance.EXTRACTED) return;
9231
9494
  const evidenceFile = edge.evidence?.file;
9232
9495
  if (!evidenceFile) return;
9233
- if (import_node_path49.default.isAbsolute(evidenceFile)) {
9496
+ if (import_node_path50.default.isAbsolute(evidenceFile)) {
9234
9497
  if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
9235
9498
  return;
9236
9499
  }
9237
- const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path49.default.join(base, evidenceFile)));
9500
+ const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path50.default.join(base, evidenceFile)));
9238
9501
  if (!found) toDrop.push(id);
9239
9502
  });
9240
9503
  for (const id of toDrop) graph.dropEdge(id);
@@ -9287,7 +9550,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
9287
9550
  }
9288
9551
  const droppedEntries = drainDroppedExtracted();
9289
9552
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
9290
- const rejectedPath = import_node_path50.default.join(import_node_path50.default.dirname(opts.errorsPath), "rejected.ndjson");
9553
+ const rejectedPath = import_node_path51.default.join(import_node_path51.default.dirname(opts.errorsPath), "rejected.ndjson");
9291
9554
  try {
9292
9555
  await writeRejectedExtracted(droppedEntries, rejectedPath);
9293
9556
  } catch (err) {
@@ -9321,39 +9584,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
9321
9584
 
9322
9585
  // src/divergences.ts
9323
9586
  init_cjs_shims();
9324
- var import_types37 = require("@neat.is/types");
9587
+ var import_types39 = require("@neat.is/types");
9325
9588
  function bucketKey(source, target, type) {
9326
9589
  return `${type}|${source}|${target}`;
9327
9590
  }
9328
9591
  function bucketSourceFor(graph, edge) {
9329
- if (edge.type !== import_types37.EdgeType.CONNECTS_TO) return edge.source;
9330
- const parsed = (0, import_types37.parseFileId)(edge.source);
9592
+ if (edge.type !== import_types39.EdgeType.CONNECTS_TO) return edge.source;
9593
+ const parsed = (0, import_types39.parseFileId)(edge.source);
9331
9594
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
9332
9595
  const target = graph.getNodeAttributes(edge.target);
9333
- if (target.type !== import_types37.NodeType.DatabaseNode) return edge.source;
9334
- return (0, import_types37.serviceId)(parsed.service);
9596
+ if (target.type !== import_types39.NodeType.DatabaseNode) return edge.source;
9597
+ return (0, import_types39.serviceId)(parsed.service);
9335
9598
  }
9336
9599
  function bucketEdges(graph) {
9337
9600
  const buckets2 = /* @__PURE__ */ new Map();
9338
9601
  graph.forEachEdge((id, attrs) => {
9339
9602
  const e = attrs;
9340
- const parsed = (0, import_types37.parseEdgeId)(id);
9603
+ const parsed = (0, import_types39.parseEdgeId)(id);
9341
9604
  const provenance = parsed?.provenance ?? e.provenance;
9342
9605
  const source = bucketSourceFor(graph, e);
9343
9606
  const key = bucketKey(source, e.target, e.type);
9344
9607
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
9345
9608
  switch (provenance) {
9346
- case import_types37.Provenance.EXTRACTED:
9609
+ case import_types39.Provenance.EXTRACTED:
9347
9610
  cur.extracted = e;
9348
9611
  break;
9349
- case import_types37.Provenance.OBSERVED:
9612
+ case import_types39.Provenance.OBSERVED:
9350
9613
  cur.observed = e;
9351
9614
  break;
9352
- case import_types37.Provenance.INFERRED:
9615
+ case import_types39.Provenance.INFERRED:
9353
9616
  cur.inferred = e;
9354
9617
  break;
9355
9618
  default:
9356
- if (e.provenance === import_types37.Provenance.STALE) cur.stale = e;
9619
+ if (e.provenance === import_types39.Provenance.STALE) cur.stale = e;
9357
9620
  }
9358
9621
  buckets2.set(key, cur);
9359
9622
  });
@@ -9362,17 +9625,17 @@ function bucketEdges(graph) {
9362
9625
  function nodeIsFrontier(graph, nodeId) {
9363
9626
  if (!graph.hasNode(nodeId)) return false;
9364
9627
  const attrs = graph.getNodeAttributes(nodeId);
9365
- return attrs.type === import_types37.NodeType.FrontierNode;
9628
+ return attrs.type === import_types39.NodeType.FrontierNode;
9366
9629
  }
9367
9630
  function nodeIsWebsocketChannel(graph, nodeId) {
9368
9631
  if (!graph.hasNode(nodeId)) return false;
9369
9632
  const attrs = graph.getNodeAttributes(nodeId);
9370
- return attrs.type === import_types37.NodeType.WebSocketChannelNode;
9633
+ return attrs.type === import_types39.NodeType.WebSocketChannelNode;
9371
9634
  }
9372
9635
  function nodeIsSymbol(graph, nodeId) {
9373
9636
  if (!graph.hasNode(nodeId)) return false;
9374
9637
  const attrs = graph.getNodeAttributes(nodeId);
9375
- return attrs.type === import_types37.NodeType.SymbolNode;
9638
+ return attrs.type === import_types39.NodeType.SymbolNode;
9376
9639
  }
9377
9640
  function clampConfidence(n) {
9378
9641
  if (!Number.isFinite(n)) return 0;
@@ -9392,14 +9655,14 @@ function gradedConfidence(edge) {
9392
9655
  return clampConfidence(confidenceForEdge(edge));
9393
9656
  }
9394
9657
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
9395
- import_types37.EdgeType.CALLS,
9396
- import_types37.EdgeType.CONNECTS_TO,
9397
- import_types37.EdgeType.PUBLISHES_TO,
9398
- import_types37.EdgeType.CONSUMES_FROM
9658
+ import_types39.EdgeType.CALLS,
9659
+ import_types39.EdgeType.CONNECTS_TO,
9660
+ import_types39.EdgeType.PUBLISHES_TO,
9661
+ import_types39.EdgeType.CONSUMES_FROM
9399
9662
  ]);
9400
9663
  function detectMissingDivergences(graph, bucket) {
9401
9664
  const out = [];
9402
- if (bucket.type === import_types37.EdgeType.CONTAINS) return out;
9665
+ if (bucket.type === import_types39.EdgeType.CONTAINS) return out;
9403
9666
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
9404
9667
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
9405
9668
  if (!nodeIsFrontier(graph, bucket.target)) {
@@ -9441,7 +9704,7 @@ function declaredHostFor(svc) {
9441
9704
  function hasExtractedConfiguredBy(graph, svcId) {
9442
9705
  for (const edgeId of graph.outboundEdges(svcId)) {
9443
9706
  const e = graph.getEdgeAttributes(edgeId);
9444
- if (e.type === import_types37.EdgeType.CONFIGURED_BY && e.provenance === import_types37.Provenance.EXTRACTED) {
9707
+ if (e.type === import_types39.EdgeType.CONFIGURED_BY && e.provenance === import_types39.Provenance.EXTRACTED) {
9445
9708
  return true;
9446
9709
  }
9447
9710
  }
@@ -9454,10 +9717,10 @@ function detectHostMismatch(graph, svcId, svc) {
9454
9717
  const out = [];
9455
9718
  for (const edgeId of graph.outboundEdges(svcId)) {
9456
9719
  const edge = graph.getEdgeAttributes(edgeId);
9457
- if (edge.type !== import_types37.EdgeType.CONNECTS_TO) continue;
9458
- if (edge.provenance !== import_types37.Provenance.OBSERVED) continue;
9720
+ if (edge.type !== import_types39.EdgeType.CONNECTS_TO) continue;
9721
+ if (edge.provenance !== import_types39.Provenance.OBSERVED) continue;
9459
9722
  const target = graph.getNodeAttributes(edge.target);
9460
- if (target.type !== import_types37.NodeType.DatabaseNode) continue;
9723
+ if (target.type !== import_types39.NodeType.DatabaseNode) continue;
9461
9724
  const observedHost = target.host?.trim();
9462
9725
  if (!observedHost) continue;
9463
9726
  if (observedHost === declaredHost) continue;
@@ -9479,10 +9742,10 @@ function detectCompatDivergences(graph, svcId, svc) {
9479
9742
  const deps = svc.dependencies ?? {};
9480
9743
  for (const edgeId of graph.outboundEdges(svcId)) {
9481
9744
  const edge = graph.getEdgeAttributes(edgeId);
9482
- if (edge.type !== import_types37.EdgeType.CONNECTS_TO) continue;
9483
- if (edge.provenance !== import_types37.Provenance.OBSERVED) continue;
9745
+ if (edge.type !== import_types39.EdgeType.CONNECTS_TO) continue;
9746
+ if (edge.provenance !== import_types39.Provenance.OBSERVED) continue;
9484
9747
  const target = graph.getNodeAttributes(edge.target);
9485
- if (target.type !== import_types37.NodeType.DatabaseNode) continue;
9748
+ if (target.type !== import_types39.NodeType.DatabaseNode) continue;
9486
9749
  for (const pair of compatPairs()) {
9487
9750
  if (pair.engine !== target.engine) continue;
9488
9751
  const declared = deps[pair.driver];
@@ -9532,6 +9795,44 @@ function detectCompatDivergences(graph, svcId, svc) {
9532
9795
  }
9533
9796
  return out;
9534
9797
  }
9798
+ 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.";
9799
+ 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.";
9800
+ function detectColumnDrift(node) {
9801
+ const columns = node.columns;
9802
+ if (!columns || columns.length === 0) return [];
9803
+ const anyDeclared = columns.some(columnIsDeclared);
9804
+ const anyObserved = columns.some(columnIsObserved);
9805
+ if (!anyDeclared || !anyObserved) return [];
9806
+ const out = [];
9807
+ for (const col of columns) {
9808
+ const declared = columnIsDeclared(col);
9809
+ const observed = columnIsObserved(col);
9810
+ if (declared && !observed) {
9811
+ out.push({
9812
+ type: "missing-observed",
9813
+ source: node.id,
9814
+ target: node.id,
9815
+ table: node.id,
9816
+ column: col.name,
9817
+ confidence: clampConfidence(col.confidence),
9818
+ reason: `Schema declares column ${node.name}.${col.name} but no production statement has touched it.`,
9819
+ recommendation: RECOMMENDATION_COLUMN_MISSING_OBSERVED
9820
+ });
9821
+ } else if (observed && !declared) {
9822
+ out.push({
9823
+ type: "missing-extracted",
9824
+ source: node.id,
9825
+ target: node.id,
9826
+ table: node.id,
9827
+ column: col.name,
9828
+ confidence: clampConfidence(col.confidence),
9829
+ reason: `Production touched column ${node.name}.${col.name} but the schema does not declare it.`,
9830
+ recommendation: RECOMMENDATION_COLUMN_MISSING_EXTRACTED
9831
+ });
9832
+ }
9833
+ }
9834
+ return out;
9835
+ }
9535
9836
  function involvesNode(d, nodeId) {
9536
9837
  return d.source === nodeId || d.target === nodeId;
9537
9838
  }
@@ -9541,7 +9842,7 @@ function suppressHostMismatchHalves(all) {
9541
9842
  for (const d of all) {
9542
9843
  if (d.type !== "host-mismatch") continue;
9543
9844
  observedHalf.add(`${d.source}->${d.target}`);
9544
- declaredHalf.add((0, import_types37.databaseId)(d.extractedHost));
9845
+ declaredHalf.add((0, import_types39.databaseId)(d.extractedHost));
9545
9846
  }
9546
9847
  if (observedHalf.size === 0) return all;
9547
9848
  return all.filter((d) => {
@@ -9560,10 +9861,15 @@ function computeDivergences(graph, opts = {}) {
9560
9861
  }
9561
9862
  graph.forEachNode((nodeId, attrs) => {
9562
9863
  const n = attrs;
9563
- if (n.type !== import_types37.NodeType.ServiceNode) return;
9564
- const svc = n;
9565
- for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
9566
- for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
9864
+ if (n.type === import_types39.NodeType.ServiceNode) {
9865
+ const svc = n;
9866
+ for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
9867
+ for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
9868
+ return;
9869
+ }
9870
+ if (n.type === import_types39.NodeType.InfraNode && n.kind === "sql-table") {
9871
+ for (const d of detectColumnDrift(n)) all.push(d);
9872
+ }
9567
9873
  });
9568
9874
  const reconciled = suppressHostMismatchHalves(all);
9569
9875
  let filtered = reconciled;
@@ -9592,9 +9898,12 @@ function computeDivergences(graph, opts = {}) {
9592
9898
  if (lead !== 0) return lead;
9593
9899
  if (a.type !== b.type) return a.type.localeCompare(b.type);
9594
9900
  if (a.source !== b.source) return a.source.localeCompare(b.source);
9595
- return a.target.localeCompare(b.target);
9901
+ if (a.target !== b.target) return a.target.localeCompare(b.target);
9902
+ const ac = "column" in a && a.column ? a.column : "";
9903
+ const bc = "column" in b && b.column ? b.column : "";
9904
+ return ac.localeCompare(bc);
9596
9905
  });
9597
- return import_types37.DivergenceResultSchema.parse({
9906
+ return import_types39.DivergenceResultSchema.parse({
9598
9907
  divergences: filtered,
9599
9908
  totalAffected: filtered.length,
9600
9909
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -9604,9 +9913,9 @@ function computeDivergences(graph, opts = {}) {
9604
9913
  // src/persist.ts
9605
9914
  init_cjs_shims();
9606
9915
  var import_node_fs26 = require("fs");
9607
- var import_node_path51 = __toESM(require("path"), 1);
9608
- var import_types38 = require("@neat.is/types");
9609
- var SCHEMA_VERSION = 5;
9916
+ var import_node_path52 = __toESM(require("path"), 1);
9917
+ var import_types40 = require("@neat.is/types");
9918
+ var SCHEMA_VERSION = 6;
9610
9919
  function migrateV1ToV2(payload) {
9611
9920
  const nodes = payload.graph.nodes;
9612
9921
  if (Array.isArray(nodes)) {
@@ -9624,18 +9933,30 @@ function migrateV3ToV4(payload) {
9624
9933
  function migrateV4ToV5(payload) {
9625
9934
  return { ...payload, schemaVersion: 5 };
9626
9935
  }
9936
+ function migrateV5ToV6(payload) {
9937
+ const nodes = payload.graph.nodes;
9938
+ if (Array.isArray(nodes)) {
9939
+ for (const node of nodes) {
9940
+ const attrs = node.attributes;
9941
+ if (!attrs || attrs.type !== import_types40.NodeType.InfraNode) continue;
9942
+ if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
9943
+ if (!Array.isArray(attrs.columns)) attrs.columns = [];
9944
+ }
9945
+ }
9946
+ return { ...payload, schemaVersion: 6 };
9947
+ }
9627
9948
  function migrateV2ToV3(payload) {
9628
9949
  const edges = payload.graph.edges;
9629
9950
  if (Array.isArray(edges)) {
9630
9951
  for (const edge of edges) {
9631
9952
  const attrs = edge.attributes;
9632
9953
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
9633
- attrs.provenance = import_types38.Provenance.OBSERVED;
9954
+ attrs.provenance = import_types40.Provenance.OBSERVED;
9634
9955
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
9635
9956
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
9636
9957
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
9637
9958
  if (type && source && target) {
9638
- const newId = (0, import_types38.observedEdgeId)(source, target, type);
9959
+ const newId = (0, import_types40.observedEdgeId)(source, target, type);
9639
9960
  attrs.id = newId;
9640
9961
  if (edge.key) edge.key = newId;
9641
9962
  }
@@ -9644,7 +9965,7 @@ function migrateV2ToV3(payload) {
9644
9965
  return { ...payload, schemaVersion: 3 };
9645
9966
  }
9646
9967
  async function ensureDir(filePath) {
9647
- await import_node_fs26.promises.mkdir(import_node_path51.default.dirname(filePath), { recursive: true });
9968
+ await import_node_fs26.promises.mkdir(import_node_path52.default.dirname(filePath), { recursive: true });
9648
9969
  }
9649
9970
  async function saveGraphToDisk(graph, outPath) {
9650
9971
  await ensureDir(outPath);
@@ -9678,6 +9999,9 @@ async function loadGraphFromDisk(graph, outPath) {
9678
9999
  if (payload.schemaVersion === 4) {
9679
10000
  payload = migrateV4ToV5(payload);
9680
10001
  }
10002
+ if (payload.schemaVersion === 5) {
10003
+ payload = migrateV5ToV6(payload);
10004
+ }
9681
10005
  if (payload.schemaVersion !== SCHEMA_VERSION) {
9682
10006
  throw new Error(
9683
10007
  `persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
@@ -9729,7 +10053,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
9729
10053
  // src/gitignore.ts
9730
10054
  init_cjs_shims();
9731
10055
  var import_node_fs27 = require("fs");
9732
- var import_node_path52 = __toESM(require("path"), 1);
10056
+ var import_node_path53 = __toESM(require("path"), 1);
9733
10057
  var NEAT_OUT_LINE = "neat-out/";
9734
10058
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
9735
10059
  function isNeatOutLine(line) {
@@ -9737,7 +10061,7 @@ function isNeatOutLine(line) {
9737
10061
  return trimmed === "neat-out/" || trimmed === "neat-out";
9738
10062
  }
9739
10063
  async function ensureNeatOutIgnored(projectDir) {
9740
- const file = import_node_path52.default.join(projectDir, ".gitignore");
10064
+ const file = import_node_path53.default.join(projectDir, ".gitignore");
9741
10065
  let existing = null;
9742
10066
  try {
9743
10067
  existing = await import_node_fs27.promises.readFile(file, "utf8");
@@ -9764,7 +10088,7 @@ ${NEAT_OUT_LINE}
9764
10088
 
9765
10089
  // src/summary.ts
9766
10090
  init_cjs_shims();
9767
- var import_types39 = require("@neat.is/types");
10091
+ var import_types41 = require("@neat.is/types");
9768
10092
  function renderOtelEnvBlock() {
9769
10093
  return [
9770
10094
  "for prod OTel routing, set these in your deploy platform's env:",
@@ -9774,19 +10098,19 @@ function renderOtelEnvBlock() {
9774
10098
  }
9775
10099
  function findIncompatServices(nodes) {
9776
10100
  return nodes.filter(
9777
- (n) => n.type === import_types39.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
10101
+ (n) => n.type === import_types41.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
9778
10102
  );
9779
10103
  }
9780
10104
  function servicesWithoutObserved(nodes, edges) {
9781
10105
  const seen = /* @__PURE__ */ new Set();
9782
10106
  for (const e of edges) {
9783
- if (e.provenance === import_types39.Provenance.OBSERVED) {
10107
+ if (e.provenance === import_types41.Provenance.OBSERVED) {
9784
10108
  seen.add(e.source);
9785
10109
  seen.add(e.target);
9786
10110
  }
9787
10111
  }
9788
10112
  return nodes.filter(
9789
- (n) => n.type === import_types39.NodeType.ServiceNode && !seen.has(n.id)
10113
+ (n) => n.type === import_types41.NodeType.ServiceNode && !seen.has(n.id)
9790
10114
  );
9791
10115
  }
9792
10116
  function formatDivergence(d) {
@@ -9861,26 +10185,26 @@ function formatIncompat(inc) {
9861
10185
  // src/watch.ts
9862
10186
  init_cjs_shims();
9863
10187
  var import_node_fs36 = __toESM(require("fs"), 1);
9864
- var import_node_path61 = __toESM(require("path"), 1);
10188
+ var import_node_path62 = __toESM(require("path"), 1);
9865
10189
  var import_chokidar = __toESM(require("chokidar"), 1);
9866
10190
 
9867
10191
  // src/api.ts
9868
10192
  init_cjs_shims();
9869
10193
  var import_fastify2 = __toESM(require("fastify"), 1);
9870
10194
  var import_cors = __toESM(require("@fastify/cors"), 1);
9871
- var import_types57 = require("@neat.is/types");
10195
+ var import_types59 = require("@neat.is/types");
9872
10196
 
9873
10197
  // src/extend/index.ts
9874
10198
  init_cjs_shims();
9875
10199
  var import_node_fs29 = require("fs");
9876
- var import_node_path54 = __toESM(require("path"), 1);
10200
+ var import_node_path55 = __toESM(require("path"), 1);
9877
10201
  var import_node_os2 = __toESM(require("os"), 1);
9878
10202
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
9879
10203
 
9880
10204
  // src/installers/package-manager.ts
9881
10205
  init_cjs_shims();
9882
10206
  var import_node_fs28 = require("fs");
9883
- var import_node_path53 = __toESM(require("path"), 1);
10207
+ var import_node_path54 = __toESM(require("path"), 1);
9884
10208
  var import_node_child_process = require("child_process");
9885
10209
  var LOCKFILE_PRIORITY = [
9886
10210
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -9902,22 +10226,22 @@ async function exists2(p) {
9902
10226
  }
9903
10227
  }
9904
10228
  async function detectPackageManager(serviceDir) {
9905
- let dir = import_node_path53.default.resolve(serviceDir);
10229
+ let dir = import_node_path54.default.resolve(serviceDir);
9906
10230
  const stops = /* @__PURE__ */ new Set();
9907
10231
  for (let i = 0; i < 64; i++) {
9908
10232
  if (stops.has(dir)) break;
9909
10233
  stops.add(dir);
9910
10234
  for (const candidate of LOCKFILE_PRIORITY) {
9911
- const lockPath = import_node_path53.default.join(dir, candidate.lockfile);
10235
+ const lockPath = import_node_path54.default.join(dir, candidate.lockfile);
9912
10236
  if (await exists2(lockPath)) {
9913
10237
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
9914
10238
  }
9915
10239
  }
9916
- const parent = import_node_path53.default.dirname(dir);
10240
+ const parent = import_node_path54.default.dirname(dir);
9917
10241
  if (parent === dir) break;
9918
10242
  dir = parent;
9919
10243
  }
9920
- return { pm: "npm", cwd: import_node_path53.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
10244
+ return { pm: "npm", cwd: import_node_path54.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
9921
10245
  }
9922
10246
  async function runPackageManagerInstall(cmd) {
9923
10247
  return new Promise((resolve) => {
@@ -9966,7 +10290,7 @@ async function fileExists2(p) {
9966
10290
  }
9967
10291
  }
9968
10292
  async function readPackageJson(scanPath) {
9969
- const pkgPath = import_node_path54.default.join(scanPath, "package.json");
10293
+ const pkgPath = import_node_path55.default.join(scanPath, "package.json");
9970
10294
  const raw = await import_node_fs29.promises.readFile(pkgPath, "utf8");
9971
10295
  return JSON.parse(raw);
9972
10296
  }
@@ -9985,11 +10309,11 @@ async function findHookFiles(scanPath) {
9985
10309
  for (const entry2 of entries) {
9986
10310
  if (entry2.isDirectory()) {
9987
10311
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
9988
- await walk6(import_node_path54.default.join(dir, entry2.name));
10312
+ await walk6(import_node_path55.default.join(dir, entry2.name));
9989
10313
  } else if (entry2.isFile()) {
9990
10314
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
9991
- const rel = import_node_path54.default.relative(scanPath, import_node_path54.default.join(dir, entry2.name));
9992
- found.push(rel.split(import_node_path54.default.sep).join("/"));
10315
+ const rel = import_node_path55.default.relative(scanPath, import_node_path55.default.join(dir, entry2.name));
10316
+ found.push(rel.split(import_node_path55.default.sep).join("/"));
9993
10317
  }
9994
10318
  }
9995
10319
  }
@@ -10000,7 +10324,7 @@ async function findHookFiles(scanPath) {
10000
10324
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
10001
10325
  let fallback = null;
10002
10326
  for (const file of hookFiles) {
10003
- const content = await import_node_fs29.promises.readFile(import_node_path54.default.join(scanPath, file), "utf8");
10327
+ const content = await import_node_fs29.promises.readFile(import_node_path55.default.join(scanPath, file), "utf8");
10004
10328
  const patched = splicedContent(content, snippet2);
10005
10329
  if (patched !== null) return { file, content, patched };
10006
10330
  if (fallback === null) fallback = { file, content };
@@ -10008,11 +10332,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
10008
10332
  return { file: fallback.file, content: fallback.content, patched: null };
10009
10333
  }
10010
10334
  function extendLogPath() {
10011
- return process.env.NEAT_EXTEND_LOG ?? import_node_path54.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
10335
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path55.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
10012
10336
  }
10013
10337
  async function appendExtendLog(entry2) {
10014
10338
  const logPath = extendLogPath();
10015
- await import_node_fs29.promises.mkdir(import_node_path54.default.dirname(logPath), { recursive: true });
10339
+ await import_node_fs29.promises.mkdir(import_node_path55.default.dirname(logPath), { recursive: true });
10016
10340
  await import_node_fs29.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
10017
10341
  }
10018
10342
  function splicedContent(fileContent, snippet2) {
@@ -10071,7 +10395,7 @@ function lookupInstrumentation(library, installedVersion) {
10071
10395
  }
10072
10396
  async function describeProjectInstrumentation(ctx) {
10073
10397
  const hookFiles = await findHookFiles(ctx.scanPath);
10074
- const envNeat = await fileExists2(import_node_path54.default.join(ctx.scanPath, ".env.neat"));
10398
+ const envNeat = await fileExists2(import_node_path55.default.join(ctx.scanPath, ".env.neat"));
10075
10399
  const registryInstrPackages = new Set(
10076
10400
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
10077
10401
  );
@@ -10093,7 +10417,7 @@ async function applyExtension(ctx, args, options) {
10093
10417
  );
10094
10418
  }
10095
10419
  for (const file of hookFiles) {
10096
- const content = await import_node_fs29.promises.readFile(import_node_path54.default.join(ctx.scanPath, file), "utf8");
10420
+ const content = await import_node_fs29.promises.readFile(import_node_path55.default.join(ctx.scanPath, file), "utf8");
10097
10421
  if (content.includes(args.registration_snippet)) {
10098
10422
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
10099
10423
  }
@@ -10105,10 +10429,10 @@ async function applyExtension(ctx, args, options) {
10105
10429
  );
10106
10430
  }
10107
10431
  const primaryFile = primary.file;
10108
- const primaryPath = import_node_path54.default.join(ctx.scanPath, primaryFile);
10432
+ const primaryPath = import_node_path55.default.join(ctx.scanPath, primaryFile);
10109
10433
  const filesTouched = [];
10110
10434
  const depsAdded = [];
10111
- const pkgPath = import_node_path54.default.join(ctx.scanPath, "package.json");
10435
+ const pkgPath = import_node_path55.default.join(ctx.scanPath, "package.json");
10112
10436
  const pkg = await readPackageJson(ctx.scanPath);
10113
10437
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
10114
10438
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -10147,7 +10471,7 @@ async function dryRunExtension(ctx, args) {
10147
10471
  };
10148
10472
  }
10149
10473
  for (const file of hookFiles) {
10150
- const content = await import_node_fs29.promises.readFile(import_node_path54.default.join(ctx.scanPath, file), "utf8");
10474
+ const content = await import_node_fs29.promises.readFile(import_node_path55.default.join(ctx.scanPath, file), "utf8");
10151
10475
  if (content.includes(args.registration_snippet)) {
10152
10476
  return {
10153
10477
  library: args.library,
@@ -10188,7 +10512,7 @@ async function rollbackExtension(ctx, args) {
10188
10512
  if (!match) {
10189
10513
  return { undone: false, message: "no apply found for library" };
10190
10514
  }
10191
- const pkgPath = import_node_path54.default.join(ctx.scanPath, "package.json");
10515
+ const pkgPath = import_node_path55.default.join(ctx.scanPath, "package.json");
10192
10516
  if (await fileExists2(pkgPath)) {
10193
10517
  const pkg = await readPackageJson(ctx.scanPath);
10194
10518
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -10199,7 +10523,7 @@ async function rollbackExtension(ctx, args) {
10199
10523
  }
10200
10524
  const hookFiles = await findHookFiles(ctx.scanPath);
10201
10525
  for (const file of hookFiles) {
10202
- const filePath = import_node_path54.default.join(ctx.scanPath, file);
10526
+ const filePath = import_node_path55.default.join(ctx.scanPath, file);
10203
10527
  const content = await import_node_fs29.promises.readFile(filePath, "utf8");
10204
10528
  if (content.includes(match.registration_snippet)) {
10205
10529
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -10337,23 +10661,23 @@ function canonicalJson(value) {
10337
10661
 
10338
10662
  // src/projects.ts
10339
10663
  init_cjs_shims();
10340
- var import_node_path55 = __toESM(require("path"), 1);
10664
+ var import_node_path56 = __toESM(require("path"), 1);
10341
10665
  function pathsForProject(project, baseDir) {
10342
10666
  if (project === DEFAULT_PROJECT) {
10343
10667
  return {
10344
- snapshotPath: import_node_path55.default.join(baseDir, "graph.json"),
10345
- errorsPath: import_node_path55.default.join(baseDir, "errors.ndjson"),
10346
- staleEventsPath: import_node_path55.default.join(baseDir, "stale-events.ndjson"),
10347
- embeddingsCachePath: import_node_path55.default.join(baseDir, "embeddings.json"),
10348
- policyViolationsPath: import_node_path55.default.join(baseDir, "policy-violations.ndjson")
10668
+ snapshotPath: import_node_path56.default.join(baseDir, "graph.json"),
10669
+ errorsPath: import_node_path56.default.join(baseDir, "errors.ndjson"),
10670
+ staleEventsPath: import_node_path56.default.join(baseDir, "stale-events.ndjson"),
10671
+ embeddingsCachePath: import_node_path56.default.join(baseDir, "embeddings.json"),
10672
+ policyViolationsPath: import_node_path56.default.join(baseDir, "policy-violations.ndjson")
10349
10673
  };
10350
10674
  }
10351
10675
  return {
10352
- snapshotPath: import_node_path55.default.join(baseDir, `${project}.json`),
10353
- errorsPath: import_node_path55.default.join(baseDir, `errors.${project}.ndjson`),
10354
- staleEventsPath: import_node_path55.default.join(baseDir, `stale-events.${project}.ndjson`),
10355
- embeddingsCachePath: import_node_path55.default.join(baseDir, `embeddings.${project}.json`),
10356
- policyViolationsPath: import_node_path55.default.join(baseDir, `policy-violations.${project}.ndjson`)
10676
+ snapshotPath: import_node_path56.default.join(baseDir, `${project}.json`),
10677
+ errorsPath: import_node_path56.default.join(baseDir, `errors.${project}.ndjson`),
10678
+ staleEventsPath: import_node_path56.default.join(baseDir, `stale-events.${project}.ndjson`),
10679
+ embeddingsCachePath: import_node_path56.default.join(baseDir, `embeddings.${project}.json`),
10680
+ policyViolationsPath: import_node_path56.default.join(baseDir, `policy-violations.${project}.ndjson`)
10357
10681
  };
10358
10682
  }
10359
10683
  var Projects = class {
@@ -10391,26 +10715,26 @@ var Projects = class {
10391
10715
  init_cjs_shims();
10392
10716
  var import_node_fs31 = require("fs");
10393
10717
  var import_node_os3 = __toESM(require("os"), 1);
10394
- var import_node_path56 = __toESM(require("path"), 1);
10395
- var import_types40 = require("@neat.is/types");
10718
+ var import_node_path57 = __toESM(require("path"), 1);
10719
+ var import_types42 = require("@neat.is/types");
10396
10720
  var LOCK_TIMEOUT_MS = 5e3;
10397
10721
  var LOCK_RETRY_MS = 50;
10398
10722
  function neatHome() {
10399
10723
  const override = process.env.NEAT_HOME;
10400
- if (override && override.length > 0) return import_node_path56.default.resolve(override);
10401
- return import_node_path56.default.join(import_node_os3.default.homedir(), ".neat");
10724
+ if (override && override.length > 0) return import_node_path57.default.resolve(override);
10725
+ return import_node_path57.default.join(import_node_os3.default.homedir(), ".neat");
10402
10726
  }
10403
10727
  function registryPath() {
10404
- return import_node_path56.default.join(neatHome(), "projects.json");
10728
+ return import_node_path57.default.join(neatHome(), "projects.json");
10405
10729
  }
10406
10730
  function registryLockPath() {
10407
- return import_node_path56.default.join(neatHome(), "projects.json.lock");
10731
+ return import_node_path57.default.join(neatHome(), "projects.json.lock");
10408
10732
  }
10409
10733
  function daemonPidPath() {
10410
- return import_node_path56.default.join(neatHome(), "neatd.pid");
10734
+ return import_node_path57.default.join(neatHome(), "neatd.pid");
10411
10735
  }
10412
10736
  function daemonsDir() {
10413
- return import_node_path56.default.join(neatHome(), "daemons");
10737
+ return import_node_path57.default.join(neatHome(), "daemons");
10414
10738
  }
10415
10739
  function isFiniteInt(v) {
10416
10740
  return typeof v === "number" && Number.isFinite(v);
@@ -10451,7 +10775,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
10451
10775
  const out = [];
10452
10776
  for (const name of names) {
10453
10777
  if (!name.endsWith(".json")) continue;
10454
- const file = import_node_path56.default.join(dir, name);
10778
+ const file = import_node_path57.default.join(dir, name);
10455
10779
  let raw;
10456
10780
  try {
10457
10781
  raw = await import_node_fs31.promises.readFile(file, "utf8");
@@ -10572,7 +10896,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
10572
10896
  }
10573
10897
  }
10574
10898
  async function normalizeProjectPath(input) {
10575
- const resolved = import_node_path56.default.resolve(input);
10899
+ const resolved = import_node_path57.default.resolve(input);
10576
10900
  try {
10577
10901
  return await import_node_fs31.promises.realpath(resolved);
10578
10902
  } catch {
@@ -10580,7 +10904,7 @@ async function normalizeProjectPath(input) {
10580
10904
  }
10581
10905
  }
10582
10906
  async function writeAtomically(target, contents) {
10583
- await import_node_fs31.promises.mkdir(import_node_path56.default.dirname(target), { recursive: true });
10907
+ await import_node_fs31.promises.mkdir(import_node_path57.default.dirname(target), { recursive: true });
10584
10908
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
10585
10909
  const fd = await import_node_fs31.promises.open(tmp, "w");
10586
10910
  try {
@@ -10593,7 +10917,7 @@ async function writeAtomically(target, contents) {
10593
10917
  }
10594
10918
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
10595
10919
  const deadline = Date.now() + timeoutMs;
10596
- await import_node_fs31.promises.mkdir(import_node_path56.default.dirname(lockPath), { recursive: true });
10920
+ await import_node_fs31.promises.mkdir(import_node_path57.default.dirname(lockPath), { recursive: true });
10597
10921
  let probedHolder = false;
10598
10922
  while (true) {
10599
10923
  try {
@@ -10646,10 +10970,10 @@ async function readRegistry() {
10646
10970
  throw err;
10647
10971
  }
10648
10972
  const parsed = JSON.parse(raw);
10649
- return import_types40.RegistryFileSchema.parse(parsed);
10973
+ return import_types42.RegistryFileSchema.parse(parsed);
10650
10974
  }
10651
10975
  async function writeRegistry(reg) {
10652
- const validated = import_types40.RegistryFileSchema.parse(reg);
10976
+ const validated = import_types42.RegistryFileSchema.parse(reg);
10653
10977
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
10654
10978
  }
10655
10979
  var ProjectNameCollisionError = class extends Error {
@@ -10835,7 +11159,7 @@ init_auth();
10835
11159
  // src/connectors-config.ts
10836
11160
  init_cjs_shims();
10837
11161
  var import_node_os4 = __toESM(require("os"), 1);
10838
- var import_node_path57 = __toESM(require("path"), 1);
11162
+ var import_node_path58 = __toESM(require("path"), 1);
10839
11163
  var import_node_fs32 = require("fs");
10840
11164
  var CONNECTORS_CONFIG_VERSION = 1;
10841
11165
  var EnvRefUnsetError = class extends Error {
@@ -10850,11 +11174,11 @@ var EnvRefUnsetError = class extends Error {
10850
11174
  };
10851
11175
  function neatHome2() {
10852
11176
  const override = process.env.NEAT_HOME;
10853
- if (override && override.length > 0) return import_node_path57.default.resolve(override);
10854
- return import_node_path57.default.join(import_node_os4.default.homedir(), ".neat");
11177
+ if (override && override.length > 0) return import_node_path58.default.resolve(override);
11178
+ return import_node_path58.default.join(import_node_os4.default.homedir(), ".neat");
10855
11179
  }
10856
11180
  function connectorsConfigPath(home = neatHome2()) {
10857
- return import_node_path57.default.join(home, "connectors.json");
11181
+ return import_node_path58.default.join(home, "connectors.json");
10858
11182
  }
10859
11183
  var MODE_MASK_LOOSER_THAN_0600 = 63;
10860
11184
  async function warnIfModeLooserThan0600(file) {
@@ -10985,7 +11309,7 @@ function connectorMatchesProject(entry2, project) {
10985
11309
  var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
10986
11310
  var CONNECTORS_LOCK_RETRY_MS = 50;
10987
11311
  function connectorsConfigLockPath(home = neatHome2()) {
10988
- return import_node_path57.default.join(home, "connectors.json.lock");
11312
+ return import_node_path58.default.join(home, "connectors.json.lock");
10989
11313
  }
10990
11314
  function isEnvRef(value) {
10991
11315
  return value.length > 1 && value.startsWith("$");
@@ -10998,7 +11322,7 @@ function redactCredentialRef(ref) {
10998
11322
  return out;
10999
11323
  }
11000
11324
  async function writeConfigAtomically0600(file, contents) {
11001
- await import_node_fs32.promises.mkdir(import_node_path57.default.dirname(file), { recursive: true });
11325
+ await import_node_fs32.promises.mkdir(import_node_path58.default.dirname(file), { recursive: true });
11002
11326
  const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
11003
11327
  const fd = await import_node_fs32.promises.open(tmp, "w", 384);
11004
11328
  try {
@@ -11012,7 +11336,7 @@ async function writeConfigAtomically0600(file, contents) {
11012
11336
  }
11013
11337
  async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
11014
11338
  const deadline = Date.now() + timeoutMs;
11015
- await import_node_fs32.promises.mkdir(import_node_path57.default.dirname(lockPath), { recursive: true });
11339
+ await import_node_fs32.promises.mkdir(import_node_path58.default.dirname(lockPath), { recursive: true });
11016
11340
  for (; ; ) {
11017
11341
  try {
11018
11342
  const fd = await import_node_fs32.promises.open(lockPath, "wx");
@@ -11162,15 +11486,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
11162
11486
 
11163
11487
  // src/connectors/index.ts
11164
11488
  init_cjs_shims();
11165
- var import_types41 = require("@neat.is/types");
11489
+ var import_types43 = require("@neat.is/types");
11166
11490
  var NO_ENV = "unknown";
11167
11491
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
11168
11492
  if (!graph.hasNode(targetNodeId)) return void 0;
11169
11493
  const sites = [];
11170
11494
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
11171
11495
  const edge = graph.getEdgeAttributes(edgeId);
11172
- if (edge.provenance !== import_types41.Provenance.EXTRACTED) continue;
11173
- const parsed = (0, import_types41.parseFileId)(edge.source);
11496
+ if (edge.provenance !== import_types43.Provenance.EXTRACTED) continue;
11497
+ const parsed = (0, import_types43.parseFileId)(edge.source);
11174
11498
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
11175
11499
  const site = { relPath: edge.evidence.file };
11176
11500
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -11181,7 +11505,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
11181
11505
  function routeCallSiteFor(graph, targetNodeId) {
11182
11506
  if (!graph.hasNode(targetNodeId)) return void 0;
11183
11507
  const attrs = graph.getNodeAttributes(targetNodeId);
11184
- if (attrs.type !== import_types41.NodeType.RouteNode || !attrs.path) return void 0;
11508
+ if (attrs.type !== import_types43.NodeType.RouteNode || !attrs.path) return void 0;
11185
11509
  const site = { relPath: attrs.path };
11186
11510
  if (attrs.line !== void 0) site.line = attrs.line;
11187
11511
  return site;
@@ -11201,6 +11525,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
11201
11525
  const { kind, name, provider } = resolved.ensureInfraNode;
11202
11526
  ensureInfraNode(graph, kind, name, provider);
11203
11527
  }
11528
+ mergeObservedColumns(graph, resolved.targetNodeId, signal.columns);
11204
11529
  const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
11205
11530
  const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
11206
11531
  const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
@@ -11631,6 +11956,7 @@ async function fetchSupabaseEdgeLogs(config, token, startIso, endIso, fetchImpl
11631
11956
 
11632
11957
  // src/connectors/supabase/map.ts
11633
11958
  init_cjs_shims();
11959
+ init_otel();
11634
11960
 
11635
11961
  // src/connectors/supabase/types.ts
11636
11962
  init_cjs_shims();
@@ -11656,10 +11982,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
11656
11982
  // src/connectors/supabase/map.ts
11657
11983
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
11658
11984
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
11659
- function targetFromRestPath(path70) {
11660
- const rpcMatch = REST_RPC_PATH_RE.exec(path70);
11985
+ function targetFromRestPath(path71) {
11986
+ const rpcMatch = REST_RPC_PATH_RE.exec(path71);
11661
11987
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
11662
- const tableMatch = REST_TABLE_PATH_RE.exec(path70);
11988
+ const tableMatch = REST_TABLE_PATH_RE.exec(path71);
11663
11989
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
11664
11990
  return null;
11665
11991
  }
@@ -11723,12 +12049,14 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
11723
12049
  if (delta <= 0) continue;
11724
12050
  const table = tableNameFromQueryText(row.query);
11725
12051
  if (!table) continue;
12052
+ const columns = columnsFromSqlStatement(row.query);
11726
12053
  signals.push({
11727
12054
  targetKind: SUPABASE_TABLE_TARGET_KIND,
11728
12055
  targetName: table,
11729
12056
  callCount: delta,
11730
12057
  errorCount: 0,
11731
- lastObservedIso: nowIso2
12058
+ lastObservedIso: nowIso2,
12059
+ ...columns.length > 0 ? { columns } : {}
11732
12060
  });
11733
12061
  }
11734
12062
  for (const queryid of [...previous.keys()]) {
@@ -11768,23 +12096,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
11768
12096
 
11769
12097
  // src/connectors/supabase/resolve.ts
11770
12098
  init_cjs_shims();
11771
- var import_types43 = require("@neat.is/types");
12099
+ var import_types45 = require("@neat.is/types");
11772
12100
  function createSupabaseResolveTarget(graph, config) {
11773
12101
  return (signal, _ctx) => {
11774
12102
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
11775
12103
  return null;
11776
12104
  }
11777
- const subResourceId = (0, import_types43.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
12105
+ const subResourceId = (0, import_types45.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
11778
12106
  if (graph.hasNode(subResourceId)) {
11779
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types43.EdgeType.CALLS };
12107
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types45.EdgeType.CALLS };
11780
12108
  }
11781
- const bareResourceId = (0, import_types43.infraId)(signal.targetKind, signal.targetName);
12109
+ const bareResourceId = (0, import_types45.infraId)(signal.targetKind, signal.targetName);
11782
12110
  if (graph.hasNode(bareResourceId)) {
11783
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types43.EdgeType.CALLS };
12111
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types45.EdgeType.CALLS };
11784
12112
  }
11785
- const projectLevelId = (0, import_types43.infraId)("supabase", config.nodeRef);
12113
+ const projectLevelId = (0, import_types45.infraId)("supabase", config.nodeRef);
11786
12114
  if (graph.hasNode(projectLevelId)) {
11787
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types43.EdgeType.CALLS };
12115
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types45.EdgeType.CALLS };
11788
12116
  }
11789
12117
  return null;
11790
12118
  };
@@ -11877,7 +12205,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
11877
12205
 
11878
12206
  // src/connectors/railway/index.ts
11879
12207
  init_cjs_shims();
11880
- var import_types47 = require("@neat.is/types");
12208
+ var import_types49 = require("@neat.is/types");
11881
12209
 
11882
12210
  // src/connectors/railway/client.ts
11883
12211
  init_cjs_shims();
@@ -12028,7 +12356,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
12028
12356
  const out = [];
12029
12357
  graph.forEachNode((_id, attrs) => {
12030
12358
  const node = attrs;
12031
- if (node.type !== import_types47.NodeType.RouteNode) return;
12359
+ if (node.type !== import_types49.NodeType.RouteNode) return;
12032
12360
  const route = attrs;
12033
12361
  if (route.service !== serviceName) return;
12034
12362
  out.push({
@@ -12132,12 +12460,12 @@ function createRailwayResolveTarget(config) {
12132
12460
  const serviceName = config.serviceNameById[config.serviceId];
12133
12461
  if (!serviceName) return null;
12134
12462
  if (signal.targetKind === ROUTE_TARGET_KIND) {
12135
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types47.EdgeType.CALLS };
12463
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types49.EdgeType.CALLS };
12136
12464
  }
12137
12465
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
12138
12466
  const peerName = config.serviceNameById[signal.targetName];
12139
12467
  if (!peerName) return null;
12140
- return { targetNodeId: (0, import_types47.serviceId)(peerName), serviceName, edgeType: import_types47.EdgeType.CONNECTS_TO };
12468
+ return { targetNodeId: (0, import_types49.serviceId)(peerName), serviceName, edgeType: import_types49.EdgeType.CONNECTS_TO };
12141
12469
  }
12142
12470
  return null;
12143
12471
  };
@@ -12261,9 +12589,9 @@ function parseFirebaseTargetName(targetName) {
12261
12589
  const secondSep = rest.indexOf(FIELD_SEP);
12262
12590
  if (secondSep === -1) return null;
12263
12591
  const method = rest.slice(0, secondSep);
12264
- const path70 = rest.slice(secondSep + 1);
12265
- if (!resourceName || !method || !path70) return null;
12266
- return { resourceName, method, path: path70 };
12592
+ const path71 = rest.slice(secondSep + 1);
12593
+ if (!resourceName || !method || !path71) return null;
12594
+ return { resourceName, method, path: path71 };
12267
12595
  }
12268
12596
  function resourceNameFor(type, labels) {
12269
12597
  if (!labels) return null;
@@ -12301,14 +12629,14 @@ function mapLogEntryToSignal(entry2) {
12301
12629
  if (!req) return null;
12302
12630
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
12303
12631
  const method = req.requestMethod.toUpperCase();
12304
- const path70 = pathFromRequestUrl(req.requestUrl);
12305
- if (path70 === null) return null;
12632
+ const path71 = pathFromRequestUrl(req.requestUrl);
12633
+ if (path71 === null) return null;
12306
12634
  const timestamp = entry2.timestamp;
12307
12635
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
12308
12636
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
12309
12637
  return {
12310
12638
  targetKind: resourceType,
12311
- targetName: packFirebaseTargetName({ resourceName, method, path: path70 }),
12639
+ targetName: packFirebaseTargetName({ resourceName, method, path: path71 }),
12312
12640
  callCount: 1,
12313
12641
  errorCount: isError ? 1 : 0,
12314
12642
  lastObservedIso: timestamp
@@ -12325,7 +12653,7 @@ function mapLogEntriesToSignals(entries) {
12325
12653
 
12326
12654
  // src/connectors/firebase/resolve.ts
12327
12655
  init_cjs_shims();
12328
- var import_types48 = require("@neat.is/types");
12656
+ var import_types50 = require("@neat.is/types");
12329
12657
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
12330
12658
  switch (resourceType) {
12331
12659
  case "cloud_function":
@@ -12340,7 +12668,7 @@ function routeEntriesFor(graph, serviceName) {
12340
12668
  const entries = [];
12341
12669
  graph.forEachNode((_id, attrs) => {
12342
12670
  const node = attrs;
12343
- if (node.type !== import_types48.NodeType.RouteNode) return;
12671
+ if (node.type !== import_types50.NodeType.RouteNode) return;
12344
12672
  const route = attrs;
12345
12673
  if (route.service !== serviceName) return;
12346
12674
  entries.push({
@@ -12372,7 +12700,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
12372
12700
  return {
12373
12701
  targetNodeId: match.routeNodeId,
12374
12702
  serviceName,
12375
- edgeType: import_types48.EdgeType.CALLS
12703
+ edgeType: import_types50.EdgeType.CALLS
12376
12704
  };
12377
12705
  };
12378
12706
  }
@@ -12399,7 +12727,7 @@ init_cjs_shims();
12399
12727
 
12400
12728
  // src/connectors/cloudflare/connector.ts
12401
12729
  init_cjs_shims();
12402
- var import_types50 = require("@neat.is/types");
12730
+ var import_types52 = require("@neat.is/types");
12403
12731
 
12404
12732
  // src/connectors/cloudflare/client.ts
12405
12733
  init_cjs_shims();
@@ -12515,7 +12843,7 @@ function mapEventToSignal(event) {
12515
12843
  if (Number.isNaN(observedAt.getTime())) return null;
12516
12844
  const statusCode = metadata?.statusCode;
12517
12845
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
12518
- const path70 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
12846
+ const path71 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
12519
12847
  return {
12520
12848
  targetKind: CLOUDFLARE_TARGET_KIND,
12521
12849
  targetName: scriptName,
@@ -12523,7 +12851,7 @@ function mapEventToSignal(event) {
12523
12851
  errorCount: isError ? 1 : 0,
12524
12852
  lastObservedIso: observedAt.toISOString(),
12525
12853
  method,
12526
- ...path70 ? { path: path70 } : {},
12854
+ ...path71 ? { path: path71 } : {},
12527
12855
  ...typeof statusCode === "number" ? { statusCode } : {},
12528
12856
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
12529
12857
  };
@@ -12563,19 +12891,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
12563
12891
  graph.forEachNode((id, attrs) => {
12564
12892
  if (found) return;
12565
12893
  const a = attrs;
12566
- if (a.type === import_types50.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
12894
+ if (a.type === import_types52.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
12567
12895
  found = id;
12568
12896
  }
12569
12897
  });
12570
12898
  return found;
12571
12899
  }
12572
- function findMatchingRouteNode(graph, serviceName, method, path70) {
12573
- const normalizedPath = normalizePathTemplate(path70);
12900
+ function findMatchingRouteNode(graph, serviceName, method, path71) {
12901
+ const normalizedPath = normalizePathTemplate(path71);
12574
12902
  let found = null;
12575
12903
  graph.forEachNode((id, attrs) => {
12576
12904
  if (found) return;
12577
12905
  const a = attrs;
12578
- if (a.type !== import_types50.NodeType.RouteNode || a.service !== serviceName) return;
12906
+ if (a.type !== import_types52.NodeType.RouteNode || a.service !== serviceName) return;
12579
12907
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
12580
12908
  const routeMethod = (a.method ?? "").toUpperCase();
12581
12909
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -12587,18 +12915,18 @@ function createCloudflareResolveTarget(config, graph) {
12587
12915
  return (signal) => {
12588
12916
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
12589
12917
  const scriptName = signal.targetName;
12590
- const { method, path: path70 } = signal;
12918
+ const { method, path: path71 } = signal;
12591
12919
  const resolveRouteGrain = (serviceName, wholeFileId) => {
12592
- if (!method || !path70) return wholeFileId;
12593
- return findMatchingRouteNode(graph, serviceName, method, path70) ?? wholeFileId;
12920
+ if (!method || !path71) return wholeFileId;
12921
+ return findMatchingRouteNode(graph, serviceName, method, path71) ?? wholeFileId;
12594
12922
  };
12595
12923
  const mapping = config.workers?.[scriptName];
12596
12924
  if (mapping) {
12597
- const wholeFileId = (0, import_types50.fileId)(mapping.service, mapping.entryFile);
12925
+ const wholeFileId = (0, import_types52.fileId)(mapping.service, mapping.entryFile);
12598
12926
  return {
12599
12927
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
12600
12928
  serviceName: mapping.service,
12601
- edgeType: import_types50.EdgeType.CALLS
12929
+ edgeType: import_types52.EdgeType.CALLS
12602
12930
  };
12603
12931
  }
12604
12932
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -12607,13 +12935,13 @@ function createCloudflareResolveTarget(config, graph) {
12607
12935
  return {
12608
12936
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
12609
12937
  serviceName: fileNode.service,
12610
- edgeType: import_types50.EdgeType.CALLS
12938
+ edgeType: import_types52.EdgeType.CALLS
12611
12939
  };
12612
12940
  }
12613
12941
  return {
12614
- targetNodeId: (0, import_types50.infraId)("cloudflare-worker", scriptName),
12942
+ targetNodeId: (0, import_types52.infraId)("cloudflare-worker", scriptName),
12615
12943
  serviceName: scriptName,
12616
- edgeType: import_types50.EdgeType.CALLS,
12944
+ edgeType: import_types52.EdgeType.CALLS,
12617
12945
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
12618
12946
  };
12619
12947
  };
@@ -12791,12 +13119,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
12791
13119
  if (!prior || calls <= prior.calls) continue;
12792
13120
  const table = tableFromSqlStatement(row.query);
12793
13121
  if (!table) continue;
13122
+ const columns = columnsFromSqlStatement(row.query);
12794
13123
  signals.push({
12795
13124
  targetKind: NEON_SQL_TABLE_TARGET_KIND,
12796
13125
  targetName: table,
12797
13126
  callCount: calls - prior.calls,
12798
13127
  errorCount: 0,
12799
- lastObservedIso: observedAtIso
13128
+ lastObservedIso: observedAtIso,
13129
+ ...columns.length > 0 ? { columns } : {}
12800
13130
  });
12801
13131
  }
12802
13132
  for (const queryid of previous.keys()) {
@@ -12807,14 +13137,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
12807
13137
 
12808
13138
  // src/connectors/neon/resolve.ts
12809
13139
  init_cjs_shims();
12810
- var import_types54 = require("@neat.is/types");
13140
+ var import_types56 = require("@neat.is/types");
12811
13141
  function createNeonResolveTarget(config) {
12812
13142
  return (signal) => {
12813
13143
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
12814
13144
  return {
12815
- targetNodeId: (0, import_types54.infraId)("sql-table", signal.targetName),
13145
+ targetNodeId: (0, import_types56.infraId)("sql-table", signal.targetName),
12816
13146
  serviceName: config.serviceName,
12817
- edgeType: import_types54.EdgeType.CALLS,
13147
+ edgeType: import_types56.EdgeType.CALLS,
12818
13148
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
12819
13149
  };
12820
13150
  };
@@ -13457,11 +13787,11 @@ function registerRoutes(scope, ctx) {
13457
13787
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
13458
13788
  const parsed = [];
13459
13789
  for (const c of candidates) {
13460
- const r = import_types57.DivergenceTypeSchema.safeParse(c);
13790
+ const r = import_types59.DivergenceTypeSchema.safeParse(c);
13461
13791
  if (!r.success) {
13462
13792
  return reply.code(400).send({
13463
13793
  error: `unknown divergence type "${c}"`,
13464
- allowed: import_types57.DivergenceTypeSchema.options
13794
+ allowed: import_types59.DivergenceTypeSchema.options
13465
13795
  });
13466
13796
  }
13467
13797
  parsed.push(r.data);
@@ -13770,7 +14100,7 @@ function registerRoutes(scope, ctx) {
13770
14100
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
13771
14101
  let violations = await log.readAll();
13772
14102
  if (req.query.severity) {
13773
- const sev = import_types57.PolicySeveritySchema.safeParse(req.query.severity);
14103
+ const sev = import_types59.PolicySeveritySchema.safeParse(req.query.severity);
13774
14104
  if (!sev.success) {
13775
14105
  return reply.code(400).send({
13776
14106
  error: "invalid severity",
@@ -13809,7 +14139,7 @@ function registerRoutes(scope, ctx) {
13809
14139
  scope.post("/policies/check", async (req, reply) => {
13810
14140
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
13811
14141
  if (!proj) return;
13812
- const parsed = import_types57.PoliciesCheckBodySchema.safeParse(req.body ?? {});
14142
+ const parsed = import_types59.PoliciesCheckBodySchema.safeParse(req.body ?? {});
13813
14143
  if (!parsed.success) {
13814
14144
  return reply.code(400).send({
13815
14145
  error: "invalid /policies/check body",
@@ -14131,7 +14461,7 @@ init_otel();
14131
14461
  // src/daemon.ts
14132
14462
  init_cjs_shims();
14133
14463
  var import_node_fs34 = require("fs");
14134
- var import_node_path59 = __toESM(require("path"), 1);
14464
+ var import_node_path60 = __toESM(require("path"), 1);
14135
14465
  var import_node_module = require("module");
14136
14466
  init_otel();
14137
14467
  init_auth();
@@ -14139,28 +14469,28 @@ init_auth();
14139
14469
  // src/unrouted.ts
14140
14470
  init_cjs_shims();
14141
14471
  var import_node_fs33 = require("fs");
14142
- var import_node_path58 = __toESM(require("path"), 1);
14472
+ var import_node_path59 = __toESM(require("path"), 1);
14143
14473
 
14144
14474
  // src/daemon.ts
14145
- var import_types58 = require("@neat.is/types");
14475
+ var import_types60 = require("@neat.is/types");
14146
14476
  function daemonJsonPath(scanPath) {
14147
- return import_node_path59.default.join(scanPath, "neat-out", "daemon.json");
14477
+ return import_node_path60.default.join(scanPath, "neat-out", "daemon.json");
14148
14478
  }
14149
14479
  function daemonsDiscoveryDir(home) {
14150
14480
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
14151
- return import_node_path59.default.join(base, "daemons");
14481
+ return import_node_path60.default.join(base, "daemons");
14152
14482
  }
14153
14483
  function daemonDiscoveryPath(project, home) {
14154
- return import_node_path59.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
14484
+ return import_node_path60.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
14155
14485
  }
14156
14486
  function sanitizeDiscoveryName(project) {
14157
14487
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
14158
14488
  }
14159
14489
  function neatHomeFromEnv() {
14160
14490
  const env = process.env.NEAT_HOME;
14161
- if (env && env.length > 0) return import_node_path59.default.resolve(env);
14491
+ if (env && env.length > 0) return import_node_path60.default.resolve(env);
14162
14492
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
14163
- return import_node_path59.default.join(home, ".neat");
14493
+ return import_node_path60.default.join(home, ".neat");
14164
14494
  }
14165
14495
  async function readDaemonRecord(scanPath) {
14166
14496
  try {
@@ -14231,7 +14561,7 @@ init_otel_grpc();
14231
14561
  // src/search.ts
14232
14562
  init_cjs_shims();
14233
14563
  var import_node_fs35 = require("fs");
14234
- var import_node_path60 = __toESM(require("path"), 1);
14564
+ var import_node_path61 = __toESM(require("path"), 1);
14235
14565
  var import_node_crypto4 = require("crypto");
14236
14566
  var DEFAULT_LIMIT = 10;
14237
14567
  var NOMIC_DIM = 768;
@@ -14394,7 +14724,7 @@ async function readCache(cachePath) {
14394
14724
  }
14395
14725
  }
14396
14726
  async function writeCache(cachePath, cache) {
14397
- await import_node_fs35.promises.mkdir(import_node_path60.default.dirname(cachePath), { recursive: true });
14727
+ await import_node_fs35.promises.mkdir(import_node_path61.default.dirname(cachePath), { recursive: true });
14398
14728
  await import_node_fs35.promises.writeFile(cachePath, JSON.stringify(cache));
14399
14729
  }
14400
14730
  var VectorIndex = class {
@@ -14552,8 +14882,8 @@ var ALL_PHASES = [
14552
14882
  ];
14553
14883
  function classifyChange(relPath) {
14554
14884
  const phases = /* @__PURE__ */ new Set();
14555
- const base = import_node_path61.default.basename(relPath).toLowerCase();
14556
- const segments = relPath.split(import_node_path61.default.sep).map((s) => s.toLowerCase());
14885
+ const base = import_node_path62.default.basename(relPath).toLowerCase();
14886
+ const segments = relPath.split(import_node_path62.default.sep).map((s) => s.toLowerCase());
14557
14887
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
14558
14888
  phases.add("services");
14559
14889
  phases.add("aliases");
@@ -14681,9 +15011,9 @@ function countWatchableDirs(scanPath, limit) {
14681
15011
  for (const e of entries) {
14682
15012
  if (count >= limit) return;
14683
15013
  if (!e.isDirectory()) continue;
14684
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path61.default.join(dir, e.name) + import_node_path61.default.sep))) continue;
15014
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path62.default.join(dir, e.name) + import_node_path62.default.sep))) continue;
14685
15015
  count++;
14686
- if (depth < 2) visit(import_node_path61.default.join(dir, e.name), depth + 1);
15016
+ if (depth < 2) visit(import_node_path62.default.join(dir, e.name), depth + 1);
14687
15017
  }
14688
15018
  };
14689
15019
  visit(scanPath, 0);
@@ -14701,8 +15031,8 @@ async function startWatch(graph, opts) {
14701
15031
  const projectName = opts.project ?? DEFAULT_PROJECT;
14702
15032
  await loadGraphFromDisk(graph, opts.outPath);
14703
15033
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
14704
- const policyFilePath = import_node_path61.default.join(opts.scanPath, "policy.json");
14705
- const policyViolationsPath = import_node_path61.default.join(import_node_path61.default.dirname(opts.outPath), "policy-violations.ndjson");
15034
+ const policyFilePath = import_node_path62.default.join(opts.scanPath, "policy.json");
15035
+ const policyViolationsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), "policy-violations.ndjson");
14706
15036
  let policies = [];
14707
15037
  try {
14708
15038
  policies = await loadPolicyFile(policyFilePath);
@@ -14762,7 +15092,7 @@ async function startWatch(graph, opts) {
14762
15092
  assertBindAuthority(host, auth.authToken);
14763
15093
  const port = opts.port ?? 8080;
14764
15094
  const otelPort = opts.otelPort ?? 4318;
14765
- const cachePath = opts.embeddingsCachePath ?? import_node_path61.default.join(import_node_path61.default.dirname(opts.outPath), "embeddings.json");
15095
+ const cachePath = opts.embeddingsCachePath ?? import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), "embeddings.json");
14766
15096
  let searchIndex;
14767
15097
  try {
14768
15098
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -14780,7 +15110,7 @@ async function startWatch(graph, opts) {
14780
15110
  // Paths are derived from the explicit options the watch caller passes
14781
15111
  // — pathsForProject is only used to fill in the embeddings/snapshot
14782
15112
  // fields so the registry shape is complete.
14783
- ...pathsForProject(projectName, import_node_path61.default.dirname(opts.outPath)),
15113
+ ...pathsForProject(projectName, import_node_path62.default.dirname(opts.outPath)),
14784
15114
  snapshotPath: opts.outPath,
14785
15115
  errorsPath: opts.errorsPath,
14786
15116
  staleEventsPath: opts.staleEventsPath
@@ -14898,9 +15228,9 @@ async function startWatch(graph, opts) {
14898
15228
  };
14899
15229
  const onPath = (absPath) => {
14900
15230
  if (shouldIgnore(absPath)) return;
14901
- const rel = import_node_path61.default.relative(opts.scanPath, absPath);
15231
+ const rel = import_node_path62.default.relative(opts.scanPath, absPath);
14902
15232
  if (!rel || rel.startsWith("..")) return;
14903
- pendingPaths.add(rel.split(import_node_path61.default.sep).join("/"));
15233
+ pendingPaths.add(rel.split(import_node_path62.default.sep).join("/"));
14904
15234
  const phases = classifyChange(rel);
14905
15235
  if (phases.size === 0) {
14906
15236
  for (const p of ALL_PHASES) pending.add(p);
@@ -14958,7 +15288,7 @@ async function startWatch(graph, opts) {
14958
15288
  // src/deploy/detect.ts
14959
15289
  init_cjs_shims();
14960
15290
  var import_node_fs37 = require("fs");
14961
- var import_node_path62 = __toESM(require("path"), 1);
15291
+ var import_node_path63 = __toESM(require("path"), 1);
14962
15292
  var import_node_child_process2 = require("child_process");
14963
15293
  var import_node_crypto5 = require("crypto");
14964
15294
  function generateToken() {
@@ -15058,7 +15388,7 @@ async function runDeploy(opts = {}) {
15058
15388
  const token = generateToken();
15059
15389
  switch (substrate) {
15060
15390
  case "docker-compose": {
15061
- const artifactPath = import_node_path62.default.join(cwd, "docker-compose.neat.yml");
15391
+ const artifactPath = import_node_path63.default.join(cwd, "docker-compose.neat.yml");
15062
15392
  const contents = emitDockerCompose(cwd);
15063
15393
  await import_node_fs37.promises.writeFile(artifactPath, contents, "utf8");
15064
15394
  return {
@@ -15066,11 +15396,11 @@ async function runDeploy(opts = {}) {
15066
15396
  artifactPath,
15067
15397
  token,
15068
15398
  contents,
15069
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path62.default.basename(artifactPath)} up -d`
15399
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path63.default.basename(artifactPath)} up -d`
15070
15400
  };
15071
15401
  }
15072
15402
  case "systemd": {
15073
- const artifactPath = import_node_path62.default.join(cwd, "neat.service");
15403
+ const artifactPath = import_node_path63.default.join(cwd, "neat.service");
15074
15404
  const contents = emitSystemdUnit(cwd);
15075
15405
  await import_node_fs37.promises.writeFile(artifactPath, contents, "utf8");
15076
15406
  return {
@@ -15106,13 +15436,13 @@ init_cjs_shims();
15106
15436
  // src/installers/javascript.ts
15107
15437
  init_cjs_shims();
15108
15438
  var import_node_fs38 = require("fs");
15109
- var import_node_path63 = __toESM(require("path"), 1);
15439
+ var import_node_path64 = __toESM(require("path"), 1);
15110
15440
  var import_semver2 = __toESM(require("semver"), 1);
15111
15441
 
15112
15442
  // src/installers/templates.ts
15113
15443
  init_cjs_shims();
15114
15444
  var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
15115
- var OTEL_INIT_STAMP = "// neat-template-version: 7 \u2014 OTel init degrades to no-OBSERVED instead of crashing the host app when @opentelemetry deps are absent (#820); daemon.json endpoint resolution (ADR-096) + layered file-first capture (ADR-090).";
15445
+ var OTEL_INIT_STAMP = "// neat-template-version: 8 \u2014 CommonJS TypeScript uses a synchronous bootstrap (#910); OTel init degrades to no-OBSERVED when dependencies are absent (#820); daemon.json endpoint resolution (ADR-096) + layered file-first capture (ADR-090).";
15116
15446
  var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer ' + process.env.NEAT_OTEL_TOKEN";
15117
15447
  var OTEL_OTLP_PROTOCOL_JS = "process.env.OTEL_EXPORTER_OTLP_PROTOCOL ||= 'http/json'";
15118
15448
  var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
@@ -15476,6 +15806,13 @@ ${neatWireCaptureSource(false)}
15476
15806
  }
15477
15807
  }
15478
15808
  `;
15809
+ var OTEL_INIT_TS_CJS = OTEL_INIT_CJS.replace(
15810
+ `${OTEL_INIT_HEADER}
15811
+ ${OTEL_INIT_STAMP}`,
15812
+ `${OTEL_INIT_HEADER}
15813
+ ${OTEL_INIT_STAMP}
15814
+ // @ts-nocheck \u2014 generated CommonJS runtime shim.`
15815
+ );
15479
15816
  var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
15480
15817
  ${OTEL_INIT_STAMP}
15481
15818
  ${OTEL_ESM_NODE_IMPORTS}
@@ -15747,6 +16084,13 @@ function detectNonBundledInstrumentations(pkg) {
15747
16084
  registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
15748
16085
  });
15749
16086
  }
16087
+ if (getMajor(deps["@nestjs/core"]) >= 11) {
16088
+ out.push({
16089
+ pkg: "@opentelemetry/instrumentation-nestjs-core",
16090
+ version: "^0.67.0",
16091
+ registration: "instrumentations.push(new (require('@opentelemetry/instrumentation-nestjs-core').NestInstrumentation)())"
16092
+ });
16093
+ }
15750
16094
  return out;
15751
16095
  }
15752
16096
  var OTEL_ENV = {
@@ -15759,11 +16103,11 @@ var OTEL_ENV = {
15759
16103
  value: "http://localhost:4318/projects/<project>/v1/traces"
15760
16104
  };
15761
16105
  function serviceNodeName(pkg, serviceDir) {
15762
- return pkg.name ?? import_node_path63.default.basename(serviceDir);
16106
+ return pkg.name ?? import_node_path64.default.basename(serviceDir);
15763
16107
  }
15764
16108
  function projectToken(pkg, serviceDir, project) {
15765
16109
  if (project && project.length > 0) return project;
15766
- return pkg.name ?? import_node_path63.default.basename(serviceDir);
16110
+ return pkg.name ?? import_node_path64.default.basename(serviceDir);
15767
16111
  }
15768
16112
  async function readJsonFile(p) {
15769
16113
  try {
@@ -15776,16 +16120,16 @@ async function readJsonFile(p) {
15776
16120
  async function detectRuntimeKind(pkgRoot, pkg) {
15777
16121
  const deps = allDeps(pkg);
15778
16122
  if ("react-native" in deps || "expo" in deps) return "react-native";
15779
- const appJson = await readJsonFile(import_node_path63.default.join(pkgRoot, "app.json"));
16123
+ const appJson = await readJsonFile(import_node_path64.default.join(pkgRoot, "app.json"));
15780
16124
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
15781
16125
  return "react-native";
15782
16126
  }
15783
- if (await exists3(import_node_path63.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path63.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path63.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
16127
+ if (await exists3(import_node_path64.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path64.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path64.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
15784
16128
  return "browser-bundle";
15785
16129
  }
15786
- if (await exists3(import_node_path63.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
15787
- if (await exists3(import_node_path63.default.join(pkgRoot, "bun.lockb"))) return "bun";
15788
- if (await exists3(import_node_path63.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path63.default.join(pkgRoot, "deno.lock"))) {
16130
+ if (await exists3(import_node_path64.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
16131
+ if (await exists3(import_node_path64.default.join(pkgRoot, "bun.lockb"))) return "bun";
16132
+ if (await exists3(import_node_path64.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path64.default.join(pkgRoot, "deno.lock"))) {
15789
16133
  return "deno";
15790
16134
  }
15791
16135
  const engines = pkg.engines ?? {};
@@ -15794,7 +16138,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
15794
16138
  }
15795
16139
  async function readPackageJson2(serviceDir) {
15796
16140
  try {
15797
- const raw = await import_node_fs38.promises.readFile(import_node_path63.default.join(serviceDir, "package.json"), "utf8");
16141
+ const raw = await import_node_fs38.promises.readFile(import_node_path64.default.join(serviceDir, "package.json"), "utf8");
15798
16142
  return JSON.parse(raw);
15799
16143
  } catch {
15800
16144
  return null;
@@ -15838,7 +16182,7 @@ function needsVersionUpgrade(installed, expected) {
15838
16182
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
15839
16183
  async function findNextConfig(serviceDir) {
15840
16184
  for (const name of NEXT_CONFIG_CANDIDATES) {
15841
- const candidate = import_node_path63.default.join(serviceDir, name);
16185
+ const candidate = import_node_path64.default.join(serviceDir, name);
15842
16186
  if (await exists3(candidate)) return candidate;
15843
16187
  }
15844
16188
  return null;
@@ -15907,7 +16251,7 @@ function hasRemixDependency(pkg) {
15907
16251
  }
15908
16252
  async function findRemixEntry(serviceDir) {
15909
16253
  for (const rel of REMIX_ENTRY_CANDIDATES) {
15910
- const candidate = import_node_path63.default.join(serviceDir, rel);
16254
+ const candidate = import_node_path64.default.join(serviceDir, rel);
15911
16255
  if (await exists3(candidate)) return candidate;
15912
16256
  }
15913
16257
  return null;
@@ -15919,14 +16263,14 @@ function hasSvelteKitDependency(pkg) {
15919
16263
  }
15920
16264
  async function findSvelteKitHooks(serviceDir) {
15921
16265
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
15922
- const candidate = import_node_path63.default.join(serviceDir, rel);
16266
+ const candidate = import_node_path64.default.join(serviceDir, rel);
15923
16267
  if (await exists3(candidate)) return candidate;
15924
16268
  }
15925
16269
  return null;
15926
16270
  }
15927
16271
  async function findSvelteKitConfig(serviceDir) {
15928
16272
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
15929
- const candidate = import_node_path63.default.join(serviceDir, rel);
16273
+ const candidate = import_node_path64.default.join(serviceDir, rel);
15930
16274
  if (await exists3(candidate)) return candidate;
15931
16275
  }
15932
16276
  return null;
@@ -15937,7 +16281,7 @@ function hasNuxtDependency(pkg) {
15937
16281
  }
15938
16282
  async function findNuxtConfig(serviceDir) {
15939
16283
  for (const name of NUXT_CONFIG_CANDIDATES) {
15940
- const candidate = import_node_path63.default.join(serviceDir, name);
16284
+ const candidate = import_node_path64.default.join(serviceDir, name);
15941
16285
  if (await exists3(candidate)) return candidate;
15942
16286
  }
15943
16287
  return null;
@@ -15948,7 +16292,7 @@ function hasAstroDependency(pkg) {
15948
16292
  }
15949
16293
  async function findAstroConfig(serviceDir) {
15950
16294
  for (const name of ASTRO_CONFIG_CANDIDATES) {
15951
- const candidate = import_node_path63.default.join(serviceDir, name);
16295
+ const candidate = import_node_path64.default.join(serviceDir, name);
15952
16296
  if (await exists3(candidate)) return candidate;
15953
16297
  }
15954
16298
  return null;
@@ -15962,7 +16306,7 @@ function parseNextMajor(range) {
15962
16306
  return Number.isFinite(n) ? n : null;
15963
16307
  }
15964
16308
  async function isTypeScriptProject(serviceDir) {
15965
- return exists3(import_node_path63.default.join(serviceDir, "tsconfig.json"));
16309
+ return exists3(import_node_path64.default.join(serviceDir, "tsconfig.json"));
15966
16310
  }
15967
16311
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
15968
16312
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -16011,7 +16355,7 @@ function entryFromScript(script) {
16011
16355
  }
16012
16356
  async function resolveEntry(serviceDir, pkg) {
16013
16357
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
16014
- const candidate = import_node_path63.default.resolve(serviceDir, pkg.main);
16358
+ const candidate = import_node_path64.default.resolve(serviceDir, pkg.main);
16015
16359
  if (await exists3(candidate)) return candidate;
16016
16360
  }
16017
16361
  if (pkg.bin) {
@@ -16025,59 +16369,60 @@ async function resolveEntry(serviceDir, pkg) {
16025
16369
  if (typeof first === "string") binEntry = first;
16026
16370
  }
16027
16371
  if (binEntry) {
16028
- const candidate = import_node_path63.default.resolve(serviceDir, binEntry);
16372
+ const candidate = import_node_path64.default.resolve(serviceDir, binEntry);
16029
16373
  if (await exists3(candidate)) return candidate;
16030
16374
  }
16031
16375
  }
16032
16376
  const startEntry = entryFromScript(pkg.scripts?.start);
16033
16377
  if (startEntry) {
16034
- const candidate = import_node_path63.default.resolve(serviceDir, startEntry);
16378
+ const candidate = import_node_path64.default.resolve(serviceDir, startEntry);
16035
16379
  if (await exists3(candidate)) return candidate;
16036
16380
  }
16037
16381
  const devEntry = entryFromScript(pkg.scripts?.dev);
16038
16382
  if (devEntry) {
16039
- const candidate = import_node_path63.default.resolve(serviceDir, devEntry);
16383
+ const candidate = import_node_path64.default.resolve(serviceDir, devEntry);
16040
16384
  if (await exists3(candidate)) return candidate;
16041
16385
  }
16042
16386
  for (const rel of SRC_INDEX_CANDIDATES) {
16043
- const candidate = import_node_path63.default.join(serviceDir, rel);
16387
+ const candidate = import_node_path64.default.join(serviceDir, rel);
16044
16388
  if (await exists3(candidate)) return candidate;
16045
16389
  }
16046
16390
  for (const rel of SRC_NAMED_CANDIDATES) {
16047
- const candidate = import_node_path63.default.join(serviceDir, rel);
16391
+ const candidate = import_node_path64.default.join(serviceDir, rel);
16048
16392
  if (await exists3(candidate)) return candidate;
16049
16393
  }
16050
16394
  for (const rel of ROOT_NAMED_CANDIDATES) {
16051
- const candidate = import_node_path63.default.join(serviceDir, rel);
16395
+ const candidate = import_node_path64.default.join(serviceDir, rel);
16052
16396
  if (await exists3(candidate)) return candidate;
16053
16397
  }
16054
16398
  for (const name of INDEX_CANDIDATES) {
16055
- const candidate = import_node_path63.default.join(serviceDir, name);
16399
+ const candidate = import_node_path64.default.join(serviceDir, name);
16056
16400
  if (await exists3(candidate)) return candidate;
16057
16401
  }
16058
16402
  return null;
16059
16403
  }
16060
16404
  function dispatchEntry(entryFile, pkg) {
16061
- const ext = import_node_path63.default.extname(entryFile).toLowerCase();
16062
- if (ext === ".ts" || ext === ".tsx") return "ts";
16405
+ const ext = import_node_path64.default.extname(entryFile).toLowerCase();
16406
+ if (ext === ".ts" || ext === ".tsx") return pkg.type === "module" ? "ts" : "ts-cjs";
16063
16407
  if (ext === ".mjs") return "esm";
16064
16408
  if (ext === ".cjs") return "cjs";
16065
16409
  return pkg.type === "module" ? "esm" : "cjs";
16066
16410
  }
16067
16411
  function otelInitFilename(flavor) {
16068
- if (flavor === "ts") return "otel-init.ts";
16412
+ if (flavor === "ts" || flavor === "ts-cjs") return "otel-init.ts";
16069
16413
  if (flavor === "esm") return "otel-init.mjs";
16070
16414
  return "otel-init.cjs";
16071
16415
  }
16072
16416
  function otelInitContents(flavor) {
16073
16417
  if (flavor === "ts") return OTEL_INIT_TS;
16418
+ if (flavor === "ts-cjs") return OTEL_INIT_TS_CJS;
16074
16419
  if (flavor === "esm") return OTEL_INIT_ESM;
16075
16420
  return OTEL_INIT_CJS;
16076
16421
  }
16077
16422
  function injectionLine(flavor, entryFile, otelInitFile) {
16078
- let rel = import_node_path63.default.relative(import_node_path63.default.dirname(entryFile), otelInitFile);
16423
+ let rel = import_node_path64.default.relative(import_node_path64.default.dirname(entryFile), otelInitFile);
16079
16424
  if (!rel.startsWith(".")) rel = `./${rel}`;
16080
- rel = rel.split(import_node_path63.default.sep).join("/");
16425
+ rel = rel.split(import_node_path64.default.sep).join("/");
16081
16426
  if (flavor === "cjs") return `require('${rel}')`;
16082
16427
  if (flavor === "esm") return `import '${rel}'`;
16083
16428
  const tsRel = rel.replace(/\.ts$/, "");
@@ -16090,27 +16435,27 @@ function lineIsOtelInjection(line) {
16090
16435
  }
16091
16436
  async function detectsSrcLayout(serviceDir) {
16092
16437
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
16093
- exists3(import_node_path63.default.join(serviceDir, "src", "app")),
16094
- exists3(import_node_path63.default.join(serviceDir, "src", "pages")),
16095
- exists3(import_node_path63.default.join(serviceDir, "app")),
16096
- exists3(import_node_path63.default.join(serviceDir, "pages"))
16438
+ exists3(import_node_path64.default.join(serviceDir, "src", "app")),
16439
+ exists3(import_node_path64.default.join(serviceDir, "src", "pages")),
16440
+ exists3(import_node_path64.default.join(serviceDir, "app")),
16441
+ exists3(import_node_path64.default.join(serviceDir, "pages"))
16097
16442
  ]);
16098
16443
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
16099
16444
  }
16100
16445
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
16101
16446
  const useTs = await isTypeScriptProject(serviceDir);
16102
16447
  const srcLayout = await detectsSrcLayout(serviceDir);
16103
- const baseDir = srcLayout ? import_node_path63.default.join(serviceDir, "src") : serviceDir;
16104
- const instrumentationFile = import_node_path63.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
16105
- const instrumentationNodeFile = import_node_path63.default.join(
16448
+ const baseDir = srcLayout ? import_node_path64.default.join(serviceDir, "src") : serviceDir;
16449
+ const instrumentationFile = import_node_path64.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
16450
+ const instrumentationNodeFile = import_node_path64.default.join(
16106
16451
  baseDir,
16107
16452
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
16108
16453
  );
16109
- const instrumentationEdgeFile = import_node_path63.default.join(
16454
+ const instrumentationEdgeFile = import_node_path64.default.join(
16110
16455
  baseDir,
16111
16456
  useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
16112
16457
  );
16113
- const envNeatFile = import_node_path63.default.join(baseDir, ".env.neat");
16458
+ const envNeatFile = import_node_path64.default.join(baseDir, ".env.neat");
16114
16459
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
16115
16460
  const dependencyEdits = [];
16116
16461
  for (const sdk of SDK_PACKAGES) {
@@ -16235,7 +16580,7 @@ function buildDependencyEdits(pkg, manifestPath) {
16235
16580
  return edits;
16236
16581
  }
16237
16582
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
16238
- const envNeatFile = import_node_path63.default.join(serviceDir, ".env.neat");
16583
+ const envNeatFile = import_node_path64.default.join(serviceDir, ".env.neat");
16239
16584
  if (!await exists3(envNeatFile)) {
16240
16585
  generatedFiles.push({
16241
16586
  file: envNeatFile,
@@ -16270,7 +16615,7 @@ function fileImportsOtelHook(raw, specifiers) {
16270
16615
  }
16271
16616
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
16272
16617
  const useTs = await isTypeScriptProject(serviceDir);
16273
- const otelServerFile = import_node_path63.default.join(
16618
+ const otelServerFile = import_node_path64.default.join(
16274
16619
  serviceDir,
16275
16620
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
16276
16621
  );
@@ -16327,11 +16672,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
16327
16672
  }
16328
16673
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
16329
16674
  const useTs = await isTypeScriptProject(serviceDir);
16330
- const otelInitFile = import_node_path63.default.join(
16675
+ const otelInitFile = import_node_path64.default.join(
16331
16676
  serviceDir,
16332
16677
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
16333
16678
  );
16334
- const resolvedHooksFile = hooksFile ?? import_node_path63.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
16679
+ const resolvedHooksFile = hooksFile ?? import_node_path64.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
16335
16680
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
16336
16681
  const generatedFiles = [];
16337
16682
  const entrypointEdits = [];
@@ -16393,11 +16738,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
16393
16738
  }
16394
16739
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
16395
16740
  const useTs = await isTypeScriptProject(serviceDir);
16396
- const otelPluginFile = import_node_path63.default.join(
16741
+ const otelPluginFile = import_node_path64.default.join(
16397
16742
  serviceDir,
16398
16743
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
16399
16744
  );
16400
- const otelInitFile = import_node_path63.default.join(
16745
+ const otelInitFile = import_node_path64.default.join(
16401
16746
  serviceDir,
16402
16747
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
16403
16748
  );
@@ -16448,19 +16793,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
16448
16793
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
16449
16794
  async function findAstroMiddleware(serviceDir) {
16450
16795
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
16451
- const candidate = import_node_path63.default.join(serviceDir, rel);
16796
+ const candidate = import_node_path64.default.join(serviceDir, rel);
16452
16797
  if (await exists3(candidate)) return candidate;
16453
16798
  }
16454
16799
  return null;
16455
16800
  }
16456
16801
  async function planAstro(serviceDir, pkg, manifestPath, project) {
16457
16802
  const useTs = await isTypeScriptProject(serviceDir);
16458
- const otelInitFile = import_node_path63.default.join(
16803
+ const otelInitFile = import_node_path64.default.join(
16459
16804
  serviceDir,
16460
16805
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
16461
16806
  );
16462
16807
  const existingMiddleware = await findAstroMiddleware(serviceDir);
16463
- const middlewareFile = existingMiddleware ?? import_node_path63.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
16808
+ const middlewareFile = existingMiddleware ?? import_node_path64.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
16464
16809
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
16465
16810
  const generatedFiles = [];
16466
16811
  const entrypointEdits = [];
@@ -16556,7 +16901,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
16556
16901
  }
16557
16902
  async function plan(serviceDir, opts) {
16558
16903
  const pkg = await readPackageJson2(serviceDir);
16559
- const manifestPath = import_node_path63.default.join(serviceDir, "package.json");
16904
+ const manifestPath = import_node_path64.default.join(serviceDir, "package.json");
16560
16905
  const project = opts?.project;
16561
16906
  const empty = {
16562
16907
  language: "javascript",
@@ -16591,8 +16936,8 @@ async function plan(serviceDir, opts) {
16591
16936
  return { ...empty, libOnly: true };
16592
16937
  }
16593
16938
  const flavor = dispatchEntry(entryFile, pkg);
16594
- const otelInitFile = import_node_path63.default.join(import_node_path63.default.dirname(entryFile), otelInitFilename(flavor));
16595
- const envNeatFile = import_node_path63.default.join(serviceDir, ".env.neat");
16939
+ const otelInitFile = import_node_path64.default.join(import_node_path64.default.dirname(entryFile), otelInitFilename(flavor));
16940
+ const envNeatFile = import_node_path64.default.join(serviceDir, ".env.neat");
16596
16941
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
16597
16942
  const dependencyEdits = [];
16598
16943
  for (const sdk of SDK_PACKAGES) {
@@ -16661,13 +17006,13 @@ async function plan(serviceDir, opts) {
16661
17006
  };
16662
17007
  }
16663
17008
  function isAllowedWritePath(serviceDir, target) {
16664
- const rel = import_node_path63.default.relative(serviceDir, target);
17009
+ const rel = import_node_path64.default.relative(serviceDir, target);
16665
17010
  if (rel.startsWith("..")) return false;
16666
- const base = import_node_path63.default.basename(target);
17011
+ const base = import_node_path64.default.basename(target);
16667
17012
  if (base === "package.json") return true;
16668
17013
  if (base === ".env.neat") return true;
16669
17014
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
16670
- const relPosix = rel.split(import_node_path63.default.sep).join("/");
17015
+ const relPosix = rel.split(import_node_path64.default.sep).join("/");
16671
17016
  if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
16672
17017
  if (relPosix === base) return true;
16673
17018
  if (relPosix === `src/${base}`) return true;
@@ -16684,7 +17029,7 @@ function isAllowedWritePath(serviceDir, target) {
16684
17029
  return false;
16685
17030
  }
16686
17031
  async function writeAtomic(file, contents) {
16687
- await import_node_fs38.promises.mkdir(import_node_path63.default.dirname(file), { recursive: true });
17032
+ await import_node_fs38.promises.mkdir(import_node_path64.default.dirname(file), { recursive: true });
16688
17033
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
16689
17034
  await import_node_fs38.promises.writeFile(tmp, contents, "utf8");
16690
17035
  await import_node_fs38.promises.rename(tmp, file);
@@ -16868,7 +17213,7 @@ async function rollback(installPlan, originals, createdFiles) {
16868
17213
  ...removed.map((f) => `removed: ${f}`),
16869
17214
  ""
16870
17215
  ];
16871
- const rollbackPath = import_node_path63.default.join(installPlan.serviceDir, "neat-rollback.patch");
17216
+ const rollbackPath = import_node_path64.default.join(installPlan.serviceDir, "neat-rollback.patch");
16872
17217
  await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
16873
17218
  }
16874
17219
  function injectInstrumentationHook(raw) {
@@ -16899,7 +17244,7 @@ var javascriptInstaller = {
16899
17244
  // src/installers/python.ts
16900
17245
  init_cjs_shims();
16901
17246
  var import_node_fs39 = require("fs");
16902
- var import_node_path64 = __toESM(require("path"), 1);
17247
+ var import_node_path65 = __toESM(require("path"), 1);
16903
17248
  var SDK_PACKAGES2 = [
16904
17249
  { name: "opentelemetry-distro", version: ">=0.49b0" },
16905
17250
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -16997,7 +17342,7 @@ async function writeFileAtomic(file, contents) {
16997
17342
  await import_node_fs39.promises.rename(tmp, file);
16998
17343
  }
16999
17344
  async function resolvePyEntrypoint(serviceDir) {
17000
- const procfile = import_node_path64.default.join(serviceDir, "Procfile");
17345
+ const procfile = import_node_path65.default.join(serviceDir, "Procfile");
17001
17346
  if (await exists4(procfile)) {
17002
17347
  const raw = await import_node_fs39.promises.readFile(procfile, "utf8");
17003
17348
  for (const line of raw.split(/\r?\n/)) {
@@ -17007,20 +17352,20 @@ async function resolvePyEntrypoint(serviceDir) {
17007
17352
  const asgi = cmd.match(/\b(?:uvicorn|gunicorn|hypercorn|daphne)\s+([\w.]+):/);
17008
17353
  if (asgi) {
17009
17354
  const modPath = asgi[1].replace(/\./g, "/");
17010
- const asFile = import_node_path64.default.join(serviceDir, `${modPath}.py`);
17355
+ const asFile = import_node_path65.default.join(serviceDir, `${modPath}.py`);
17011
17356
  if (await exists4(asFile)) return asFile;
17012
- const asPkg = import_node_path64.default.join(serviceDir, modPath, "__init__.py");
17357
+ const asPkg = import_node_path65.default.join(serviceDir, modPath, "__init__.py");
17013
17358
  if (await exists4(asPkg)) return asPkg;
17014
17359
  }
17015
17360
  const runFile = cmd.match(/\b(?:python3?|fastapi\s+(?:run|dev))\s+([\w./-]+\.py)\b/);
17016
17361
  if (runFile) {
17017
- const p = import_node_path64.default.join(serviceDir, runFile[1]);
17362
+ const p = import_node_path65.default.join(serviceDir, runFile[1]);
17018
17363
  if (await exists4(p)) return p;
17019
17364
  }
17020
17365
  }
17021
17366
  }
17022
17367
  for (const name of ["main.py", "app.py", "asgi.py", "wsgi.py", "manage.py", "server.py"]) {
17023
- const p = import_node_path64.default.join(serviceDir, name);
17368
+ const p = import_node_path65.default.join(serviceDir, name);
17024
17369
  if (await exists4(p)) return p;
17025
17370
  }
17026
17371
  return null;
@@ -17046,7 +17391,7 @@ async function exists4(p) {
17046
17391
  async function detect2(serviceDir) {
17047
17392
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
17048
17393
  for (const m of markers) {
17049
- if (await exists4(import_node_path64.default.join(serviceDir, m))) return true;
17394
+ if (await exists4(import_node_path65.default.join(serviceDir, m))) return true;
17050
17395
  }
17051
17396
  return false;
17052
17397
  }
@@ -17056,7 +17401,7 @@ function reqPackageName(line) {
17056
17401
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
17057
17402
  }
17058
17403
  async function planRequirementsTxtEdits(serviceDir) {
17059
- const file = import_node_path64.default.join(serviceDir, "requirements.txt");
17404
+ const file = import_node_path65.default.join(serviceDir, "requirements.txt");
17060
17405
  if (!await exists4(file)) return null;
17061
17406
  const raw = await import_node_fs39.promises.readFile(file, "utf8");
17062
17407
  const presentNames = new Set(
@@ -17066,7 +17411,7 @@ async function planRequirementsTxtEdits(serviceDir) {
17066
17411
  return { manifest: file, missing: [...missing] };
17067
17412
  }
17068
17413
  async function planProcfileEdits(serviceDir) {
17069
- const procfile = import_node_path64.default.join(serviceDir, "Procfile");
17414
+ const procfile = import_node_path65.default.join(serviceDir, "Procfile");
17070
17415
  if (!await exists4(procfile)) return [];
17071
17416
  const raw = await import_node_fs39.promises.readFile(procfile, "utf8");
17072
17417
  const edits = [];
@@ -17104,7 +17449,7 @@ async function plan2(serviceDir) {
17104
17449
  }
17105
17450
  const entrypointEdits = await planProcfileEdits(serviceDir);
17106
17451
  const entryFile = await resolvePyEntrypoint(serviceDir);
17107
- const generatedFiles = entryFile ? [{ file: import_node_path64.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
17452
+ const generatedFiles = entryFile ? [{ file: import_node_path65.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
17108
17453
  if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
17109
17454
  return empty;
17110
17455
  }
@@ -17172,7 +17517,7 @@ async function apply2(installPlan) {
17172
17517
  if (raw === void 0) {
17173
17518
  throw new Error(`python installer: cannot read ${file} during apply`);
17174
17519
  }
17175
- const base = import_node_path64.default.basename(file);
17520
+ const base = import_node_path65.default.basename(file);
17176
17521
  if (base === "requirements.txt") {
17177
17522
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
17178
17523
  if (edits.length > 0) {
@@ -17227,7 +17572,7 @@ async function rollback2(installPlan, originals, createdFiles = []) {
17227
17572
  ...removed.map((f) => `removed: ${f}`),
17228
17573
  ""
17229
17574
  ];
17230
- const rollbackPath = import_node_path64.default.join(installPlan.serviceDir, "neat-rollback.patch");
17575
+ const rollbackPath = import_node_path65.default.join(installPlan.serviceDir, "neat-rollback.patch");
17231
17576
  await import_node_fs39.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
17232
17577
  }
17233
17578
  var pythonInstaller = {
@@ -17240,7 +17585,7 @@ var pythonInstaller = {
17240
17585
  // src/installers/go.ts
17241
17586
  init_cjs_shims();
17242
17587
  var import_node_fs40 = require("fs");
17243
- var import_node_path65 = __toESM(require("path"), 1);
17588
+ var import_node_path66 = __toESM(require("path"), 1);
17244
17589
  var GO_DEPS = [
17245
17590
  { name: "go.opentelemetry.io/otel", version: "v1.38.0" },
17246
17591
  { name: "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp", version: "v1.38.0" },
@@ -17250,13 +17595,13 @@ async function exists5(file) {
17250
17595
  return import_node_fs40.promises.stat(file).then(() => true, () => false);
17251
17596
  }
17252
17597
  async function findMain(serviceDir) {
17253
- const root = import_node_path65.default.join(serviceDir, "main.go");
17598
+ const root = import_node_path66.default.join(serviceDir, "main.go");
17254
17599
  if (await exists5(root)) return root;
17255
- const cmd = import_node_path65.default.join(serviceDir, "cmd");
17600
+ const cmd = import_node_path66.default.join(serviceDir, "cmd");
17256
17601
  const entries = await import_node_fs40.promises.readdir(cmd, { withFileTypes: true }).catch(() => []);
17257
17602
  for (const entry2 of entries.sort((a, b) => a.name.localeCompare(b.name))) {
17258
17603
  if (!entry2.isDirectory()) continue;
17259
- const candidate = import_node_path65.default.join(cmd, entry2.name, "main.go");
17604
+ const candidate = import_node_path66.default.join(cmd, entry2.name, "main.go");
17260
17605
  if (await exists5(candidate)) return candidate;
17261
17606
  }
17262
17607
  return null;
@@ -17331,17 +17676,17 @@ func init() {
17331
17676
  `;
17332
17677
  }
17333
17678
  async function detect3(serviceDir) {
17334
- return exists5(import_node_path65.default.join(serviceDir, "go.mod"));
17679
+ return exists5(import_node_path66.default.join(serviceDir, "go.mod"));
17335
17680
  }
17336
17681
  async function plan3(serviceDir) {
17337
- const manifest = import_node_path65.default.join(serviceDir, "go.mod");
17682
+ const manifest = import_node_path66.default.join(serviceDir, "go.mod");
17338
17683
  const raw = await import_node_fs40.promises.readFile(manifest, "utf8");
17339
17684
  const main2 = await findMain(serviceDir);
17340
17685
  const dependencyEdits = GO_DEPS.filter((dep) => !raw.includes(dep.name)).map((dep) => ({ file: manifest, kind: "add", ...dep }));
17341
17686
  if (!main2) return { language: "go", serviceDir, dependencyEdits: [], entrypointEdits: [], envEdits: [], libOnly: true };
17342
17687
  const source = await import_node_fs40.promises.readFile(main2, "utf8");
17343
17688
  const packageName = source.match(/^\s*package\s+(\w+)\s*$/m)?.[1] ?? "main";
17344
- const generated = import_node_path65.default.join(import_node_path65.default.dirname(main2), "neat_otel.go");
17689
+ const generated = import_node_path66.default.join(import_node_path66.default.dirname(main2), "neat_otel.go");
17345
17690
  const generatedFiles = await exists5(generated) ? [] : [{ file: generated, contents: neatOtelGo(packageName) }];
17346
17691
  return { language: "go", serviceDir, dependencyEdits, entrypointEdits: [], envEdits: [], generatedFiles, entryFile: main2 };
17347
17692
  }
@@ -17483,7 +17828,7 @@ init_cjs_shims();
17483
17828
  var import_node_fs41 = require("fs");
17484
17829
  var import_node_http = __toESM(require("http"), 1);
17485
17830
  var import_node_net = __toESM(require("net"), 1);
17486
- var import_node_path66 = __toESM(require("path"), 1);
17831
+ var import_node_path67 = __toESM(require("path"), 1);
17487
17832
  var import_node_url4 = require("url");
17488
17833
  var import_node_child_process3 = require("child_process");
17489
17834
  var import_node_readline = __toESM(require("readline"), 1);
@@ -17493,7 +17838,7 @@ async function extractAndPersist(opts) {
17493
17838
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
17494
17839
  resetGraph(graphKey);
17495
17840
  const graph = getGraph(graphKey);
17496
- const projectPaths = pathsForProject(graphKey, import_node_path66.default.join(opts.scanPath, "neat-out"));
17841
+ const projectPaths = pathsForProject(graphKey, import_node_path67.default.join(opts.scanPath, "neat-out"));
17497
17842
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
17498
17843
  errorsPath: projectPaths.errorsPath
17499
17844
  });
@@ -17545,7 +17890,7 @@ async function applyInstallersOver(services, project, options = {}) {
17545
17890
  libOnly++;
17546
17891
  const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
17547
17892
  if (appDeps.length > 0) {
17548
- const svcName = import_node_path66.default.basename(svc.dir);
17893
+ const svcName = import_node_path67.default.basename(svc.dir);
17549
17894
  const list = appDeps.join(", ");
17550
17895
  console.warn(
17551
17896
  `neat: runtime layer won't engage for ${svcName}: no entry point found.
@@ -17558,7 +17903,7 @@ async function applyInstallersOver(services, project, options = {}) {
17558
17903
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
17559
17904
  } else if (outcome.outcome === "react-native") {
17560
17905
  reactNative++;
17561
- const svcName = import_node_path66.default.basename(svc.dir);
17906
+ const svcName = import_node_path67.default.basename(svc.dir);
17562
17907
  console.log(
17563
17908
  `neat: ${svc.dir} detected as React Native / Expo
17564
17909
  The installer doesn't cover this runtime deterministically.
@@ -17569,7 +17914,7 @@ async function applyInstallersOver(services, project, options = {}) {
17569
17914
  );
17570
17915
  } else if (outcome.outcome === "bun") {
17571
17916
  bun++;
17572
- const svcName = import_node_path66.default.basename(svc.dir);
17917
+ const svcName = import_node_path67.default.basename(svc.dir);
17573
17918
  console.log(
17574
17919
  `neat: ${svc.dir} detected as Bun
17575
17920
  The installer doesn't cover this runtime deterministically.
@@ -17580,7 +17925,7 @@ async function applyInstallersOver(services, project, options = {}) {
17580
17925
  );
17581
17926
  } else if (outcome.outcome === "deno") {
17582
17927
  deno++;
17583
- const svcName = import_node_path66.default.basename(svc.dir);
17928
+ const svcName = import_node_path67.default.basename(svc.dir);
17584
17929
  console.log(
17585
17930
  `neat: ${svc.dir} detected as Deno
17586
17931
  The installer doesn't cover this runtime deterministically.
@@ -17591,7 +17936,7 @@ async function applyInstallersOver(services, project, options = {}) {
17591
17936
  );
17592
17937
  } else if (outcome.outcome === "cloudflare-workers") {
17593
17938
  cloudflareWorkers++;
17594
- const svcName = import_node_path66.default.basename(svc.dir);
17939
+ const svcName = import_node_path67.default.basename(svc.dir);
17595
17940
  console.log(
17596
17941
  `neat: ${svc.dir} detected as Cloudflare Workers
17597
17942
  The installer doesn't cover this runtime deterministically.
@@ -17602,7 +17947,7 @@ async function applyInstallersOver(services, project, options = {}) {
17602
17947
  );
17603
17948
  } else if (outcome.outcome === "electron") {
17604
17949
  electron++;
17605
- const svcName = import_node_path66.default.basename(svc.dir);
17950
+ const svcName = import_node_path67.default.basename(svc.dir);
17606
17951
  console.log(
17607
17952
  `neat: ${svc.dir} detected as Electron
17608
17953
  The installer doesn't cover this runtime deterministically.
@@ -17615,7 +17960,7 @@ async function applyInstallersOver(services, project, options = {}) {
17615
17960
  if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
17616
17961
  const gaps = uninstrumentedLibraries(svc.pkg);
17617
17962
  if (gaps.length > 0) {
17618
- const svcName = import_node_path66.default.basename(svc.dir);
17963
+ const svcName = import_node_path67.default.basename(svc.dir);
17619
17964
  const list = gaps.join(", ");
17620
17965
  const subject = gaps.length === 1 ? "this library" : "these libraries";
17621
17966
  const aux = gaps.length === 1 ? "isn't" : "aren't";
@@ -17821,8 +18166,8 @@ async function persistedPortsFor(scanPath) {
17821
18166
  return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
17822
18167
  }
17823
18168
  async function acquireSpawnLock(scanPath) {
17824
- const lockPath = import_node_path66.default.join(scanPath, "neat-out", "daemon.spawn.lock");
17825
- await import_node_fs41.promises.mkdir(import_node_path66.default.dirname(lockPath), { recursive: true });
18169
+ const lockPath = import_node_path67.default.join(scanPath, "neat-out", "daemon.spawn.lock");
18170
+ await import_node_fs41.promises.mkdir(import_node_path67.default.dirname(lockPath), { recursive: true });
17826
18171
  const STALE_LOCK_MS = 6e4;
17827
18172
  try {
17828
18173
  const fd = await import_node_fs41.promises.open(lockPath, "wx");
@@ -17867,13 +18212,13 @@ async function healthIsForProject(restPort, project) {
17867
18212
  return false;
17868
18213
  }
17869
18214
  function daemonLogPath(projectPath2) {
17870
- return import_node_path66.default.join(projectPath2, "neat-out", "daemon.log");
18215
+ return import_node_path67.default.join(projectPath2, "neat-out", "daemon.log");
17871
18216
  }
17872
18217
  function spawnDaemonDetached(spec) {
17873
- const here = import_node_path66.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
18218
+ const here = import_node_path67.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
17874
18219
  const candidates = [
17875
- import_node_path66.default.join(here, "neatd.cjs"),
17876
- import_node_path66.default.join(here, "neatd.js")
18220
+ import_node_path67.default.join(here, "neatd.cjs"),
18221
+ import_node_path67.default.join(here, "neatd.js")
17877
18222
  ];
17878
18223
  let entry2 = null;
17879
18224
  const fsSync = require("fs");
@@ -17903,7 +18248,7 @@ function spawnDaemonDetached(spec) {
17903
18248
  let logFd = null;
17904
18249
  if (spec) {
17905
18250
  const logPath = daemonLogPath(spec.projectPath);
17906
- fsSync.mkdirSync(import_node_path66.default.dirname(logPath), { recursive: true });
18251
+ fsSync.mkdirSync(import_node_path67.default.dirname(logPath), { recursive: true });
17907
18252
  logFd = fsSync.openSync(logPath, "a");
17908
18253
  }
17909
18254
  const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
@@ -18102,7 +18447,7 @@ async function runOrchestrator(opts) {
18102
18447
  result.steps.browser = openBrowser(dashboardUrl);
18103
18448
  }
18104
18449
  const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
18105
- const daemonLog = daemonRunning ? import_node_path66.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
18450
+ const daemonLog = daemonRunning ? import_node_path67.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
18106
18451
  printSummary(result, graph, dashboardUrl, daemonLog);
18107
18452
  return result;
18108
18453
  }
@@ -18561,7 +18906,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
18561
18906
 
18562
18907
  // src/hooks-cli.ts
18563
18908
  init_cjs_shims();
18564
- var import_node_path67 = __toESM(require("path"), 1);
18909
+ var import_node_path68 = __toESM(require("path"), 1);
18565
18910
  var import_node_os5 = __toESM(require("os"), 1);
18566
18911
  var import_node_fs42 = require("fs");
18567
18912
  var import_node_url5 = require("url");
@@ -18570,14 +18915,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
18570
18915
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
18571
18916
  var HOOK_MATCHER = "Grep|Glob|Bash";
18572
18917
  function moduleDir() {
18573
- return typeof __dirname !== "undefined" ? __dirname : import_node_path67.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
18918
+ return typeof __dirname !== "undefined" ? __dirname : import_node_path68.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
18574
18919
  }
18575
18920
  async function readSkillAsset(rel) {
18576
18921
  const here = moduleDir();
18577
18922
  const candidates = [
18578
- import_node_path67.default.resolve(here, "../../claude-skill", rel),
18579
- import_node_path67.default.resolve(here, "../../../claude-skill", rel),
18580
- import_node_path67.default.resolve(here, "../claude-skill", rel)
18923
+ import_node_path68.default.resolve(here, "../../claude-skill", rel),
18924
+ import_node_path68.default.resolve(here, "../../../claude-skill", rel),
18925
+ import_node_path68.default.resolve(here, "../claude-skill", rel)
18581
18926
  ];
18582
18927
  for (const candidate of candidates) {
18583
18928
  try {
@@ -18591,17 +18936,17 @@ async function readSkillAsset(rel) {
18591
18936
  }
18592
18937
  function neatHome3() {
18593
18938
  const override = process.env.NEAT_HOME;
18594
- if (override && override.length > 0) return import_node_path67.default.resolve(override);
18595
- return import_node_path67.default.join(import_node_os5.default.homedir(), ".neat");
18939
+ if (override && override.length > 0) return import_node_path68.default.resolve(override);
18940
+ return import_node_path68.default.join(import_node_os5.default.homedir(), ".neat");
18596
18941
  }
18597
18942
  function claudeSettingsPath() {
18598
18943
  const override = process.env.NEAT_CLAUDE_SETTINGS;
18599
- if (override && override.length > 0) return import_node_path67.default.resolve(override);
18944
+ if (override && override.length > 0) return import_node_path68.default.resolve(override);
18600
18945
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
18601
- return import_node_path67.default.join(home, ".claude", "settings.json");
18946
+ return import_node_path68.default.join(home, ".claude", "settings.json");
18602
18947
  }
18603
18948
  function installedHookPath() {
18604
- return import_node_path67.default.join(neatHome3(), "hooks", HOOK_FILENAME);
18949
+ return import_node_path68.default.join(neatHome3(), "hooks", HOOK_FILENAME);
18605
18950
  }
18606
18951
  function isNeatSearchEntry(entry2) {
18607
18952
  return (entry2.hooks ?? []).some(
@@ -18634,9 +18979,9 @@ async function runHooks(opts) {
18634
18979
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
18635
18980
  const guide = await readSkillAsset(GUIDE_FILENAME);
18636
18981
  const scriptPath = installedHookPath();
18637
- await import_node_fs42.promises.mkdir(import_node_path67.default.dirname(scriptPath), { recursive: true });
18982
+ await import_node_fs42.promises.mkdir(import_node_path68.default.dirname(scriptPath), { recursive: true });
18638
18983
  await import_node_fs42.promises.writeFile(scriptPath, hookScript, { mode: 493 });
18639
- const guidePath = import_node_path67.default.join(neatHome3(), GUIDE_INSTALL_NAME);
18984
+ const guidePath = import_node_path68.default.join(neatHome3(), GUIDE_INSTALL_NAME);
18640
18985
  await import_node_fs42.promises.writeFile(guidePath, guide, "utf8");
18641
18986
  const settingsFile = claudeSettingsPath();
18642
18987
  let settings = {};
@@ -18663,7 +19008,7 @@ async function runHooks(opts) {
18663
19008
  ...settings,
18664
19009
  hooks: { ...hooks, PreToolUse: preToolUse }
18665
19010
  };
18666
- await import_node_fs42.promises.mkdir(import_node_path67.default.dirname(settingsFile), { recursive: true });
19011
+ await import_node_fs42.promises.mkdir(import_node_path68.default.dirname(settingsFile), { recursive: true });
18667
19012
  await import_node_fs42.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
18668
19013
  console.log(`neat hooks: installed the search-nudge hook`);
18669
19014
  console.log(` script: ${scriptPath}`);
@@ -18735,11 +19080,11 @@ async function runHooksCommand(args) {
18735
19080
 
18736
19081
  // src/cli-verbs.ts
18737
19082
  init_cjs_shims();
18738
- var import_node_path68 = __toESM(require("path"), 1);
19083
+ var import_node_path69 = __toESM(require("path"), 1);
18739
19084
 
18740
19085
  // src/cli-client.ts
18741
19086
  init_cjs_shims();
18742
- var import_types59 = require("@neat.is/types");
19087
+ var import_types61 = require("@neat.is/types");
18743
19088
  var HttpError = class extends Error {
18744
19089
  constructor(status2, message, responseBody = "") {
18745
19090
  super(message);
@@ -18764,10 +19109,10 @@ function createHttpClient(baseUrl, bearerToken) {
18764
19109
  const root = baseUrl.replace(/\/$/, "");
18765
19110
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
18766
19111
  return {
18767
- async get(path70) {
19112
+ async get(path71) {
18768
19113
  let res;
18769
19114
  try {
18770
- res = await fetch(`${root}${path70}`, {
19115
+ res = await fetch(`${root}${path71}`, {
18771
19116
  headers: { ...authHeader }
18772
19117
  });
18773
19118
  } catch (err) {
@@ -18779,16 +19124,16 @@ function createHttpClient(baseUrl, bearerToken) {
18779
19124
  const body = await res.text().catch(() => "");
18780
19125
  throw new HttpError(
18781
19126
  res.status,
18782
- `${res.status} ${res.statusText} on GET ${path70}: ${body}`,
19127
+ `${res.status} ${res.statusText} on GET ${path71}: ${body}`,
18783
19128
  body
18784
19129
  );
18785
19130
  }
18786
19131
  return await res.json();
18787
19132
  },
18788
- async post(path70, body) {
19133
+ async post(path71, body) {
18789
19134
  let res;
18790
19135
  try {
18791
- res = await fetch(`${root}${path70}`, {
19136
+ res = await fetch(`${root}${path71}`, {
18792
19137
  method: "POST",
18793
19138
  headers: { "content-type": "application/json", ...authHeader },
18794
19139
  body: JSON.stringify(body)
@@ -18802,7 +19147,7 @@ function createHttpClient(baseUrl, bearerToken) {
18802
19147
  const text = await res.text().catch(() => "");
18803
19148
  throw new HttpError(
18804
19149
  res.status,
18805
- `${res.status} ${res.statusText} on POST ${path70}: ${text}`,
19150
+ `${res.status} ${res.statusText} on POST ${path71}: ${text}`,
18806
19151
  text
18807
19152
  );
18808
19153
  }
@@ -18816,12 +19161,12 @@ function projectPath(project, suffix) {
18816
19161
  }
18817
19162
  async function runRootCause(client, input) {
18818
19163
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
18819
- const path70 = projectPath(
19164
+ const path71 = projectPath(
18820
19165
  input.project,
18821
19166
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
18822
19167
  );
18823
19168
  try {
18824
- const result = await client.get(path70);
19169
+ const result = await client.get(path71);
18825
19170
  const arrowPath = result.traversalPath.join(" \u2190 ");
18826
19171
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
18827
19172
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -18847,12 +19192,12 @@ async function runRootCause(client, input) {
18847
19192
  }
18848
19193
  async function runBlastRadius(client, input) {
18849
19194
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
18850
- const path70 = projectPath(
19195
+ const path71 = projectPath(
18851
19196
  input.project,
18852
19197
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
18853
19198
  );
18854
19199
  try {
18855
- const result = await client.get(path70);
19200
+ const result = await client.get(path71);
18856
19201
  if (result.totalAffected === 0) {
18857
19202
  return {
18858
19203
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -18881,17 +19226,17 @@ async function runBlastRadius(client, input) {
18881
19226
  }
18882
19227
  }
18883
19228
  function formatBlastEntry(n) {
18884
- const tag = n.edgeProvenance === import_types59.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
19229
+ const tag = n.edgeProvenance === import_types61.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
18885
19230
  return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
18886
19231
  }
18887
19232
  async function runDependencies(client, input) {
18888
19233
  const depth = input.depth ?? 3;
18889
- const path70 = projectPath(
19234
+ const path71 = projectPath(
18890
19235
  input.project,
18891
19236
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
18892
19237
  );
18893
19238
  try {
18894
- const result = await client.get(path70);
19239
+ const result = await client.get(path71);
18895
19240
  if (result.total === 0) {
18896
19241
  return {
18897
19242
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -18938,7 +19283,7 @@ async function runObservedDependencies(client, input) {
18938
19283
  if (result.observed) {
18939
19284
  return {
18940
19285
  summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
18941
- provenance: import_types59.Provenance.OBSERVED
19286
+ provenance: import_types61.Provenance.OBSERVED
18942
19287
  };
18943
19288
  }
18944
19289
  const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
@@ -18948,7 +19293,7 @@ async function runObservedDependencies(client, input) {
18948
19293
  return {
18949
19294
  summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
18950
19295
  block: blockLines.join("\n"),
18951
- provenance: import_types59.Provenance.OBSERVED
19296
+ provenance: import_types61.Provenance.OBSERVED
18952
19297
  };
18953
19298
  } catch (err) {
18954
19299
  if (err instanceof HttpError && err.status === 404) {
@@ -18983,9 +19328,9 @@ function formatDuration(ms) {
18983
19328
  return `${Math.round(h / 24)}d`;
18984
19329
  }
18985
19330
  async function runIncidents(client, input) {
18986
- const path70 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
19331
+ const path71 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
18987
19332
  try {
18988
- const body = await client.get(path70);
19333
+ const body = await client.get(path71);
18989
19334
  const events = body.events;
18990
19335
  if (events.length === 0) {
18991
19336
  return {
@@ -19002,7 +19347,7 @@ async function runIncidents(client, input) {
19002
19347
  return {
19003
19348
  summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
19004
19349
  block: blockLines.join("\n"),
19005
- provenance: import_types59.Provenance.OBSERVED
19350
+ provenance: import_types61.Provenance.OBSERVED
19006
19351
  };
19007
19352
  } catch (err) {
19008
19353
  if (err instanceof HttpError && err.status === 404) {
@@ -19111,7 +19456,7 @@ async function runStaleEdges(client, input) {
19111
19456
  return {
19112
19457
  summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
19113
19458
  block: blockLines.join("\n"),
19114
- provenance: import_types59.Provenance.STALE
19459
+ provenance: import_types61.Provenance.STALE
19115
19460
  };
19116
19461
  }
19117
19462
  async function runPolicies(client, input) {
@@ -19179,6 +19524,9 @@ function formatDivergenceLine(d) {
19179
19524
  switch (d.type) {
19180
19525
  case "missing-observed":
19181
19526
  case "missing-extracted":
19527
+ if (d.column) {
19528
+ return ` \u2022 [${d.type}] ${d.table ?? d.source} column ${d.column} \u2014 confidence ${d.confidence.toFixed(2)}`;
19529
+ }
19182
19530
  return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
19183
19531
  case "version-mismatch":
19184
19532
  return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`;
@@ -19275,7 +19623,7 @@ async function resolveProjectEntry(opts) {
19275
19623
  const cwd = opts.cwd ?? process.cwd();
19276
19624
  const resolvedCwd = await normalizeProjectPath(cwd);
19277
19625
  for (const entry2 of entries) {
19278
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path68.default.sep}`)) {
19626
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path69.default.sep}`)) {
19279
19627
  return entry2;
19280
19628
  }
19281
19629
  }
@@ -19428,7 +19776,7 @@ async function runSync(opts) {
19428
19776
  }
19429
19777
 
19430
19778
  // src/cli.ts
19431
- var import_types60 = require("@neat.is/types");
19779
+ var import_types62 = require("@neat.is/types");
19432
19780
  function isNpxInvocation() {
19433
19781
  if (process.env.npm_command === "exec") return true;
19434
19782
  const execpath = process.env.npm_execpath ?? "";
@@ -19739,12 +20087,12 @@ async function runInit(opts) {
19739
20087
  printDiscoveryReport(opts, services);
19740
20088
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
19741
20089
  const patch = renderPatch(sections);
19742
- const patchPath = import_node_path69.default.join(opts.scanPath, "neat.patch");
20090
+ const patchPath = import_node_path70.default.join(opts.scanPath, "neat.patch");
19743
20091
  if (opts.dryRun) {
19744
20092
  await import_node_fs43.promises.writeFile(patchPath, patch, "utf8");
19745
20093
  written.push(patchPath);
19746
20094
  console.log(`dry-run: patch written to ${patchPath}`);
19747
- const gitignorePath = import_node_path69.default.join(opts.scanPath, ".gitignore");
20095
+ const gitignorePath = import_node_path70.default.join(opts.scanPath, ".gitignore");
19748
20096
  const gitignoreExists = await import_node_fs43.promises.stat(gitignorePath).then(() => true).catch(() => false);
19749
20097
  const verb = gitignoreExists ? "append" : "create";
19750
20098
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
@@ -19756,9 +20104,9 @@ async function runInit(opts) {
19756
20104
  const graph = getGraph(graphKey);
19757
20105
  const projectPaths = pathsForProject(
19758
20106
  graphKey,
19759
- import_node_path69.default.join(opts.scanPath, "neat-out")
20107
+ import_node_path70.default.join(opts.scanPath, "neat-out")
19760
20108
  );
19761
- const errorsPath = import_node_path69.default.join(import_node_path69.default.dirname(opts.outPath), import_node_path69.default.basename(projectPaths.errorsPath));
20109
+ const errorsPath = import_node_path70.default.join(import_node_path70.default.dirname(opts.outPath), import_node_path70.default.basename(projectPaths.errorsPath));
19762
20110
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
19763
20111
  await saveGraphToDisk(graph, opts.outPath);
19764
20112
  written.push(opts.outPath);
@@ -19877,9 +20225,9 @@ var CLAUDE_SKILL_CONFIG = {
19877
20225
  };
19878
20226
  function claudeConfigPath() {
19879
20227
  const override = process.env.NEAT_CLAUDE_CONFIG;
19880
- if (override && override.length > 0) return import_node_path69.default.resolve(override);
20228
+ if (override && override.length > 0) return import_node_path70.default.resolve(override);
19881
20229
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
19882
- return import_node_path69.default.join(home, ".claude.json");
20230
+ return import_node_path70.default.join(home, ".claude.json");
19883
20231
  }
19884
20232
  async function runSkill(opts) {
19885
20233
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -19903,7 +20251,7 @@ async function runSkill(opts) {
19903
20251
  ...existing,
19904
20252
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
19905
20253
  };
19906
- await import_node_fs43.promises.mkdir(import_node_path69.default.dirname(target), { recursive: true });
20254
+ await import_node_fs43.promises.mkdir(import_node_path70.default.dirname(target), { recursive: true });
19907
20255
  await import_node_fs43.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
19908
20256
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
19909
20257
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -19969,12 +20317,12 @@ async function main() {
19969
20317
  console.error("neat init: --apply and --dry-run are mutually exclusive");
19970
20318
  process.exit(2);
19971
20319
  }
19972
- const scanPath = import_node_path69.default.resolve(target);
20320
+ const scanPath = import_node_path70.default.resolve(target);
19973
20321
  const projectExplicit = parsed.project !== null;
19974
- const projectName = projectExplicit ? project : import_node_path69.default.basename(scanPath);
20322
+ const projectName = projectExplicit ? project : import_node_path70.default.basename(scanPath);
19975
20323
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
19976
- const fallback = pathsForProject(projectKey, import_node_path69.default.join(scanPath, "neat-out")).snapshotPath;
19977
- const outPath = import_node_path69.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
20324
+ const fallback = pathsForProject(projectKey, import_node_path70.default.join(scanPath, "neat-out")).snapshotPath;
20325
+ const outPath = import_node_path70.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
19978
20326
  const result = await runInit({
19979
20327
  scanPath,
19980
20328
  outPath,
@@ -19995,21 +20343,21 @@ async function main() {
19995
20343
  usage2();
19996
20344
  process.exit(2);
19997
20345
  }
19998
- const scanPath = import_node_path69.default.resolve(target);
20346
+ const scanPath = import_node_path70.default.resolve(target);
19999
20347
  const stat = await import_node_fs43.promises.stat(scanPath).catch(() => null);
20000
20348
  if (!stat || !stat.isDirectory()) {
20001
20349
  console.error(`neat watch: ${scanPath} is not a directory`);
20002
20350
  process.exit(2);
20003
20351
  }
20004
- const projectPaths = pathsForProject(project, import_node_path69.default.join(scanPath, "neat-out"));
20005
- const outPath = import_node_path69.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
20006
- const errorsPath = import_node_path69.default.resolve(
20007
- process.env.NEAT_ERRORS_PATH ?? import_node_path69.default.join(import_node_path69.default.dirname(outPath), import_node_path69.default.basename(projectPaths.errorsPath))
20352
+ const projectPaths = pathsForProject(project, import_node_path70.default.join(scanPath, "neat-out"));
20353
+ const outPath = import_node_path70.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
20354
+ const errorsPath = import_node_path70.default.resolve(
20355
+ process.env.NEAT_ERRORS_PATH ?? import_node_path70.default.join(import_node_path70.default.dirname(outPath), import_node_path70.default.basename(projectPaths.errorsPath))
20008
20356
  );
20009
- const staleEventsPath = import_node_path69.default.resolve(
20010
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path69.default.join(import_node_path69.default.dirname(outPath), import_node_path69.default.basename(projectPaths.staleEventsPath))
20357
+ const staleEventsPath = import_node_path70.default.resolve(
20358
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path70.default.join(import_node_path70.default.dirname(outPath), import_node_path70.default.basename(projectPaths.staleEventsPath))
20011
20359
  );
20012
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path69.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
20360
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path70.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
20013
20361
  const handle = await startWatch(getGraph(project), {
20014
20362
  scanPath,
20015
20363
  outPath,
@@ -20018,7 +20366,7 @@ async function main() {
20018
20366
  project,
20019
20367
  // Resolve NEAT_HOME so a `neat watch` picks up connectors added to
20020
20368
  // ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
20021
- neatHome: process.env.NEAT_HOME ? import_node_path69.default.resolve(process.env.NEAT_HOME) : import_node_path69.default.join(import_node_os6.default.homedir(), ".neat"),
20369
+ neatHome: process.env.NEAT_HOME ? import_node_path70.default.resolve(process.env.NEAT_HOME) : import_node_path70.default.join(import_node_os6.default.homedir(), ".neat"),
20022
20370
  ...embeddingsCachePath ? { embeddingsCachePath } : {},
20023
20371
  host: process.env.HOST ?? "0.0.0.0",
20024
20372
  port: Number(process.env.PORT ?? 8080),
@@ -20195,11 +20543,11 @@ async function main() {
20195
20543
  process.exit(1);
20196
20544
  }
20197
20545
  async function tryOrchestrator(cmd, parsed) {
20198
- const scanPath = import_node_path69.default.resolve(cmd);
20546
+ const scanPath = import_node_path70.default.resolve(cmd);
20199
20547
  const stat = await import_node_fs43.promises.stat(scanPath).catch(() => null);
20200
20548
  if (!stat || !stat.isDirectory()) return null;
20201
20549
  const projectExplicit = parsed.project !== null;
20202
- const projectName = projectExplicit ? parsed.project : import_node_path69.default.basename(scanPath);
20550
+ const projectName = projectExplicit ? parsed.project : import_node_path70.default.basename(scanPath);
20203
20551
  const result = await runOrchestrator({
20204
20552
  scanPath,
20205
20553
  project: projectName,
@@ -20388,10 +20736,10 @@ async function runQueryVerb(cmd, parsed) {
20388
20736
  const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
20389
20737
  const out = [];
20390
20738
  for (const p of parts) {
20391
- const r = import_types60.DivergenceTypeSchema.safeParse(p);
20739
+ const r = import_types62.DivergenceTypeSchema.safeParse(p);
20392
20740
  if (!r.success) {
20393
20741
  console.error(
20394
- `neat divergences: unknown --type "${p}". allowed: ${import_types60.DivergenceTypeSchema.options.join(", ")}`
20742
+ `neat divergences: unknown --type "${p}". allowed: ${import_types62.DivergenceTypeSchema.options.join(", ")}`
20395
20743
  );
20396
20744
  return 2;
20397
20745
  }