@neat.is/core 0.5.2 → 0.5.4-dev.20260721

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  mountBearerAuth,
3
3
  readAuthEnv
4
- } from "./chunk-CFDPIMRP.js";
4
+ } from "./chunk-I4NZ7PSN.js";
5
5
 
6
6
  // src/graph.ts
7
7
  import GraphDefault from "graphology";
@@ -456,19 +456,19 @@ function confidenceFromMix(edges, now = Date.now()) {
456
456
  function longestIncomingWalk(graph, start, maxDepth) {
457
457
  let best = { path: [start], edges: [] };
458
458
  const visited = /* @__PURE__ */ new Set([start]);
459
- function step(node, path47, edges) {
460
- if (path47.length > best.path.length) {
461
- best = { path: [...path47], edges: [...edges] };
459
+ function step(node, path48, edges) {
460
+ if (path48.length > best.path.length) {
461
+ best = { path: [...path48], edges: [...edges] };
462
462
  }
463
- if (path47.length - 1 >= maxDepth) return;
463
+ if (path48.length - 1 >= maxDepth) return;
464
464
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
465
465
  for (const [srcId, edge] of incoming) {
466
466
  if (visited.has(srcId)) continue;
467
467
  visited.add(srcId);
468
- path47.push(srcId);
468
+ path48.push(srcId);
469
469
  edges.push(edge);
470
- step(srcId, path47, edges);
471
- path47.pop();
470
+ step(srcId, path48, edges);
471
+ path48.pop();
472
472
  edges.pop();
473
473
  visited.delete(srcId);
474
474
  }
@@ -662,26 +662,26 @@ function dominantFailingCall(graph, serviceId5, visited) {
662
662
  return best;
663
663
  }
664
664
  function followFailingCallChain(graph, originServiceId, maxDepth) {
665
- const path47 = [originServiceId];
665
+ const path48 = [originServiceId];
666
666
  const edges = [];
667
667
  const visited = /* @__PURE__ */ new Set([originServiceId]);
668
668
  let current = originServiceId;
669
669
  for (let depth = 0; depth < maxDepth; depth++) {
670
670
  const hop = dominantFailingCall(graph, current, visited);
671
671
  if (!hop) break;
672
- path47.push(hop.nextService);
672
+ path48.push(hop.nextService);
673
673
  edges.push(hop.edge);
674
674
  visited.add(hop.nextService);
675
675
  current = hop.nextService;
676
676
  }
677
677
  if (edges.length === 0) return null;
678
- return { path: path47, edges, culprit: current };
678
+ return { path: path48, edges, culprit: current };
679
679
  }
680
680
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
681
681
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
682
682
  if (!chain) return null;
683
683
  const culprit = chain.culprit;
684
- const path47 = [...chain.path];
684
+ const path48 = [...chain.path];
685
685
  const edgeProvenances = chain.edges.map((e) => e.provenance);
686
686
  const baseConfidence = confidenceFromMix(chain.edges);
687
687
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -689,14 +689,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
689
689
  if (loc) {
690
690
  let rootCauseNode = culprit;
691
691
  if (loc.fileNode) {
692
- path47.push(loc.fileNode);
692
+ path48.push(loc.fileNode);
693
693
  edgeProvenances.push(Provenance.OBSERVED);
694
694
  rootCauseNode = loc.fileNode;
695
695
  }
696
696
  return RootCauseResultSchema.parse({
697
697
  rootCauseNode,
698
698
  rootCauseReason: loc.rootCauseReason,
699
- traversalPath: path47,
699
+ traversalPath: path48,
700
700
  edgeProvenances,
701
701
  confidence,
702
702
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -708,7 +708,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
708
708
  return RootCauseResultSchema.parse({
709
709
  rootCauseNode: culprit,
710
710
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
711
- traversalPath: path47,
711
+ traversalPath: path48,
712
712
  edgeProvenances,
713
713
  confidence,
714
714
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2229,6 +2229,18 @@ async function handleSpan(ctx, span) {
2229
2229
  callSiteEvidence
2230
2230
  );
2231
2231
  if (result) affectedNode = targetId;
2232
+ if (span.dbSystem === "mongodb" && span.dbCollection) {
2233
+ const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
2234
+ upsertObservedEdge(
2235
+ ctx.graph,
2236
+ EdgeType3.CALLS,
2237
+ observedSource(),
2238
+ collectionId,
2239
+ ts,
2240
+ isError,
2241
+ callSiteEvidence
2242
+ );
2243
+ }
2232
2244
  }
2233
2245
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
2234
2246
  const targetId = ensureMessagingDestinationNode(
@@ -3990,14 +4002,21 @@ function engineFromImage(image) {
3990
4002
  // src/extract/databases/dotenv.ts
3991
4003
  var CONNECTION_KEYS = /* @__PURE__ */ new Set([
3992
4004
  "DATABASE_URL",
4005
+ "DATABASE_URI",
3993
4006
  "DB_URL",
4007
+ "DB_URI",
3994
4008
  "POSTGRES_URL",
4009
+ "POSTGRES_URI",
3995
4010
  "POSTGRESQL_URL",
4011
+ "POSTGRESQL_URI",
3996
4012
  "MYSQL_URL",
4013
+ "MYSQL_URI",
4014
+ "MONGODB_URL",
3997
4015
  "MONGODB_URI",
3998
4016
  "MONGO_URL",
3999
4017
  "MONGO_URI",
4000
- "REDIS_URL"
4018
+ "REDIS_URL",
4019
+ "REDIS_URI"
4001
4020
  ]);
4002
4021
  function parseDotenvLine(line) {
4003
4022
  const trimmed = line.trim();
@@ -5819,6 +5838,301 @@ function supabaseEndpointsFromFile(file, serviceDir) {
5819
5838
  return out;
5820
5839
  }
5821
5840
 
5841
+ // src/extract/calls/mongoose.ts
5842
+ import path31 from "path";
5843
+ import { infraId as infraId7 } from "@neat.is/types";
5844
+ var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
5845
+ var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
5846
+ var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
5847
+ var UNCOUNTABLES = /* @__PURE__ */ new Set([
5848
+ "advice",
5849
+ "energy",
5850
+ "excretion",
5851
+ "digestion",
5852
+ "cooperation",
5853
+ "health",
5854
+ "justice",
5855
+ "labour",
5856
+ "machinery",
5857
+ "equipment",
5858
+ "information",
5859
+ "pollution",
5860
+ "sewage",
5861
+ "paper",
5862
+ "money",
5863
+ "species",
5864
+ "series",
5865
+ "rain",
5866
+ "rice",
5867
+ "fish",
5868
+ "sheep",
5869
+ "moose",
5870
+ "deer",
5871
+ "news",
5872
+ "expertise",
5873
+ "status",
5874
+ "media"
5875
+ ]);
5876
+ var PLURALIZE_RULES = [
5877
+ [/human$/gi, "humans"],
5878
+ [/(m)an$/gi, "$1en"],
5879
+ [/(pe)rson$/gi, "$1ople"],
5880
+ [/(child)$/gi, "$1ren"],
5881
+ [/^(ox)$/gi, "$1en"],
5882
+ [/(ax|test)is$/gi, "$1es"],
5883
+ [/(octop|vir)us$/gi, "$1i"],
5884
+ [/(alias|status)$/gi, "$1es"],
5885
+ [/(bu)s$/gi, "$1ses"],
5886
+ [/(buffal|tomat|potat)o$/gi, "$1oes"],
5887
+ [/([ti])um$/gi, "$1a"],
5888
+ [/sis$/gi, "ses"],
5889
+ [/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
5890
+ [/(hive)$/gi, "$1s"],
5891
+ [/([^aeiouy]|qu)y$/gi, "$1ies"],
5892
+ [/(x|ch|ss|sh)$/gi, "$1es"],
5893
+ [/(matr|vert|ind)ix|ex$/gi, "$1ices"],
5894
+ [/([m|l])ouse$/gi, "$1ice"],
5895
+ [/(kn|w|l)ife$/gi, "$1ives"],
5896
+ [/(quiz)$/gi, "$1zes"],
5897
+ [/s$/gi, "s"],
5898
+ [/([^a-z])$/, "$1"],
5899
+ [/$/gi, "s"]
5900
+ ];
5901
+ function pluralizeCollection(name) {
5902
+ const str = name.toLowerCase();
5903
+ if (UNCOUNTABLES.has(str)) return str;
5904
+ for (const [re, repl] of PLURALIZE_RULES) {
5905
+ if (str.match(re)) return str.replace(re, repl);
5906
+ }
5907
+ return str;
5908
+ }
5909
+ var MODEL_METHODS = [
5910
+ "find",
5911
+ "findOne",
5912
+ "findById",
5913
+ "findByIdAndUpdate",
5914
+ "findByIdAndDelete",
5915
+ "findOneAndUpdate",
5916
+ "findOneAndDelete",
5917
+ "findOneAndReplace",
5918
+ "countDocuments",
5919
+ "estimatedDocumentCount",
5920
+ "distinct",
5921
+ "aggregate",
5922
+ "exists",
5923
+ "where",
5924
+ "populate",
5925
+ "watch",
5926
+ "hydrate",
5927
+ "create",
5928
+ "insertMany",
5929
+ "insertOne",
5930
+ "updateOne",
5931
+ "updateMany",
5932
+ "replaceOne",
5933
+ "deleteOne",
5934
+ "deleteMany",
5935
+ "bulkWrite",
5936
+ "bulkSave",
5937
+ "save",
5938
+ // pre-v7, removed in current mongoose but present in older codebases:
5939
+ "count",
5940
+ "update",
5941
+ "remove",
5942
+ "findOneAndRemove",
5943
+ "findByIdAndRemove"
5944
+ ];
5945
+ var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
5946
+ function schemaCollectionVars(content) {
5947
+ const out = /* @__PURE__ */ new Map();
5948
+ const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
5949
+ let m;
5950
+ while ((m = re.exec(content)) !== null) {
5951
+ const windowText = content.slice(m.index, m.index + 2e3);
5952
+ const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
5953
+ if (opt) out.set(m[1], opt[1]);
5954
+ }
5955
+ return out;
5956
+ }
5957
+ function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
5958
+ const trimmed = rest.trim();
5959
+ const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
5960
+ if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
5961
+ const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
5962
+ if (firstIdent && schemaColl.has(firstIdent)) {
5963
+ return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
5964
+ }
5965
+ if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
5966
+ return { name: modelName, kind: "mongodb-model" };
5967
+ }
5968
+ return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
5969
+ }
5970
+ function collectModelDefs(content, pluralizeOn) {
5971
+ const schemaColl = schemaCollectionVars(content);
5972
+ const out = [];
5973
+ const re = /(?:(?:const|let|var)\s+(\w+)\s*=\s*)?(?:await\s+)?(?:\b\w+\s*\.\s*)?\bmodel\s*\(\s*['"`]([\w$]+)['"`]\s*(?:,\s*([\s\S]{0,300}?))?\)/g;
5974
+ let m;
5975
+ while ((m = re.exec(content)) !== null) {
5976
+ out.push({
5977
+ modelName: m[2],
5978
+ varName: m[1] ?? null,
5979
+ resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
5980
+ matchText: m[0]
5981
+ });
5982
+ }
5983
+ return out;
5984
+ }
5985
+ function endpoint(r, file, serviceDir, matchText) {
5986
+ const line = lineOf(file.content, matchText);
5987
+ return {
5988
+ infraId: infraId7(r.kind, r.name),
5989
+ name: r.name,
5990
+ kind: r.kind,
5991
+ edgeType: "CALLS",
5992
+ confidenceKind: "verified-call-site",
5993
+ evidence: { file: path31.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
5994
+ };
5995
+ }
5996
+ function mongooseEndpointsFromFile(file, serviceDir) {
5997
+ const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
5998
+ const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
5999
+ if (!hasMongoose && !hasMongodb) return [];
6000
+ const content = file.content;
6001
+ const out = [];
6002
+ const seen = /* @__PURE__ */ new Set();
6003
+ const push = (r, matchText) => {
6004
+ const key = `${r.kind}/${r.name}`;
6005
+ if (seen.has(key)) return;
6006
+ seen.add(key);
6007
+ out.push(endpoint(r, file, serviceDir, matchText));
6008
+ };
6009
+ if (hasMongoose) {
6010
+ const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
6011
+ for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
6012
+ }
6013
+ const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
6014
+ let c;
6015
+ while ((c = collRe.exec(content)) !== null) {
6016
+ push({ name: c[1], kind: "mongodb-collection" }, c[0]);
6017
+ }
6018
+ return out;
6019
+ }
6020
+ function parseDestructure(inner, specifier, out) {
6021
+ for (const raw of inner.split(",")) {
6022
+ const part = raw.trim();
6023
+ if (!part) continue;
6024
+ const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
6025
+ const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
6026
+ if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
6027
+ else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
6028
+ else {
6029
+ const id = /^(\w+)$/.exec(part);
6030
+ if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
6031
+ }
6032
+ }
6033
+ }
6034
+ function parseImportBindings(content) {
6035
+ const out = [];
6036
+ const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
6037
+ let m;
6038
+ while ((m = esm.exec(content)) !== null) {
6039
+ const clause = m[1].trim();
6040
+ const spec = m[2];
6041
+ const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
6042
+ if (ns) {
6043
+ out.push({ local: ns[1], kind: "namespace", specifier: spec });
6044
+ continue;
6045
+ }
6046
+ const named = /\{([^}]*)\}/.exec(clause);
6047
+ if (named) parseDestructure(named[1], spec, out);
6048
+ const def = /^(\w+)\s*(?:,|$)/.exec(clause);
6049
+ if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
6050
+ }
6051
+ const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
6052
+ while ((m = cjs.exec(content)) !== null) {
6053
+ const lhs = m[1];
6054
+ const spec = m[2];
6055
+ if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
6056
+ else out.push({ local: lhs, kind: "whole", specifier: spec });
6057
+ }
6058
+ return out;
6059
+ }
6060
+ function fileExportsOf(content, pluralizeOn) {
6061
+ const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
6062
+ if (defs.length === 0) return null;
6063
+ const byVar = /* @__PURE__ */ new Map();
6064
+ for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
6065
+ const byName = new Map(byVar);
6066
+ let def;
6067
+ const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
6068
+ if (named && byVar.has(named[1])) def = byVar.get(named[1]);
6069
+ if (!def) {
6070
+ const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
6071
+ if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
6072
+ }
6073
+ if (!def && byVar.size === 1) def = [...byVar.values()][0];
6074
+ return { byName, ...def ? { default: def } : {} };
6075
+ }
6076
+ async function mongooseCrossFileEndpoints(files, serviceDir) {
6077
+ const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
6078
+ if (mongooseFiles.length === 0) return [];
6079
+ const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
6080
+ const registry = /* @__PURE__ */ new Map();
6081
+ for (const f of mongooseFiles) {
6082
+ const fx = fileExportsOf(f.content, pluralizeOn);
6083
+ if (fx) registry.set(toPosix2(path31.relative(serviceDir, f.path)), fx);
6084
+ }
6085
+ if (registry.size === 0) return [];
6086
+ const out = [];
6087
+ const seen = /* @__PURE__ */ new Set();
6088
+ for (const f of files) {
6089
+ const bindings = parseImportBindings(f.content);
6090
+ if (bindings.length === 0) continue;
6091
+ const directColl = /* @__PURE__ */ new Map();
6092
+ const nsExports = /* @__PURE__ */ new Map();
6093
+ for (const b of bindings) {
6094
+ const resolvedRel = await resolveJsImport(b.specifier, path31.dirname(f.path), serviceDir, null);
6095
+ if (!resolvedRel) continue;
6096
+ const fx = registry.get(resolvedRel);
6097
+ if (!fx) continue;
6098
+ if (b.kind === "named" && b.exportName) {
6099
+ const coll = fx.byName.get(b.exportName);
6100
+ if (coll) directColl.set(b.local, coll);
6101
+ } else if (b.kind === "default") {
6102
+ if (fx.default) directColl.set(b.local, fx.default);
6103
+ } else {
6104
+ if (fx.default) directColl.set(b.local, fx.default);
6105
+ nsExports.set(b.local, fx);
6106
+ }
6107
+ }
6108
+ if (directColl.size === 0 && nsExports.size === 0) continue;
6109
+ const localDefs = new Set(
6110
+ collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
6111
+ );
6112
+ const emit = (collection, matchText) => {
6113
+ const key = `${f.path}::${collection}`;
6114
+ if (seen.has(key)) return;
6115
+ seen.add(key);
6116
+ out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
6117
+ };
6118
+ for (const [local, collection] of directColl) {
6119
+ if (localDefs.has(local)) continue;
6120
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
6121
+ let qm;
6122
+ while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
6123
+ }
6124
+ for (const [local, fx] of nsExports) {
6125
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
6126
+ let qm;
6127
+ while ((qm = qre.exec(f.content)) !== null) {
6128
+ const coll = fx.byName.get(qm[1]);
6129
+ if (coll) emit(coll, qm[0]);
6130
+ }
6131
+ }
6132
+ }
6133
+ return out;
6134
+ }
6135
+
5822
6136
  // src/extract/calls/index.ts
5823
6137
  function edgeTypeFromEndpoint(ep) {
5824
6138
  switch (ep.edgeType) {
@@ -5839,16 +6153,20 @@ async function addExternalEndpointEdges(graph, services) {
5839
6153
  for (const service of services) {
5840
6154
  const files = await loadSourceFiles(service.dir);
5841
6155
  const endpoints = [];
6156
+ const maskedFiles = [];
5842
6157
  for (const file of files) {
5843
6158
  if (isTestPath(file.path)) continue;
5844
6159
  const masked = maskCommentsInSource(file.content);
5845
6160
  const maskedFile = { path: file.path, content: masked };
6161
+ maskedFiles.push(maskedFile);
5846
6162
  endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
5847
6163
  endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
5848
6164
  endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
5849
6165
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
5850
6166
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
6167
+ endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
5851
6168
  }
6169
+ endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
5852
6170
  if (endpoints.length === 0) continue;
5853
6171
  const seenEdges = /* @__PURE__ */ new Set();
5854
6172
  for (const ep of endpoints) {
@@ -5919,14 +6237,14 @@ async function addCallEdges(graph, services) {
5919
6237
  }
5920
6238
 
5921
6239
  // src/extract/infra/docker-compose.ts
5922
- import path31 from "path";
6240
+ import path32 from "path";
5923
6241
  import { EdgeType as EdgeType13, Provenance as Provenance13, confidenceForExtracted as confidenceForExtracted11 } from "@neat.is/types";
5924
6242
 
5925
6243
  // src/extract/infra/shared.ts
5926
- import { NodeType as NodeType13, Provenance as Provenance12, confidenceForExtracted as confidenceForExtracted10, infraId as infraId7 } from "@neat.is/types";
6244
+ import { NodeType as NodeType13, Provenance as Provenance12, confidenceForExtracted as confidenceForExtracted10, infraId as infraId8 } from "@neat.is/types";
5927
6245
  function makeInfraNode(kind, name, provider = "self", extras) {
5928
6246
  return {
5929
- id: infraId7(kind, name),
6247
+ id: infraId8(kind, name),
5930
6248
  type: NodeType13.InfraNode,
5931
6249
  name,
5932
6250
  provider,
@@ -5989,7 +6307,7 @@ function dependsOnList(value) {
5989
6307
  }
5990
6308
  function serviceNameToServiceNode(name, services) {
5991
6309
  for (const s of services) {
5992
- if (s.node.name === name || path31.basename(s.dir) === name) return s.node.id;
6310
+ if (s.node.name === name || path32.basename(s.dir) === name) return s.node.id;
5993
6311
  }
5994
6312
  return null;
5995
6313
  }
@@ -5998,7 +6316,7 @@ async function addComposeInfra(graph, scanPath, services) {
5998
6316
  let edgesAdded = 0;
5999
6317
  let composePath = null;
6000
6318
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
6001
- const abs = path31.join(scanPath, name);
6319
+ const abs = path32.join(scanPath, name);
6002
6320
  if (await exists(abs)) {
6003
6321
  composePath = abs;
6004
6322
  break;
@@ -6011,13 +6329,13 @@ async function addComposeInfra(graph, scanPath, services) {
6011
6329
  } catch (err) {
6012
6330
  recordExtractionError(
6013
6331
  "infra docker-compose",
6014
- path31.relative(scanPath, composePath),
6332
+ path32.relative(scanPath, composePath),
6015
6333
  err
6016
6334
  );
6017
6335
  return { nodesAdded, edgesAdded };
6018
6336
  }
6019
6337
  if (!compose?.services) return { nodesAdded, edgesAdded };
6020
- const evidenceFile = path31.relative(scanPath, composePath).split(path31.sep).join("/");
6338
+ const evidenceFile = path32.relative(scanPath, composePath).split(path32.sep).join("/");
6021
6339
  const composeNameToNodeId = /* @__PURE__ */ new Map();
6022
6340
  for (const [composeName, svc] of Object.entries(compose.services)) {
6023
6341
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -6058,7 +6376,7 @@ async function addComposeInfra(graph, scanPath, services) {
6058
6376
  }
6059
6377
 
6060
6378
  // src/extract/infra/dockerfile.ts
6061
- import path32 from "path";
6379
+ import path33 from "path";
6062
6380
  import { promises as fs16 } from "fs";
6063
6381
  import { EdgeType as EdgeType14, Provenance as Provenance14, confidenceForExtracted as confidenceForExtracted12 } from "@neat.is/types";
6064
6382
  function readDockerfile(content) {
@@ -6089,7 +6407,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6089
6407
  let nodesAdded = 0;
6090
6408
  let edgesAdded = 0;
6091
6409
  for (const service of services) {
6092
- const dockerfilePath = path32.join(service.dir, "Dockerfile");
6410
+ const dockerfilePath = path33.join(service.dir, "Dockerfile");
6093
6411
  if (!await exists(dockerfilePath)) continue;
6094
6412
  let content;
6095
6413
  try {
@@ -6097,7 +6415,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6097
6415
  } catch (err) {
6098
6416
  recordExtractionError(
6099
6417
  "infra dockerfile",
6100
- path32.relative(scanPath, dockerfilePath),
6418
+ path33.relative(scanPath, dockerfilePath),
6101
6419
  err
6102
6420
  );
6103
6421
  continue;
@@ -6109,8 +6427,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6109
6427
  graph.addNode(node.id, node);
6110
6428
  nodesAdded++;
6111
6429
  }
6112
- const relDockerfile = toPosix2(path32.relative(service.dir, dockerfilePath));
6113
- const evidenceFile = toPosix2(path32.relative(scanPath, dockerfilePath));
6430
+ const relDockerfile = toPosix2(path33.relative(service.dir, dockerfilePath));
6431
+ const evidenceFile = toPosix2(path33.relative(scanPath, dockerfilePath));
6114
6432
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
6115
6433
  graph,
6116
6434
  service.pkg.name,
@@ -6162,7 +6480,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6162
6480
 
6163
6481
  // src/extract/infra/terraform.ts
6164
6482
  import { promises as fs17 } from "fs";
6165
- import path33 from "path";
6483
+ import path34 from "path";
6166
6484
  import { EdgeType as EdgeType15, Provenance as Provenance15, confidenceForExtracted as confidenceForExtracted13 } from "@neat.is/types";
6167
6485
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
6168
6486
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
@@ -6173,11 +6491,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
6173
6491
  for (const entry of entries) {
6174
6492
  if (entry.isDirectory()) {
6175
6493
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
6176
- const child = path33.join(start, entry.name);
6494
+ const child = path34.join(start, entry.name);
6177
6495
  if (await isPythonVenvDir(child)) continue;
6178
6496
  out.push(...await walkTfFiles(child, depth + 1, max));
6179
6497
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
6180
- out.push(path33.join(start, entry.name));
6498
+ out.push(path34.join(start, entry.name));
6181
6499
  }
6182
6500
  }
6183
6501
  return out;
@@ -6209,7 +6527,7 @@ async function addTerraformResources(graph, scanPath) {
6209
6527
  const files = await walkTfFiles(scanPath);
6210
6528
  for (const file of files) {
6211
6529
  const content = await fs17.readFile(file, "utf8");
6212
- const evidenceFile = toPosix2(path33.relative(scanPath, file));
6530
+ const evidenceFile = toPosix2(path34.relative(scanPath, file));
6213
6531
  const resources = [];
6214
6532
  const byKey = /* @__PURE__ */ new Map();
6215
6533
  RESOURCE_RE.lastIndex = 0;
@@ -6266,7 +6584,7 @@ async function addTerraformResources(graph, scanPath) {
6266
6584
 
6267
6585
  // src/extract/infra/k8s.ts
6268
6586
  import { promises as fs18 } from "fs";
6269
- import path34 from "path";
6587
+ import path35 from "path";
6270
6588
  import { parseAllDocuments as parseAllDocuments2 } from "yaml";
6271
6589
  var K8S_KIND_TO_INFRA_KIND = {
6272
6590
  Service: "k8s-service",
@@ -6284,11 +6602,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
6284
6602
  for (const entry of entries) {
6285
6603
  if (entry.isDirectory()) {
6286
6604
  if (IGNORED_DIRS.has(entry.name)) continue;
6287
- const child = path34.join(start, entry.name);
6605
+ const child = path35.join(start, entry.name);
6288
6606
  if (await isPythonVenvDir(child)) continue;
6289
6607
  out.push(...await walkYamlFiles2(child, depth + 1, max));
6290
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path34.extname(entry.name))) {
6291
- out.push(path34.join(start, entry.name));
6608
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path35.extname(entry.name))) {
6609
+ out.push(path35.join(start, entry.name));
6292
6610
  }
6293
6611
  }
6294
6612
  return out;
@@ -6321,13 +6639,13 @@ async function addK8sResources(graph, scanPath) {
6321
6639
 
6322
6640
  // src/extract/infra/cloudflare.ts
6323
6641
  import { promises as fs19 } from "fs";
6324
- import path35 from "path";
6642
+ import path36 from "path";
6325
6643
  import { parse as parseToml2 } from "smol-toml";
6326
6644
  import { EdgeType as EdgeType16, Provenance as Provenance16, confidenceForExtracted as confidenceForExtracted14 } from "@neat.is/types";
6327
6645
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
6328
6646
  async function readWranglerConfig(dir) {
6329
6647
  for (const filename of WRANGLER_FILENAMES) {
6330
- const abs = path35.join(dir, filename);
6648
+ const abs = path36.join(dir, filename);
6331
6649
  if (!await exists(abs)) continue;
6332
6650
  const raw = await fs19.readFile(abs, "utf8");
6333
6651
  const config = filename === "wrangler.toml" ? parseToml2(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -6390,11 +6708,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
6390
6708
  try {
6391
6709
  read = await readWranglerConfig(service.dir);
6392
6710
  } catch (err) {
6393
- recordExtractionError("infra cloudflare", path35.relative(scanPath, service.dir), err);
6711
+ recordExtractionError("infra cloudflare", path36.relative(scanPath, service.dir), err);
6394
6712
  continue;
6395
6713
  }
6396
6714
  if (!read || !read.config.name) continue;
6397
- const evidenceFile = toPosix2(path35.relative(scanPath, path35.join(service.dir, read.relFile)));
6715
+ const evidenceFile = toPosix2(path36.relative(scanPath, path36.join(service.dir, read.relFile)));
6398
6716
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
6399
6717
  }
6400
6718
  for (const worker of discovered) {
@@ -6406,7 +6724,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
6406
6724
  }
6407
6725
  let anchorId = service.node.id;
6408
6726
  if (config.main) {
6409
- const entryRelPath = toPosix2(path35.normalize(config.main));
6727
+ const entryRelPath = toPosix2(path36.normalize(config.main));
6410
6728
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
6411
6729
  graph,
6412
6730
  service.pkg.name,
@@ -6553,12 +6871,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
6553
6871
 
6554
6872
  // src/extract/infra/vercel.ts
6555
6873
  import { promises as fs20 } from "fs";
6556
- import path36 from "path";
6874
+ import path37 from "path";
6557
6875
  import { EdgeType as EdgeType17 } from "@neat.is/types";
6558
6876
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
6559
6877
  async function readVercelConfig(dir) {
6560
6878
  for (const filename of VERCEL_CONFIG_FILENAMES) {
6561
- const abs = path36.join(dir, filename);
6879
+ const abs = path37.join(dir, filename);
6562
6880
  if (!await exists(abs)) continue;
6563
6881
  const raw = await fs20.readFile(abs, "utf8");
6564
6882
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -6567,7 +6885,7 @@ async function readVercelConfig(dir) {
6567
6885
  return null;
6568
6886
  }
6569
6887
  async function readLinkedProjectName(dir) {
6570
- const abs = path36.join(dir, ".vercel", "project.json");
6888
+ const abs = path37.join(dir, ".vercel", "project.json");
6571
6889
  if (!await exists(abs)) return void 0;
6572
6890
  const parsed = JSON.parse(await fs20.readFile(abs, "utf8"));
6573
6891
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -6585,7 +6903,7 @@ async function addVercelServices(graph, services, scanPath) {
6585
6903
  read = await readVercelConfig(service.dir);
6586
6904
  projectName = await readLinkedProjectName(service.dir);
6587
6905
  } catch (err) {
6588
- recordExtractionError("infra vercel", path36.relative(scanPath, service.dir), err);
6906
+ recordExtractionError("infra vercel", path37.relative(scanPath, service.dir), err);
6589
6907
  continue;
6590
6908
  }
6591
6909
  if (!read && !projectName) continue;
@@ -6601,7 +6919,7 @@ async function addVercelServices(graph, services, scanPath) {
6601
6919
  const anchorId = service.node.id;
6602
6920
  if (!read) continue;
6603
6921
  const { config, relFile, raw } = read;
6604
- const evidenceFile = toPosix2(path36.relative(scanPath, path36.join(service.dir, relFile)));
6922
+ const evidenceFile = toPosix2(path37.relative(scanPath, path37.join(service.dir, relFile)));
6605
6923
  const add = (edgeType, kind, name) => {
6606
6924
  if (!name) return;
6607
6925
  const result = emitPlatformResourceEdge(
@@ -6630,13 +6948,13 @@ async function addVercelServices(graph, services, scanPath) {
6630
6948
 
6631
6949
  // src/extract/infra/railway.ts
6632
6950
  import { promises as fs21 } from "fs";
6633
- import path37 from "path";
6951
+ import path38 from "path";
6634
6952
  import { parse as parseToml3 } from "smol-toml";
6635
6953
  import { EdgeType as EdgeType18 } from "@neat.is/types";
6636
6954
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
6637
6955
  async function readRailwayConfig(dir) {
6638
6956
  for (const filename of RAILWAY_FILENAMES) {
6639
- const abs = path37.join(dir, filename);
6957
+ const abs = path38.join(dir, filename);
6640
6958
  if (!await exists(abs)) continue;
6641
6959
  const raw = await fs21.readFile(abs, "utf8");
6642
6960
  const config = filename === "railway.toml" ? parseToml3(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -6652,7 +6970,7 @@ async function addRailwayServices(graph, services, scanPath) {
6652
6970
  try {
6653
6971
  read = await readRailwayConfig(service.dir);
6654
6972
  } catch (err) {
6655
- recordExtractionError("infra railway", path37.relative(scanPath, service.dir), err);
6973
+ recordExtractionError("infra railway", path38.relative(scanPath, service.dir), err);
6656
6974
  continue;
6657
6975
  }
6658
6976
  if (!read) continue;
@@ -6662,7 +6980,7 @@ async function addRailwayServices(graph, services, scanPath) {
6662
6980
  }
6663
6981
  const anchorId = service.node.id;
6664
6982
  const { config, relFile, raw } = read;
6665
- const evidenceFile = toPosix2(path37.relative(scanPath, path37.join(service.dir, relFile)));
6983
+ const evidenceFile = toPosix2(path38.relative(scanPath, path38.join(service.dir, relFile)));
6666
6984
  const add = (edgeType, kind, name) => {
6667
6985
  if (!name) return;
6668
6986
  const result = emitPlatformResourceEdge(
@@ -6687,12 +7005,12 @@ async function addRailwayServices(graph, services, scanPath) {
6687
7005
 
6688
7006
  // src/extract/infra/supabase.ts
6689
7007
  import { promises as fs22 } from "fs";
6690
- import path38 from "path";
7008
+ import path39 from "path";
6691
7009
  import { parse as parseToml4 } from "smol-toml";
6692
7010
  import { EdgeType as EdgeType19 } from "@neat.is/types";
6693
7011
  async function readSupabaseConfig(dir) {
6694
- const relFile = path38.join("supabase", "config.toml");
6695
- const abs = path38.join(dir, relFile);
7012
+ const relFile = path39.join("supabase", "config.toml");
7013
+ const abs = path39.join(dir, relFile);
6696
7014
  if (!await exists(abs)) return null;
6697
7015
  const raw = await fs22.readFile(abs, "utf8");
6698
7016
  const config = parseToml4(raw);
@@ -6706,7 +7024,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
6706
7024
  try {
6707
7025
  read = await readSupabaseConfig(service.dir);
6708
7026
  } catch (err) {
6709
- recordExtractionError("infra supabase", path38.relative(scanPath, service.dir), err);
7027
+ recordExtractionError("infra supabase", path39.relative(scanPath, service.dir), err);
6710
7028
  continue;
6711
7029
  }
6712
7030
  if (!read) continue;
@@ -6721,7 +7039,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
6721
7039
  });
6722
7040
  }
6723
7041
  const anchorId = service.node.id;
6724
- const evidenceFile = toPosix2(path38.relative(scanPath, path38.join(service.dir, relFile)));
7042
+ const evidenceFile = toPosix2(path39.relative(scanPath, path39.join(service.dir, relFile)));
6725
7043
  const add = (edgeType, kind, name) => {
6726
7044
  if (!name) return;
6727
7045
  const result = emitPlatformResourceEdge(
@@ -6762,11 +7080,11 @@ async function addInfra(graph, scanPath, services) {
6762
7080
  }
6763
7081
 
6764
7082
  // src/extract/index.ts
6765
- import path40 from "path";
7083
+ import path41 from "path";
6766
7084
 
6767
7085
  // src/extract/retire.ts
6768
7086
  import { existsSync as existsSync2 } from "fs";
6769
- import path39 from "path";
7087
+ import path40 from "path";
6770
7088
  import { NodeType as NodeType14, Provenance as Provenance17 } from "@neat.is/types";
6771
7089
  function dropOrphanedFileNodes(graph) {
6772
7090
  const orphans = [];
@@ -6800,11 +7118,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
6800
7118
  if (edge.provenance !== Provenance17.EXTRACTED) return;
6801
7119
  const evidenceFile = edge.evidence?.file;
6802
7120
  if (!evidenceFile) return;
6803
- if (path39.isAbsolute(evidenceFile)) {
7121
+ if (path40.isAbsolute(evidenceFile)) {
6804
7122
  if (!existsSync2(evidenceFile)) toDrop.push(id);
6805
7123
  return;
6806
7124
  }
6807
- const found = bases.some((base) => existsSync2(path39.join(base, evidenceFile)));
7125
+ const found = bases.some((base) => existsSync2(path40.join(base, evidenceFile)));
6808
7126
  if (!found) toDrop.push(id);
6809
7127
  });
6810
7128
  for (const id of toDrop) graph.dropEdge(id);
@@ -6846,7 +7164,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
6846
7164
  }
6847
7165
  const droppedEntries = drainDroppedExtracted();
6848
7166
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
6849
- const rejectedPath = path40.join(path40.dirname(opts.errorsPath), "rejected.ndjson");
7167
+ const rejectedPath = path41.join(path41.dirname(opts.errorsPath), "rejected.ndjson");
6850
7168
  try {
6851
7169
  await writeRejectedExtracted(droppedEntries, rejectedPath);
6852
7170
  } catch (err) {
@@ -7164,7 +7482,7 @@ function computeDivergences(graph, opts = {}) {
7164
7482
 
7165
7483
  // src/persist.ts
7166
7484
  import { promises as fs23 } from "fs";
7167
- import path41 from "path";
7485
+ import path42 from "path";
7168
7486
  import { Provenance as Provenance19, observedEdgeId as observedEdgeId2 } from "@neat.is/types";
7169
7487
  var SCHEMA_VERSION = 4;
7170
7488
  function migrateV1ToV2(payload) {
@@ -7201,7 +7519,7 @@ function migrateV2ToV3(payload) {
7201
7519
  return { ...payload, schemaVersion: 3 };
7202
7520
  }
7203
7521
  async function ensureDir(filePath) {
7204
- await fs23.mkdir(path41.dirname(filePath), { recursive: true });
7522
+ await fs23.mkdir(path42.dirname(filePath), { recursive: true });
7205
7523
  }
7206
7524
  async function saveGraphToDisk(graph, outPath) {
7207
7525
  await ensureDir(outPath);
@@ -7357,23 +7675,23 @@ function canonicalJson(value) {
7357
7675
  }
7358
7676
 
7359
7677
  // src/projects.ts
7360
- import path42 from "path";
7678
+ import path43 from "path";
7361
7679
  function pathsForProject(project, baseDir) {
7362
7680
  if (project === DEFAULT_PROJECT) {
7363
7681
  return {
7364
- snapshotPath: path42.join(baseDir, "graph.json"),
7365
- errorsPath: path42.join(baseDir, "errors.ndjson"),
7366
- staleEventsPath: path42.join(baseDir, "stale-events.ndjson"),
7367
- embeddingsCachePath: path42.join(baseDir, "embeddings.json"),
7368
- policyViolationsPath: path42.join(baseDir, "policy-violations.ndjson")
7682
+ snapshotPath: path43.join(baseDir, "graph.json"),
7683
+ errorsPath: path43.join(baseDir, "errors.ndjson"),
7684
+ staleEventsPath: path43.join(baseDir, "stale-events.ndjson"),
7685
+ embeddingsCachePath: path43.join(baseDir, "embeddings.json"),
7686
+ policyViolationsPath: path43.join(baseDir, "policy-violations.ndjson")
7369
7687
  };
7370
7688
  }
7371
7689
  return {
7372
- snapshotPath: path42.join(baseDir, `${project}.json`),
7373
- errorsPath: path42.join(baseDir, `errors.${project}.ndjson`),
7374
- staleEventsPath: path42.join(baseDir, `stale-events.${project}.ndjson`),
7375
- embeddingsCachePath: path42.join(baseDir, `embeddings.${project}.json`),
7376
- policyViolationsPath: path42.join(baseDir, `policy-violations.${project}.ndjson`)
7690
+ snapshotPath: path43.join(baseDir, `${project}.json`),
7691
+ errorsPath: path43.join(baseDir, `errors.${project}.ndjson`),
7692
+ staleEventsPath: path43.join(baseDir, `stale-events.${project}.ndjson`),
7693
+ embeddingsCachePath: path43.join(baseDir, `embeddings.${project}.json`),
7694
+ policyViolationsPath: path43.join(baseDir, `policy-violations.${project}.ndjson`)
7377
7695
  };
7378
7696
  }
7379
7697
  var Projects = class {
@@ -7414,7 +7732,7 @@ function parseExtraProjects(raw) {
7414
7732
  // src/registry.ts
7415
7733
  import { promises as fs25 } from "fs";
7416
7734
  import os2 from "os";
7417
- import path43 from "path";
7735
+ import path44 from "path";
7418
7736
  import {
7419
7737
  RegistryFileSchema
7420
7738
  } from "@neat.is/types";
@@ -7422,20 +7740,20 @@ var LOCK_TIMEOUT_MS = 5e3;
7422
7740
  var LOCK_RETRY_MS = 50;
7423
7741
  function neatHome() {
7424
7742
  const override = process.env.NEAT_HOME;
7425
- if (override && override.length > 0) return path43.resolve(override);
7426
- return path43.join(os2.homedir(), ".neat");
7743
+ if (override && override.length > 0) return path44.resolve(override);
7744
+ return path44.join(os2.homedir(), ".neat");
7427
7745
  }
7428
7746
  function registryPath() {
7429
- return path43.join(neatHome(), "projects.json");
7747
+ return path44.join(neatHome(), "projects.json");
7430
7748
  }
7431
7749
  function registryLockPath() {
7432
- return path43.join(neatHome(), "projects.json.lock");
7750
+ return path44.join(neatHome(), "projects.json.lock");
7433
7751
  }
7434
7752
  function daemonPidPath() {
7435
- return path43.join(neatHome(), "neatd.pid");
7753
+ return path44.join(neatHome(), "neatd.pid");
7436
7754
  }
7437
7755
  function daemonsDir() {
7438
- return path43.join(neatHome(), "daemons");
7756
+ return path44.join(neatHome(), "daemons");
7439
7757
  }
7440
7758
  function isFiniteInt(v) {
7441
7759
  return typeof v === "number" && Number.isFinite(v);
@@ -7476,7 +7794,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
7476
7794
  const out = [];
7477
7795
  for (const name of names) {
7478
7796
  if (!name.endsWith(".json")) continue;
7479
- const file = path43.join(dir, name);
7797
+ const file = path44.join(dir, name);
7480
7798
  let raw;
7481
7799
  try {
7482
7800
  raw = await fs25.readFile(file, "utf8");
@@ -7597,7 +7915,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
7597
7915
  }
7598
7916
  }
7599
7917
  async function normalizeProjectPath(input) {
7600
- const resolved = path43.resolve(input);
7918
+ const resolved = path44.resolve(input);
7601
7919
  try {
7602
7920
  return await fs25.realpath(resolved);
7603
7921
  } catch {
@@ -7605,7 +7923,7 @@ async function normalizeProjectPath(input) {
7605
7923
  }
7606
7924
  }
7607
7925
  async function writeAtomically(target, contents) {
7608
- await fs25.mkdir(path43.dirname(target), { recursive: true });
7926
+ await fs25.mkdir(path44.dirname(target), { recursive: true });
7609
7927
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
7610
7928
  const fd = await fs25.open(tmp, "w");
7611
7929
  try {
@@ -7618,7 +7936,7 @@ async function writeAtomically(target, contents) {
7618
7936
  }
7619
7937
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
7620
7938
  const deadline = Date.now() + timeoutMs;
7621
- await fs25.mkdir(path43.dirname(lockPath), { recursive: true });
7939
+ await fs25.mkdir(path44.dirname(lockPath), { recursive: true });
7622
7940
  let probedHolder = false;
7623
7941
  while (true) {
7624
7942
  try {
@@ -7814,13 +8132,13 @@ import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } f
7814
8132
 
7815
8133
  // src/extend/index.ts
7816
8134
  import { promises as fs27 } from "fs";
7817
- import path45 from "path";
8135
+ import path46 from "path";
7818
8136
  import os3 from "os";
7819
8137
  import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
7820
8138
 
7821
8139
  // src/installers/package-manager.ts
7822
8140
  import { promises as fs26 } from "fs";
7823
- import path44 from "path";
8141
+ import path45 from "path";
7824
8142
  import { spawn } from "child_process";
7825
8143
  var LOCKFILE_PRIORITY = [
7826
8144
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -7842,22 +8160,22 @@ async function exists2(p) {
7842
8160
  }
7843
8161
  }
7844
8162
  async function detectPackageManager(serviceDir) {
7845
- let dir = path44.resolve(serviceDir);
8163
+ let dir = path45.resolve(serviceDir);
7846
8164
  const stops = /* @__PURE__ */ new Set();
7847
8165
  for (let i = 0; i < 64; i++) {
7848
8166
  if (stops.has(dir)) break;
7849
8167
  stops.add(dir);
7850
8168
  for (const candidate of LOCKFILE_PRIORITY) {
7851
- const lockPath = path44.join(dir, candidate.lockfile);
8169
+ const lockPath = path45.join(dir, candidate.lockfile);
7852
8170
  if (await exists2(lockPath)) {
7853
8171
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
7854
8172
  }
7855
8173
  }
7856
- const parent = path44.dirname(dir);
8174
+ const parent = path45.dirname(dir);
7857
8175
  if (parent === dir) break;
7858
8176
  dir = parent;
7859
8177
  }
7860
- return { pm: "npm", cwd: path44.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8178
+ return { pm: "npm", cwd: path45.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
7861
8179
  }
7862
8180
  async function runPackageManagerInstall(cmd) {
7863
8181
  return new Promise((resolve) => {
@@ -7906,22 +8224,53 @@ async function fileExists2(p) {
7906
8224
  }
7907
8225
  }
7908
8226
  async function readPackageJson(scanPath) {
7909
- const pkgPath = path45.join(scanPath, "package.json");
8227
+ const pkgPath = path46.join(scanPath, "package.json");
7910
8228
  const raw = await fs27.readFile(pkgPath, "utf8");
7911
8229
  return JSON.parse(raw);
7912
8230
  }
8231
+ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
8232
+ "node_modules",
8233
+ "dist",
8234
+ "build",
8235
+ "out",
8236
+ "coverage",
8237
+ "neat-out"
8238
+ ]);
7913
8239
  async function findHookFiles(scanPath) {
7914
- const entries = await fs27.readdir(scanPath);
7915
- return entries.filter(
7916
- (e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
7917
- ).sort();
8240
+ const found = [];
8241
+ const walk3 = async (dir) => {
8242
+ const entries = await fs27.readdir(dir, { withFileTypes: true }).catch(() => []);
8243
+ for (const entry of entries) {
8244
+ if (entry.isDirectory()) {
8245
+ if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
8246
+ await walk3(path46.join(dir, entry.name));
8247
+ } else if (entry.isFile()) {
8248
+ if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
8249
+ const rel = path46.relative(scanPath, path46.join(dir, entry.name));
8250
+ found.push(rel.split(path46.sep).join("/"));
8251
+ }
8252
+ }
8253
+ }
8254
+ };
8255
+ await walk3(scanPath);
8256
+ return found.sort();
8257
+ }
8258
+ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8259
+ let fallback = null;
8260
+ for (const file of hookFiles) {
8261
+ const content = await fs27.readFile(path46.join(scanPath, file), "utf8");
8262
+ const patched = splicedContent(content, snippet2);
8263
+ if (patched !== null) return { file, content, patched };
8264
+ if (fallback === null) fallback = { file, content };
8265
+ }
8266
+ return { file: fallback.file, content: fallback.content, patched: null };
7918
8267
  }
7919
8268
  function extendLogPath() {
7920
- return process.env.NEAT_EXTEND_LOG ?? path45.join(os3.homedir(), ".neat", "extend-log.ndjson");
8269
+ return process.env.NEAT_EXTEND_LOG ?? path46.join(os3.homedir(), ".neat", "extend-log.ndjson");
7921
8270
  }
7922
8271
  async function appendExtendLog(entry) {
7923
8272
  const logPath = extendLogPath();
7924
- await fs27.mkdir(path45.dirname(logPath), { recursive: true });
8273
+ await fs27.mkdir(path46.dirname(logPath), { recursive: true });
7925
8274
  await fs27.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
7926
8275
  }
7927
8276
  function splicedContent(fileContent, snippet2) {
@@ -7980,7 +8329,7 @@ function lookupInstrumentation(library, installedVersion) {
7980
8329
  }
7981
8330
  async function describeProjectInstrumentation(ctx) {
7982
8331
  const hookFiles = await findHookFiles(ctx.scanPath);
7983
- const envNeat = await fileExists2(path45.join(ctx.scanPath, ".env.neat"));
8332
+ const envNeat = await fileExists2(path46.join(ctx.scanPath, ".env.neat"));
7984
8333
  const registryInstrPackages = new Set(
7985
8334
  registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
7986
8335
  );
@@ -8002,16 +8351,22 @@ async function applyExtension(ctx, args, options) {
8002
8351
  );
8003
8352
  }
8004
8353
  for (const file of hookFiles) {
8005
- const content = await fs27.readFile(path45.join(ctx.scanPath, file), "utf8");
8354
+ const content = await fs27.readFile(path46.join(ctx.scanPath, file), "utf8");
8006
8355
  if (content.includes(args.registration_snippet)) {
8007
8356
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
8008
8357
  }
8009
8358
  }
8010
- const primaryFile = hookFiles[0];
8011
- const primaryPath = path45.join(ctx.scanPath, primaryFile);
8359
+ const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
8360
+ if (primary.patched === null) {
8361
+ throw new Error(
8362
+ `Could not find instrumentation insertion point in ${hookFiles.join(", ")}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
8363
+ );
8364
+ }
8365
+ const primaryFile = primary.file;
8366
+ const primaryPath = path46.join(ctx.scanPath, primaryFile);
8012
8367
  const filesTouched = [];
8013
8368
  const depsAdded = [];
8014
- const pkgPath = path45.join(ctx.scanPath, "package.json");
8369
+ const pkgPath = path46.join(ctx.scanPath, "package.json");
8015
8370
  const pkg = await readPackageJson(ctx.scanPath);
8016
8371
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
8017
8372
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -8019,14 +8374,7 @@ async function applyExtension(ctx, args, options) {
8019
8374
  filesTouched.push("package.json");
8020
8375
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
8021
8376
  }
8022
- const hookContent = await fs27.readFile(primaryPath, "utf8");
8023
- const patched = splicedContent(hookContent, args.registration_snippet);
8024
- if (!patched) {
8025
- throw new Error(
8026
- `Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
8027
- );
8028
- }
8029
- await fs27.writeFile(primaryPath, patched, "utf8");
8377
+ await fs27.writeFile(primaryPath, primary.patched, "utf8");
8030
8378
  filesTouched.push(primaryFile);
8031
8379
  const cmd = await detectPackageManager(ctx.scanPath);
8032
8380
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -8057,7 +8405,7 @@ async function dryRunExtension(ctx, args) {
8057
8405
  };
8058
8406
  }
8059
8407
  for (const file of hookFiles) {
8060
- const content = await fs27.readFile(path45.join(ctx.scanPath, file), "utf8");
8408
+ const content = await fs27.readFile(path46.join(ctx.scanPath, file), "utf8");
8061
8409
  if (content.includes(args.registration_snippet)) {
8062
8410
  return {
8063
8411
  library: args.library,
@@ -8068,7 +8416,7 @@ async function dryRunExtension(ctx, args) {
8068
8416
  };
8069
8417
  }
8070
8418
  }
8071
- const primaryFile = hookFiles[0];
8419
+ const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
8072
8420
  const filesTouched = [];
8073
8421
  const depsToAdd = [];
8074
8422
  let packageJsonPatch = {};
@@ -8079,10 +8427,8 @@ async function dryRunExtension(ctx, args) {
8079
8427
  depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
8080
8428
  filesTouched.push("package.json");
8081
8429
  }
8082
- const hookContent = await fs27.readFile(path45.join(ctx.scanPath, primaryFile), "utf8");
8083
- const patched = splicedContent(hookContent, args.registration_snippet);
8084
- if (patched) {
8085
- filesTouched.push(primaryFile);
8430
+ if (primary.patched !== null) {
8431
+ filesTouched.push(primary.file);
8086
8432
  templatePatch = `+ ${args.registration_snippet}`;
8087
8433
  } else {
8088
8434
  templatePatch = "Could not find insertion point in hook file.";
@@ -8100,7 +8446,7 @@ async function rollbackExtension(ctx, args) {
8100
8446
  if (!match) {
8101
8447
  return { undone: false, message: "no apply found for library" };
8102
8448
  }
8103
- const pkgPath = path45.join(ctx.scanPath, "package.json");
8449
+ const pkgPath = path46.join(ctx.scanPath, "package.json");
8104
8450
  if (await fileExists2(pkgPath)) {
8105
8451
  const pkg = await readPackageJson(ctx.scanPath);
8106
8452
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -8111,7 +8457,7 @@ async function rollbackExtension(ctx, args) {
8111
8457
  }
8112
8458
  const hookFiles = await findHookFiles(ctx.scanPath);
8113
8459
  for (const file of hookFiles) {
8114
- const filePath = path45.join(ctx.scanPath, file);
8460
+ const filePath = path46.join(ctx.scanPath, file);
8115
8461
  const content = await fs27.readFile(filePath, "utf8");
8116
8462
  if (content.includes(match.registration_snippet)) {
8117
8463
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -8226,7 +8572,7 @@ data: ${JSON.stringify(envelope.payload)}
8226
8572
 
8227
8573
  // src/connectors-config.ts
8228
8574
  import os4 from "os";
8229
- import path46 from "path";
8575
+ import path47 from "path";
8230
8576
  import { promises as fs28 } from "fs";
8231
8577
  var CONNECTORS_CONFIG_VERSION = 1;
8232
8578
  var EnvRefUnsetError = class extends Error {
@@ -8241,11 +8587,11 @@ var EnvRefUnsetError = class extends Error {
8241
8587
  };
8242
8588
  function neatHome2() {
8243
8589
  const override = process.env.NEAT_HOME;
8244
- if (override && override.length > 0) return path46.resolve(override);
8245
- return path46.join(os4.homedir(), ".neat");
8590
+ if (override && override.length > 0) return path47.resolve(override);
8591
+ return path47.join(os4.homedir(), ".neat");
8246
8592
  }
8247
8593
  function connectorsConfigPath(home = neatHome2()) {
8248
- return path46.join(home, "connectors.json");
8594
+ return path47.join(home, "connectors.json");
8249
8595
  }
8250
8596
  var MODE_MASK_LOOSER_THAN_0600 = 63;
8251
8597
  async function warnIfModeLooserThan0600(file) {
@@ -8376,7 +8722,7 @@ function connectorMatchesProject(entry, project) {
8376
8722
  var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
8377
8723
  var CONNECTORS_LOCK_RETRY_MS = 50;
8378
8724
  function connectorsConfigLockPath(home = neatHome2()) {
8379
- return path46.join(home, "connectors.json.lock");
8725
+ return path47.join(home, "connectors.json.lock");
8380
8726
  }
8381
8727
  function isEnvRef(value) {
8382
8728
  return value.length > 1 && value.startsWith("$");
@@ -8389,7 +8735,7 @@ function redactCredentialRef(ref) {
8389
8735
  return out;
8390
8736
  }
8391
8737
  async function writeConfigAtomically0600(file, contents) {
8392
- await fs28.mkdir(path46.dirname(file), { recursive: true });
8738
+ await fs28.mkdir(path47.dirname(file), { recursive: true });
8393
8739
  const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
8394
8740
  const fd = await fs28.open(tmp, "w", 384);
8395
8741
  try {
@@ -8403,7 +8749,7 @@ async function writeConfigAtomically0600(file, contents) {
8403
8749
  }
8404
8750
  async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
8405
8751
  const deadline = Date.now() + timeoutMs;
8406
- await fs28.mkdir(path46.dirname(lockPath), { recursive: true });
8752
+ await fs28.mkdir(path47.dirname(lockPath), { recursive: true });
8407
8753
  for (; ; ) {
8408
8754
  try {
8409
8755
  const fd = await fs28.open(lockPath, "wx");
@@ -9149,7 +9495,7 @@ function registerRoutes(scope, ctx) {
9149
9495
  });
9150
9496
  }
9151
9497
  async function buildApi(opts) {
9152
- const app = Fastify({ logger: false });
9498
+ const app = Fastify({ logger: false, routerOptions: { maxParamLength: 1024 } });
9153
9499
  await app.register(cors, { origin: true });
9154
9500
  const env = readAuthEnv();
9155
9501
  const authToken = opts.authToken ?? env.authToken;
@@ -9361,4 +9707,4 @@ export {
9361
9707
  recordConnectorPoll,
9362
9708
  buildApi
9363
9709
  };
9364
- //# sourceMappingURL=chunk-BI3XKGVG.js.map
9710
+ //# sourceMappingURL=chunk-PEFX3DBR.js.map