@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.
package/dist/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path51 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path51) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path51)) {
63
+ const path52 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path52) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path52)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
193
193
  };
194
194
  }
195
195
  function resolveProtoRoot() {
196
- const here = import_node_path47.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
- return import_node_path47.default.resolve(here, "..", "proto");
196
+ const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
+ return import_node_path48.default.resolve(here, "..", "proto");
198
198
  }
199
199
  function loadTraceService() {
200
200
  const protoRoot = resolveProtoRoot();
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
262
262
  })
263
263
  };
264
264
  }
265
- var import_node_url, import_node_path47, import_node_crypto2, grpc, protoLoader;
265
+ var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
266
266
  var init_otel_grpc = __esm({
267
267
  "src/otel-grpc.ts"() {
268
268
  "use strict";
269
269
  init_cjs_shims();
270
270
  import_node_url = require("url");
271
- import_node_path47 = __toESM(require("path"), 1);
271
+ import_node_path48 = __toESM(require("path"), 1);
272
272
  import_node_crypto2 = require("crypto");
273
273
  grpc = __toESM(require("@grpc/grpc-js"), 1);
274
274
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -361,8 +361,8 @@ function websocketChannelPathOf(attrs) {
361
361
  const v = attrs[key];
362
362
  if (typeof v === "string" && v.length > 0) {
363
363
  const q = v.indexOf("?");
364
- const path51 = q === -1 ? v : v.slice(0, q);
365
- if (path51.length > 0) return path51;
364
+ const path52 = q === -1 ? v : v.slice(0, q);
365
+ if (path52.length > 0) return path52;
366
366
  }
367
367
  }
368
368
  return void 0;
@@ -397,6 +397,7 @@ function parseOtlpRequest(body) {
397
397
  attributes: attrs,
398
398
  dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
399
399
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
400
+ dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
400
401
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
401
402
  messagingDestination: messagingDestinationOf(attrs),
402
403
  graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
@@ -416,10 +417,10 @@ function parseOtlpRequest(body) {
416
417
  return out;
417
418
  }
418
419
  function loadProtoRoot() {
419
- const here = import_node_path48.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
420
- const protoRoot = import_node_path48.default.resolve(here, "..", "proto");
420
+ const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
421
+ const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
421
422
  const root = new import_protobufjs.default.Root();
422
- root.resolvePath = (_origin, target) => import_node_path48.default.resolve(protoRoot, target);
423
+ root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
423
424
  root.loadSync(
424
425
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
425
426
  { keepCase: true }
@@ -655,12 +656,12 @@ function logSpanHandler(span) {
655
656
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
656
657
  );
657
658
  }
658
- var import_node_path48, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
659
+ var import_node_path49, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
659
660
  var init_otel = __esm({
660
661
  "src/otel.ts"() {
661
662
  "use strict";
662
663
  init_cjs_shims();
663
- import_node_path48 = __toESM(require("path"), 1);
664
+ import_node_path49 = __toESM(require("path"), 1);
664
665
  import_node_url2 = require("url");
665
666
  import_fastify2 = __toESM(require("fastify"), 1);
666
667
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -1250,19 +1251,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1250
1251
  function longestIncomingWalk(graph, start, maxDepth) {
1251
1252
  let best = { path: [start], edges: [] };
1252
1253
  const visited = /* @__PURE__ */ new Set([start]);
1253
- function step(node, path51, edges) {
1254
- if (path51.length > best.path.length) {
1255
- best = { path: [...path51], edges: [...edges] };
1254
+ function step(node, path52, edges) {
1255
+ if (path52.length > best.path.length) {
1256
+ best = { path: [...path52], edges: [...edges] };
1256
1257
  }
1257
- if (path51.length - 1 >= maxDepth) return;
1258
+ if (path52.length - 1 >= maxDepth) return;
1258
1259
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1259
1260
  for (const [srcId, edge] of incoming) {
1260
1261
  if (visited.has(srcId)) continue;
1261
1262
  visited.add(srcId);
1262
- path51.push(srcId);
1263
+ path52.push(srcId);
1263
1264
  edges.push(edge);
1264
- step(srcId, path51, edges);
1265
- path51.pop();
1265
+ step(srcId, path52, edges);
1266
+ path52.pop();
1266
1267
  edges.pop();
1267
1268
  visited.delete(srcId);
1268
1269
  }
@@ -1456,26 +1457,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
1456
1457
  return best;
1457
1458
  }
1458
1459
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1459
- const path51 = [originServiceId];
1460
+ const path52 = [originServiceId];
1460
1461
  const edges = [];
1461
1462
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1462
1463
  let current = originServiceId;
1463
1464
  for (let depth = 0; depth < maxDepth; depth++) {
1464
1465
  const hop = dominantFailingCall(graph, current, visited);
1465
1466
  if (!hop) break;
1466
- path51.push(hop.nextService);
1467
+ path52.push(hop.nextService);
1467
1468
  edges.push(hop.edge);
1468
1469
  visited.add(hop.nextService);
1469
1470
  current = hop.nextService;
1470
1471
  }
1471
1472
  if (edges.length === 0) return null;
1472
- return { path: path51, edges, culprit: current };
1473
+ return { path: path52, edges, culprit: current };
1473
1474
  }
1474
1475
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1475
1476
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1476
1477
  if (!chain) return null;
1477
1478
  const culprit = chain.culprit;
1478
- const path51 = [...chain.path];
1479
+ const path52 = [...chain.path];
1479
1480
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1480
1481
  const baseConfidence = confidenceFromMix(chain.edges);
1481
1482
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1483,14 +1484,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1483
1484
  if (loc) {
1484
1485
  let rootCauseNode = culprit;
1485
1486
  if (loc.fileNode) {
1486
- path51.push(loc.fileNode);
1487
+ path52.push(loc.fileNode);
1487
1488
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1488
1489
  rootCauseNode = loc.fileNode;
1489
1490
  }
1490
1491
  return import_types.RootCauseResultSchema.parse({
1491
1492
  rootCauseNode,
1492
1493
  rootCauseReason: loc.rootCauseReason,
1493
- traversalPath: path51,
1494
+ traversalPath: path52,
1494
1495
  edgeProvenances,
1495
1496
  confidence,
1496
1497
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1502,7 +1503,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1502
1503
  return import_types.RootCauseResultSchema.parse({
1503
1504
  rootCauseNode: culprit,
1504
1505
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1505
- traversalPath: path51,
1506
+ traversalPath: path52,
1506
1507
  edgeProvenances,
1507
1508
  confidence,
1508
1509
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2929,6 +2930,18 @@ async function handleSpan(ctx, span) {
2929
2930
  callSiteEvidence
2930
2931
  );
2931
2932
  if (result) affectedNode = targetId;
2933
+ if (span.dbSystem === "mongodb" && span.dbCollection) {
2934
+ const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
2935
+ upsertObservedEdge(
2936
+ ctx.graph,
2937
+ import_types3.EdgeType.CALLS,
2938
+ observedSource(),
2939
+ collectionId,
2940
+ ts,
2941
+ isError,
2942
+ callSiteEvidence
2943
+ );
2944
+ }
2932
2945
  }
2933
2946
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
2934
2947
  const targetId = ensureMessagingDestinationNode(
@@ -4673,14 +4686,21 @@ function engineFromImage(image) {
4673
4686
  // src/extract/databases/dotenv.ts
4674
4687
  var CONNECTION_KEYS = /* @__PURE__ */ new Set([
4675
4688
  "DATABASE_URL",
4689
+ "DATABASE_URI",
4676
4690
  "DB_URL",
4691
+ "DB_URI",
4677
4692
  "POSTGRES_URL",
4693
+ "POSTGRES_URI",
4678
4694
  "POSTGRESQL_URL",
4695
+ "POSTGRESQL_URI",
4679
4696
  "MYSQL_URL",
4697
+ "MYSQL_URI",
4698
+ "MONGODB_URL",
4680
4699
  "MONGODB_URI",
4681
4700
  "MONGO_URL",
4682
4701
  "MONGO_URI",
4683
- "REDIS_URL"
4702
+ "REDIS_URL",
4703
+ "REDIS_URI"
4684
4704
  ]);
4685
4705
  function parseDotenvLine(line) {
4686
4706
  const trimmed = line.trim();
@@ -5769,7 +5789,7 @@ async function addGrpcMethods(graph, services) {
5769
5789
 
5770
5790
  // src/extract/calls/index.ts
5771
5791
  init_cjs_shims();
5772
- var import_types20 = require("@neat.is/types");
5792
+ var import_types21 = require("@neat.is/types");
5773
5793
 
5774
5794
  // src/extract/calls/http.ts
5775
5795
  init_cjs_shims();
@@ -6482,15 +6502,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6482
6502
  return out;
6483
6503
  }
6484
6504
 
6505
+ // src/extract/calls/mongoose.ts
6506
+ init_cjs_shims();
6507
+ var import_node_path31 = __toESM(require("path"), 1);
6508
+ var import_types20 = require("@neat.is/types");
6509
+ var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
6510
+ var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
6511
+ var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
6512
+ var UNCOUNTABLES = /* @__PURE__ */ new Set([
6513
+ "advice",
6514
+ "energy",
6515
+ "excretion",
6516
+ "digestion",
6517
+ "cooperation",
6518
+ "health",
6519
+ "justice",
6520
+ "labour",
6521
+ "machinery",
6522
+ "equipment",
6523
+ "information",
6524
+ "pollution",
6525
+ "sewage",
6526
+ "paper",
6527
+ "money",
6528
+ "species",
6529
+ "series",
6530
+ "rain",
6531
+ "rice",
6532
+ "fish",
6533
+ "sheep",
6534
+ "moose",
6535
+ "deer",
6536
+ "news",
6537
+ "expertise",
6538
+ "status",
6539
+ "media"
6540
+ ]);
6541
+ var PLURALIZE_RULES = [
6542
+ [/human$/gi, "humans"],
6543
+ [/(m)an$/gi, "$1en"],
6544
+ [/(pe)rson$/gi, "$1ople"],
6545
+ [/(child)$/gi, "$1ren"],
6546
+ [/^(ox)$/gi, "$1en"],
6547
+ [/(ax|test)is$/gi, "$1es"],
6548
+ [/(octop|vir)us$/gi, "$1i"],
6549
+ [/(alias|status)$/gi, "$1es"],
6550
+ [/(bu)s$/gi, "$1ses"],
6551
+ [/(buffal|tomat|potat)o$/gi, "$1oes"],
6552
+ [/([ti])um$/gi, "$1a"],
6553
+ [/sis$/gi, "ses"],
6554
+ [/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
6555
+ [/(hive)$/gi, "$1s"],
6556
+ [/([^aeiouy]|qu)y$/gi, "$1ies"],
6557
+ [/(x|ch|ss|sh)$/gi, "$1es"],
6558
+ [/(matr|vert|ind)ix|ex$/gi, "$1ices"],
6559
+ [/([m|l])ouse$/gi, "$1ice"],
6560
+ [/(kn|w|l)ife$/gi, "$1ives"],
6561
+ [/(quiz)$/gi, "$1zes"],
6562
+ [/s$/gi, "s"],
6563
+ [/([^a-z])$/, "$1"],
6564
+ [/$/gi, "s"]
6565
+ ];
6566
+ function pluralizeCollection(name) {
6567
+ const str = name.toLowerCase();
6568
+ if (UNCOUNTABLES.has(str)) return str;
6569
+ for (const [re, repl] of PLURALIZE_RULES) {
6570
+ if (str.match(re)) return str.replace(re, repl);
6571
+ }
6572
+ return str;
6573
+ }
6574
+ var MODEL_METHODS = [
6575
+ "find",
6576
+ "findOne",
6577
+ "findById",
6578
+ "findByIdAndUpdate",
6579
+ "findByIdAndDelete",
6580
+ "findOneAndUpdate",
6581
+ "findOneAndDelete",
6582
+ "findOneAndReplace",
6583
+ "countDocuments",
6584
+ "estimatedDocumentCount",
6585
+ "distinct",
6586
+ "aggregate",
6587
+ "exists",
6588
+ "where",
6589
+ "populate",
6590
+ "watch",
6591
+ "hydrate",
6592
+ "create",
6593
+ "insertMany",
6594
+ "insertOne",
6595
+ "updateOne",
6596
+ "updateMany",
6597
+ "replaceOne",
6598
+ "deleteOne",
6599
+ "deleteMany",
6600
+ "bulkWrite",
6601
+ "bulkSave",
6602
+ "save",
6603
+ // pre-v7, removed in current mongoose but present in older codebases:
6604
+ "count",
6605
+ "update",
6606
+ "remove",
6607
+ "findOneAndRemove",
6608
+ "findByIdAndRemove"
6609
+ ];
6610
+ var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
6611
+ function schemaCollectionVars(content) {
6612
+ const out = /* @__PURE__ */ new Map();
6613
+ const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
6614
+ let m;
6615
+ while ((m = re.exec(content)) !== null) {
6616
+ const windowText = content.slice(m.index, m.index + 2e3);
6617
+ const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
6618
+ if (opt) out.set(m[1], opt[1]);
6619
+ }
6620
+ return out;
6621
+ }
6622
+ function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
6623
+ const trimmed = rest.trim();
6624
+ const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
6625
+ if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
6626
+ const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
6627
+ if (firstIdent && schemaColl.has(firstIdent)) {
6628
+ return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
6629
+ }
6630
+ if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
6631
+ return { name: modelName, kind: "mongodb-model" };
6632
+ }
6633
+ return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
6634
+ }
6635
+ function collectModelDefs(content, pluralizeOn) {
6636
+ const schemaColl = schemaCollectionVars(content);
6637
+ const out = [];
6638
+ 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;
6639
+ let m;
6640
+ while ((m = re.exec(content)) !== null) {
6641
+ out.push({
6642
+ modelName: m[2],
6643
+ varName: m[1] ?? null,
6644
+ resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
6645
+ matchText: m[0]
6646
+ });
6647
+ }
6648
+ return out;
6649
+ }
6650
+ function endpoint(r, file, serviceDir, matchText) {
6651
+ const line = lineOf(file.content, matchText);
6652
+ return {
6653
+ infraId: (0, import_types20.infraId)(r.kind, r.name),
6654
+ name: r.name,
6655
+ kind: r.kind,
6656
+ edgeType: "CALLS",
6657
+ confidenceKind: "verified-call-site",
6658
+ evidence: { file: import_node_path31.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
6659
+ };
6660
+ }
6661
+ function mongooseEndpointsFromFile(file, serviceDir) {
6662
+ const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
6663
+ const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
6664
+ if (!hasMongoose && !hasMongodb) return [];
6665
+ const content = file.content;
6666
+ const out = [];
6667
+ const seen = /* @__PURE__ */ new Set();
6668
+ const push = (r, matchText) => {
6669
+ const key = `${r.kind}/${r.name}`;
6670
+ if (seen.has(key)) return;
6671
+ seen.add(key);
6672
+ out.push(endpoint(r, file, serviceDir, matchText));
6673
+ };
6674
+ if (hasMongoose) {
6675
+ const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
6676
+ for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
6677
+ }
6678
+ const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
6679
+ let c;
6680
+ while ((c = collRe.exec(content)) !== null) {
6681
+ push({ name: c[1], kind: "mongodb-collection" }, c[0]);
6682
+ }
6683
+ return out;
6684
+ }
6685
+ function parseDestructure(inner, specifier, out) {
6686
+ for (const raw of inner.split(",")) {
6687
+ const part = raw.trim();
6688
+ if (!part) continue;
6689
+ const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
6690
+ const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
6691
+ if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
6692
+ else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
6693
+ else {
6694
+ const id = /^(\w+)$/.exec(part);
6695
+ if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
6696
+ }
6697
+ }
6698
+ }
6699
+ function parseImportBindings(content) {
6700
+ const out = [];
6701
+ const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
6702
+ let m;
6703
+ while ((m = esm.exec(content)) !== null) {
6704
+ const clause = m[1].trim();
6705
+ const spec = m[2];
6706
+ const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
6707
+ if (ns) {
6708
+ out.push({ local: ns[1], kind: "namespace", specifier: spec });
6709
+ continue;
6710
+ }
6711
+ const named = /\{([^}]*)\}/.exec(clause);
6712
+ if (named) parseDestructure(named[1], spec, out);
6713
+ const def = /^(\w+)\s*(?:,|$)/.exec(clause);
6714
+ if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
6715
+ }
6716
+ const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
6717
+ while ((m = cjs.exec(content)) !== null) {
6718
+ const lhs = m[1];
6719
+ const spec = m[2];
6720
+ if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
6721
+ else out.push({ local: lhs, kind: "whole", specifier: spec });
6722
+ }
6723
+ return out;
6724
+ }
6725
+ function fileExportsOf(content, pluralizeOn) {
6726
+ const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
6727
+ if (defs.length === 0) return null;
6728
+ const byVar = /* @__PURE__ */ new Map();
6729
+ for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
6730
+ const byName = new Map(byVar);
6731
+ let def;
6732
+ const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
6733
+ if (named && byVar.has(named[1])) def = byVar.get(named[1]);
6734
+ if (!def) {
6735
+ const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
6736
+ if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
6737
+ }
6738
+ if (!def && byVar.size === 1) def = [...byVar.values()][0];
6739
+ return { byName, ...def ? { default: def } : {} };
6740
+ }
6741
+ async function mongooseCrossFileEndpoints(files, serviceDir) {
6742
+ const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
6743
+ if (mongooseFiles.length === 0) return [];
6744
+ const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
6745
+ const registry = /* @__PURE__ */ new Map();
6746
+ for (const f of mongooseFiles) {
6747
+ const fx = fileExportsOf(f.content, pluralizeOn);
6748
+ if (fx) registry.set(toPosix2(import_node_path31.default.relative(serviceDir, f.path)), fx);
6749
+ }
6750
+ if (registry.size === 0) return [];
6751
+ const out = [];
6752
+ const seen = /* @__PURE__ */ new Set();
6753
+ for (const f of files) {
6754
+ const bindings = parseImportBindings(f.content);
6755
+ if (bindings.length === 0) continue;
6756
+ const directColl = /* @__PURE__ */ new Map();
6757
+ const nsExports = /* @__PURE__ */ new Map();
6758
+ for (const b of bindings) {
6759
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path31.default.dirname(f.path), serviceDir, null);
6760
+ if (!resolvedRel) continue;
6761
+ const fx = registry.get(resolvedRel);
6762
+ if (!fx) continue;
6763
+ if (b.kind === "named" && b.exportName) {
6764
+ const coll = fx.byName.get(b.exportName);
6765
+ if (coll) directColl.set(b.local, coll);
6766
+ } else if (b.kind === "default") {
6767
+ if (fx.default) directColl.set(b.local, fx.default);
6768
+ } else {
6769
+ if (fx.default) directColl.set(b.local, fx.default);
6770
+ nsExports.set(b.local, fx);
6771
+ }
6772
+ }
6773
+ if (directColl.size === 0 && nsExports.size === 0) continue;
6774
+ const localDefs = new Set(
6775
+ collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
6776
+ );
6777
+ const emit = (collection, matchText) => {
6778
+ const key = `${f.path}::${collection}`;
6779
+ if (seen.has(key)) return;
6780
+ seen.add(key);
6781
+ out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
6782
+ };
6783
+ for (const [local, collection] of directColl) {
6784
+ if (localDefs.has(local)) continue;
6785
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
6786
+ let qm;
6787
+ while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
6788
+ }
6789
+ for (const [local, fx] of nsExports) {
6790
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
6791
+ let qm;
6792
+ while ((qm = qre.exec(f.content)) !== null) {
6793
+ const coll = fx.byName.get(qm[1]);
6794
+ if (coll) emit(coll, qm[0]);
6795
+ }
6796
+ }
6797
+ }
6798
+ return out;
6799
+ }
6800
+
6485
6801
  // src/extract/calls/index.ts
6486
6802
  function edgeTypeFromEndpoint(ep) {
6487
6803
  switch (ep.edgeType) {
6488
6804
  case "PUBLISHES_TO":
6489
- return import_types20.EdgeType.PUBLISHES_TO;
6805
+ return import_types21.EdgeType.PUBLISHES_TO;
6490
6806
  case "CONSUMES_FROM":
6491
- return import_types20.EdgeType.CONSUMES_FROM;
6807
+ return import_types21.EdgeType.CONSUMES_FROM;
6492
6808
  default:
6493
- return import_types20.EdgeType.CALLS;
6809
+ return import_types21.EdgeType.CALLS;
6494
6810
  }
6495
6811
  }
6496
6812
  function isAwsKind(kind) {
@@ -6502,23 +6818,27 @@ async function addExternalEndpointEdges(graph, services) {
6502
6818
  for (const service of services) {
6503
6819
  const files = await loadSourceFiles(service.dir);
6504
6820
  const endpoints = [];
6821
+ const maskedFiles = [];
6505
6822
  for (const file of files) {
6506
6823
  if (isTestPath(file.path)) continue;
6507
6824
  const masked = maskCommentsInSource(file.content);
6508
6825
  const maskedFile = { path: file.path, content: masked };
6826
+ maskedFiles.push(maskedFile);
6509
6827
  endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
6510
6828
  endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
6511
6829
  endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
6512
6830
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
6513
6831
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
6832
+ endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
6514
6833
  }
6834
+ endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
6515
6835
  if (endpoints.length === 0) continue;
6516
6836
  const seenEdges = /* @__PURE__ */ new Set();
6517
6837
  for (const ep of endpoints) {
6518
6838
  if (!graph.hasNode(ep.infraId)) {
6519
6839
  const node = {
6520
6840
  id: ep.infraId,
6521
- type: import_types20.NodeType.InfraNode,
6841
+ type: import_types21.NodeType.InfraNode,
6522
6842
  name: ep.name,
6523
6843
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
6524
6844
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -6530,7 +6850,7 @@ async function addExternalEndpointEdges(graph, services) {
6530
6850
  nodesAdded++;
6531
6851
  }
6532
6852
  const edgeType = edgeTypeFromEndpoint(ep);
6533
- const confidence = (0, import_types20.confidenceForExtracted)(ep.confidenceKind);
6853
+ const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
6534
6854
  const relFile = toPosix2(ep.evidence.file);
6535
6855
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
6536
6856
  graph,
@@ -6540,7 +6860,7 @@ async function addExternalEndpointEdges(graph, services) {
6540
6860
  );
6541
6861
  nodesAdded += n;
6542
6862
  edgesAdded += e;
6543
- if (!(0, import_types20.passesExtractedFloor)(confidence)) {
6863
+ if (!(0, import_types21.passesExtractedFloor)(confidence)) {
6544
6864
  noteExtractedDropped({
6545
6865
  source: fileNodeId,
6546
6866
  target: ep.infraId,
@@ -6560,7 +6880,7 @@ async function addExternalEndpointEdges(graph, services) {
6560
6880
  source: fileNodeId,
6561
6881
  target: ep.infraId,
6562
6882
  type: edgeType,
6563
- provenance: import_types20.Provenance.EXTRACTED,
6883
+ provenance: import_types21.Provenance.EXTRACTED,
6564
6884
  confidence,
6565
6885
  evidence: ep.evidence
6566
6886
  };
@@ -6586,16 +6906,16 @@ init_cjs_shims();
6586
6906
 
6587
6907
  // src/extract/infra/docker-compose.ts
6588
6908
  init_cjs_shims();
6589
- var import_node_path31 = __toESM(require("path"), 1);
6590
- var import_types22 = require("@neat.is/types");
6909
+ var import_node_path32 = __toESM(require("path"), 1);
6910
+ var import_types23 = require("@neat.is/types");
6591
6911
 
6592
6912
  // src/extract/infra/shared.ts
6593
6913
  init_cjs_shims();
6594
- var import_types21 = require("@neat.is/types");
6914
+ var import_types22 = require("@neat.is/types");
6595
6915
  function makeInfraNode(kind, name, provider = "self", extras) {
6596
6916
  return {
6597
- id: (0, import_types21.infraId)(kind, name),
6598
- type: import_types21.NodeType.InfraNode,
6917
+ id: (0, import_types22.infraId)(kind, name),
6918
+ type: import_types22.NodeType.InfraNode,
6599
6919
  name,
6600
6920
  provider,
6601
6921
  kind,
@@ -6639,8 +6959,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
6639
6959
  source: anchorId,
6640
6960
  target: node.id,
6641
6961
  type: edgeType,
6642
- provenance: import_types21.Provenance.EXTRACTED,
6643
- confidence: (0, import_types21.confidenceForExtracted)("structural"),
6962
+ provenance: import_types22.Provenance.EXTRACTED,
6963
+ confidence: (0, import_types22.confidenceForExtracted)("structural"),
6644
6964
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
6645
6965
  };
6646
6966
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -6657,7 +6977,7 @@ function dependsOnList(value) {
6657
6977
  }
6658
6978
  function serviceNameToServiceNode(name, services) {
6659
6979
  for (const s of services) {
6660
- if (s.node.name === name || import_node_path31.default.basename(s.dir) === name) return s.node.id;
6980
+ if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
6661
6981
  }
6662
6982
  return null;
6663
6983
  }
@@ -6666,7 +6986,7 @@ async function addComposeInfra(graph, scanPath, services) {
6666
6986
  let edgesAdded = 0;
6667
6987
  let composePath = null;
6668
6988
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
6669
- const abs = import_node_path31.default.join(scanPath, name);
6989
+ const abs = import_node_path32.default.join(scanPath, name);
6670
6990
  if (await exists(abs)) {
6671
6991
  composePath = abs;
6672
6992
  break;
@@ -6679,13 +6999,13 @@ async function addComposeInfra(graph, scanPath, services) {
6679
6999
  } catch (err) {
6680
7000
  recordExtractionError(
6681
7001
  "infra docker-compose",
6682
- import_node_path31.default.relative(scanPath, composePath),
7002
+ import_node_path32.default.relative(scanPath, composePath),
6683
7003
  err
6684
7004
  );
6685
7005
  return { nodesAdded, edgesAdded };
6686
7006
  }
6687
7007
  if (!compose?.services) return { nodesAdded, edgesAdded };
6688
- const evidenceFile = import_node_path31.default.relative(scanPath, composePath).split(import_node_path31.default.sep).join("/");
7008
+ const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
6689
7009
  const composeNameToNodeId = /* @__PURE__ */ new Map();
6690
7010
  for (const [composeName, svc] of Object.entries(compose.services)) {
6691
7011
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -6707,15 +7027,15 @@ async function addComposeInfra(graph, scanPath, services) {
6707
7027
  for (const dep of dependsOnList(svc.depends_on)) {
6708
7028
  const targetId = composeNameToNodeId.get(dep);
6709
7029
  if (!targetId) continue;
6710
- const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types22.EdgeType.DEPENDS_ON);
7030
+ const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
6711
7031
  if (graph.hasEdge(edgeId)) continue;
6712
7032
  const edge = {
6713
7033
  id: edgeId,
6714
7034
  source: sourceId,
6715
7035
  target: targetId,
6716
- type: import_types22.EdgeType.DEPENDS_ON,
6717
- provenance: import_types22.Provenance.EXTRACTED,
6718
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7036
+ type: import_types23.EdgeType.DEPENDS_ON,
7037
+ provenance: import_types23.Provenance.EXTRACTED,
7038
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
6719
7039
  evidence: { file: evidenceFile }
6720
7040
  };
6721
7041
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -6727,9 +7047,9 @@ async function addComposeInfra(graph, scanPath, services) {
6727
7047
 
6728
7048
  // src/extract/infra/dockerfile.ts
6729
7049
  init_cjs_shims();
6730
- var import_node_path32 = __toESM(require("path"), 1);
7050
+ var import_node_path33 = __toESM(require("path"), 1);
6731
7051
  var import_node_fs16 = require("fs");
6732
- var import_types23 = require("@neat.is/types");
7052
+ var import_types24 = require("@neat.is/types");
6733
7053
  function readDockerfile(content) {
6734
7054
  let image = null;
6735
7055
  const ports = [];
@@ -6758,7 +7078,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6758
7078
  let nodesAdded = 0;
6759
7079
  let edgesAdded = 0;
6760
7080
  for (const service of services) {
6761
- const dockerfilePath = import_node_path32.default.join(service.dir, "Dockerfile");
7081
+ const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
6762
7082
  if (!await exists(dockerfilePath)) continue;
6763
7083
  let content;
6764
7084
  try {
@@ -6766,7 +7086,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6766
7086
  } catch (err) {
6767
7087
  recordExtractionError(
6768
7088
  "infra dockerfile",
6769
- import_node_path32.default.relative(scanPath, dockerfilePath),
7089
+ import_node_path33.default.relative(scanPath, dockerfilePath),
6770
7090
  err
6771
7091
  );
6772
7092
  continue;
@@ -6778,8 +7098,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6778
7098
  graph.addNode(node.id, node);
6779
7099
  nodesAdded++;
6780
7100
  }
6781
- const relDockerfile = toPosix2(import_node_path32.default.relative(service.dir, dockerfilePath));
6782
- const evidenceFile = toPosix2(import_node_path32.default.relative(scanPath, dockerfilePath));
7101
+ const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
7102
+ const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
6783
7103
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
6784
7104
  graph,
6785
7105
  service.pkg.name,
@@ -6788,15 +7108,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6788
7108
  );
6789
7109
  nodesAdded += fn;
6790
7110
  edgesAdded += fe;
6791
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types23.EdgeType.RUNS_ON);
7111
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
6792
7112
  if (!graph.hasEdge(edgeId)) {
6793
7113
  const edge = {
6794
7114
  id: edgeId,
6795
7115
  source: fileNodeId,
6796
7116
  target: node.id,
6797
- type: import_types23.EdgeType.RUNS_ON,
6798
- provenance: import_types23.Provenance.EXTRACTED,
6799
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7117
+ type: import_types24.EdgeType.RUNS_ON,
7118
+ provenance: import_types24.Provenance.EXTRACTED,
7119
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
6800
7120
  evidence: {
6801
7121
  file: evidenceFile,
6802
7122
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -6811,15 +7131,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6811
7131
  graph.addNode(portNode.id, portNode);
6812
7132
  nodesAdded++;
6813
7133
  }
6814
- const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types23.EdgeType.CONNECTS_TO);
7134
+ const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
6815
7135
  if (graph.hasEdge(portEdgeId)) continue;
6816
7136
  const portEdge = {
6817
7137
  id: portEdgeId,
6818
7138
  source: fileNodeId,
6819
7139
  target: portNode.id,
6820
- type: import_types23.EdgeType.CONNECTS_TO,
6821
- provenance: import_types23.Provenance.EXTRACTED,
6822
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7140
+ type: import_types24.EdgeType.CONNECTS_TO,
7141
+ provenance: import_types24.Provenance.EXTRACTED,
7142
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
6823
7143
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
6824
7144
  };
6825
7145
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -6832,8 +7152,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6832
7152
  // src/extract/infra/terraform.ts
6833
7153
  init_cjs_shims();
6834
7154
  var import_node_fs17 = require("fs");
6835
- var import_node_path33 = __toESM(require("path"), 1);
6836
- var import_types24 = require("@neat.is/types");
7155
+ var import_node_path34 = __toESM(require("path"), 1);
7156
+ var import_types25 = require("@neat.is/types");
6837
7157
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
6838
7158
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
6839
7159
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -6843,11 +7163,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
6843
7163
  for (const entry of entries) {
6844
7164
  if (entry.isDirectory()) {
6845
7165
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
6846
- const child = import_node_path33.default.join(start, entry.name);
7166
+ const child = import_node_path34.default.join(start, entry.name);
6847
7167
  if (await isPythonVenvDir(child)) continue;
6848
7168
  out.push(...await walkTfFiles(child, depth + 1, max));
6849
7169
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
6850
- out.push(import_node_path33.default.join(start, entry.name));
7170
+ out.push(import_node_path34.default.join(start, entry.name));
6851
7171
  }
6852
7172
  }
6853
7173
  return out;
@@ -6879,7 +7199,7 @@ async function addTerraformResources(graph, scanPath) {
6879
7199
  const files = await walkTfFiles(scanPath);
6880
7200
  for (const file of files) {
6881
7201
  const content = await import_node_fs17.promises.readFile(file, "utf8");
6882
- const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, file));
7202
+ const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
6883
7203
  const resources = [];
6884
7204
  const byKey = /* @__PURE__ */ new Map();
6885
7205
  RESOURCE_RE.lastIndex = 0;
@@ -6914,16 +7234,16 @@ async function addTerraformResources(graph, scanPath) {
6914
7234
  if (!target) continue;
6915
7235
  if (seen.has(target.nodeId)) continue;
6916
7236
  seen.add(target.nodeId);
6917
- const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types24.EdgeType.DEPENDS_ON);
7237
+ const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
6918
7238
  if (graph.hasEdge(edgeId)) continue;
6919
7239
  const line = lineAt2(content, resource.bodyOffset + ref.index);
6920
7240
  const edge = {
6921
7241
  id: edgeId,
6922
7242
  source: resource.nodeId,
6923
7243
  target: target.nodeId,
6924
- type: import_types24.EdgeType.DEPENDS_ON,
6925
- provenance: import_types24.Provenance.EXTRACTED,
6926
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7244
+ type: import_types25.EdgeType.DEPENDS_ON,
7245
+ provenance: import_types25.Provenance.EXTRACTED,
7246
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
6927
7247
  evidence: { file: evidenceFile, line, snippet: key }
6928
7248
  };
6929
7249
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -6937,7 +7257,7 @@ async function addTerraformResources(graph, scanPath) {
6937
7257
  // src/extract/infra/k8s.ts
6938
7258
  init_cjs_shims();
6939
7259
  var import_node_fs18 = require("fs");
6940
- var import_node_path34 = __toESM(require("path"), 1);
7260
+ var import_node_path35 = __toESM(require("path"), 1);
6941
7261
  var import_yaml3 = require("yaml");
6942
7262
  var K8S_KIND_TO_INFRA_KIND = {
6943
7263
  Service: "k8s-service",
@@ -6955,11 +7275,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
6955
7275
  for (const entry of entries) {
6956
7276
  if (entry.isDirectory()) {
6957
7277
  if (IGNORED_DIRS.has(entry.name)) continue;
6958
- const child = import_node_path34.default.join(start, entry.name);
7278
+ const child = import_node_path35.default.join(start, entry.name);
6959
7279
  if (await isPythonVenvDir(child)) continue;
6960
7280
  out.push(...await walkYamlFiles2(child, depth + 1, max));
6961
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path34.default.extname(entry.name))) {
6962
- out.push(import_node_path34.default.join(start, entry.name));
7281
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry.name))) {
7282
+ out.push(import_node_path35.default.join(start, entry.name));
6963
7283
  }
6964
7284
  }
6965
7285
  return out;
@@ -6993,13 +7313,13 @@ async function addK8sResources(graph, scanPath) {
6993
7313
  // src/extract/infra/cloudflare.ts
6994
7314
  init_cjs_shims();
6995
7315
  var import_node_fs19 = require("fs");
6996
- var import_node_path35 = __toESM(require("path"), 1);
7316
+ var import_node_path36 = __toESM(require("path"), 1);
6997
7317
  var import_smol_toml2 = require("smol-toml");
6998
- var import_types25 = require("@neat.is/types");
7318
+ var import_types26 = require("@neat.is/types");
6999
7319
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7000
7320
  async function readWranglerConfig(dir) {
7001
7321
  for (const filename of WRANGLER_FILENAMES) {
7002
- const abs = import_node_path35.default.join(dir, filename);
7322
+ const abs = import_node_path36.default.join(dir, filename);
7003
7323
  if (!await exists(abs)) continue;
7004
7324
  const raw = await import_node_fs19.promises.readFile(abs, "utf8");
7005
7325
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7043,8 +7363,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7043
7363
  source: anchorId,
7044
7364
  target: node.id,
7045
7365
  type: edgeType,
7046
- provenance: import_types25.Provenance.EXTRACTED,
7047
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7366
+ provenance: import_types26.Provenance.EXTRACTED,
7367
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7048
7368
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7049
7369
  };
7050
7370
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7062,11 +7382,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7062
7382
  try {
7063
7383
  read = await readWranglerConfig(service.dir);
7064
7384
  } catch (err) {
7065
- recordExtractionError("infra cloudflare", import_node_path35.default.relative(scanPath, service.dir), err);
7385
+ recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
7066
7386
  continue;
7067
7387
  }
7068
7388
  if (!read || !read.config.name) continue;
7069
- const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, import_node_path35.default.join(service.dir, read.relFile)));
7389
+ const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
7070
7390
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7071
7391
  }
7072
7392
  for (const worker of discovered) {
@@ -7078,7 +7398,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7078
7398
  }
7079
7399
  let anchorId = service.node.id;
7080
7400
  if (config.main) {
7081
- const entryRelPath = toPosix2(import_node_path35.default.normalize(config.main));
7401
+ const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
7082
7402
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7083
7403
  graph,
7084
7404
  service.pkg.name,
@@ -7105,15 +7425,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7105
7425
  nodesAdded++;
7106
7426
  }
7107
7427
  if (runtimeNode.id !== anchorId) {
7108
- const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types25.EdgeType.RUNS_ON);
7428
+ const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
7109
7429
  if (!graph.hasEdge(runsOnId)) {
7110
7430
  const edge = {
7111
7431
  id: runsOnId,
7112
7432
  source: anchorId,
7113
7433
  target: runtimeNode.id,
7114
- type: import_types25.EdgeType.RUNS_ON,
7115
- provenance: import_types25.Provenance.EXTRACTED,
7116
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7434
+ type: import_types26.EdgeType.RUNS_ON,
7435
+ provenance: import_types26.Provenance.EXTRACTED,
7436
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7117
7437
  evidence: {
7118
7438
  file: evidenceFile,
7119
7439
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7127,7 +7447,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7127
7447
  const result = addResourceEdge(
7128
7448
  graph,
7129
7449
  anchorId,
7130
- import_types25.EdgeType.CONNECTS_TO,
7450
+ import_types26.EdgeType.CONNECTS_TO,
7131
7451
  "cloudflare-route",
7132
7452
  route,
7133
7453
  evidenceFile,
@@ -7151,7 +7471,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7151
7471
  const result = addResourceEdge(
7152
7472
  graph,
7153
7473
  anchorId,
7154
- import_types25.EdgeType.DEPENDS_ON,
7474
+ import_types26.EdgeType.DEPENDS_ON,
7155
7475
  group.kind,
7156
7476
  name,
7157
7477
  evidenceFile,
@@ -7165,7 +7485,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7165
7485
  const result = addResourceEdge(
7166
7486
  graph,
7167
7487
  anchorId,
7168
- import_types25.EdgeType.DEPENDS_ON,
7488
+ import_types26.EdgeType.DEPENDS_ON,
7169
7489
  "cloudflare-cron",
7170
7490
  cron,
7171
7491
  evidenceFile,
@@ -7178,7 +7498,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7178
7498
  const result = addResourceEdge(
7179
7499
  graph,
7180
7500
  anchorId,
7181
- import_types25.EdgeType.DEPENDS_ON,
7501
+ import_types26.EdgeType.DEPENDS_ON,
7182
7502
  "cloudflare-env-var",
7183
7503
  varName,
7184
7504
  evidenceFile,
@@ -7191,15 +7511,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7191
7511
  if (!svc.service) continue;
7192
7512
  const target = workerIndex.get(svc.service);
7193
7513
  if (target && target.anchorId !== anchorId) {
7194
- const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types25.EdgeType.CALLS);
7514
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
7195
7515
  if (!graph.hasEdge(edgeId)) {
7196
7516
  const edge = {
7197
7517
  id: edgeId,
7198
7518
  source: anchorId,
7199
7519
  target: target.anchorId,
7200
- type: import_types25.EdgeType.CALLS,
7201
- provenance: import_types25.Provenance.EXTRACTED,
7202
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7520
+ type: import_types26.EdgeType.CALLS,
7521
+ provenance: import_types26.Provenance.EXTRACTED,
7522
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7203
7523
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7204
7524
  };
7205
7525
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7210,7 +7530,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7210
7530
  const result = addResourceEdge(
7211
7531
  graph,
7212
7532
  anchorId,
7213
- import_types25.EdgeType.DEPENDS_ON,
7533
+ import_types26.EdgeType.DEPENDS_ON,
7214
7534
  "cloudflare-service-binding",
7215
7535
  svc.service,
7216
7536
  evidenceFile,
@@ -7226,12 +7546,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7226
7546
  // src/extract/infra/vercel.ts
7227
7547
  init_cjs_shims();
7228
7548
  var import_node_fs20 = require("fs");
7229
- var import_node_path36 = __toESM(require("path"), 1);
7230
- var import_types26 = require("@neat.is/types");
7549
+ var import_node_path37 = __toESM(require("path"), 1);
7550
+ var import_types27 = require("@neat.is/types");
7231
7551
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7232
7552
  async function readVercelConfig(dir) {
7233
7553
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7234
- const abs = import_node_path36.default.join(dir, filename);
7554
+ const abs = import_node_path37.default.join(dir, filename);
7235
7555
  if (!await exists(abs)) continue;
7236
7556
  const raw = await import_node_fs20.promises.readFile(abs, "utf8");
7237
7557
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7240,7 +7560,7 @@ async function readVercelConfig(dir) {
7240
7560
  return null;
7241
7561
  }
7242
7562
  async function readLinkedProjectName(dir) {
7243
- const abs = import_node_path36.default.join(dir, ".vercel", "project.json");
7563
+ const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
7244
7564
  if (!await exists(abs)) return void 0;
7245
7565
  const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
7246
7566
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7258,7 +7578,7 @@ async function addVercelServices(graph, services, scanPath) {
7258
7578
  read = await readVercelConfig(service.dir);
7259
7579
  projectName = await readLinkedProjectName(service.dir);
7260
7580
  } catch (err) {
7261
- recordExtractionError("infra vercel", import_node_path36.default.relative(scanPath, service.dir), err);
7581
+ recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
7262
7582
  continue;
7263
7583
  }
7264
7584
  if (!read && !projectName) continue;
@@ -7274,7 +7594,7 @@ async function addVercelServices(graph, services, scanPath) {
7274
7594
  const anchorId = service.node.id;
7275
7595
  if (!read) continue;
7276
7596
  const { config, relFile, raw } = read;
7277
- const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, relFile)));
7597
+ const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
7278
7598
  const add = (edgeType, kind, name) => {
7279
7599
  if (!name) return;
7280
7600
  const result = emitPlatformResourceEdge(
@@ -7290,12 +7610,12 @@ async function addVercelServices(graph, services, scanPath) {
7290
7610
  nodesAdded += result.nodesAdded;
7291
7611
  edgesAdded += result.edgesAdded;
7292
7612
  };
7293
- add(import_types26.EdgeType.RUNS_ON, "vercel", "vercel");
7294
- for (const cron of config.crons ?? []) add(import_types26.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7295
- for (const varName of Object.keys(config.env ?? {})) add(import_types26.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7296
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types26.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7613
+ add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7614
+ for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7615
+ for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7616
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7297
7617
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7298
- add(import_types26.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7618
+ add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7299
7619
  }
7300
7620
  }
7301
7621
  return { nodesAdded, edgesAdded };
@@ -7304,13 +7624,13 @@ async function addVercelServices(graph, services, scanPath) {
7304
7624
  // src/extract/infra/railway.ts
7305
7625
  init_cjs_shims();
7306
7626
  var import_node_fs21 = require("fs");
7307
- var import_node_path37 = __toESM(require("path"), 1);
7627
+ var import_node_path38 = __toESM(require("path"), 1);
7308
7628
  var import_smol_toml3 = require("smol-toml");
7309
- var import_types27 = require("@neat.is/types");
7629
+ var import_types28 = require("@neat.is/types");
7310
7630
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7311
7631
  async function readRailwayConfig(dir) {
7312
7632
  for (const filename of RAILWAY_FILENAMES) {
7313
- const abs = import_node_path37.default.join(dir, filename);
7633
+ const abs = import_node_path38.default.join(dir, filename);
7314
7634
  if (!await exists(abs)) continue;
7315
7635
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7316
7636
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7326,7 +7646,7 @@ async function addRailwayServices(graph, services, scanPath) {
7326
7646
  try {
7327
7647
  read = await readRailwayConfig(service.dir);
7328
7648
  } catch (err) {
7329
- recordExtractionError("infra railway", import_node_path37.default.relative(scanPath, service.dir), err);
7649
+ recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
7330
7650
  continue;
7331
7651
  }
7332
7652
  if (!read) continue;
@@ -7336,7 +7656,7 @@ async function addRailwayServices(graph, services, scanPath) {
7336
7656
  }
7337
7657
  const anchorId = service.node.id;
7338
7658
  const { config, relFile, raw } = read;
7339
- const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
7659
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7340
7660
  const add = (edgeType, kind, name) => {
7341
7661
  if (!name) return;
7342
7662
  const result = emitPlatformResourceEdge(
@@ -7352,9 +7672,9 @@ async function addRailwayServices(graph, services, scanPath) {
7352
7672
  nodesAdded += result.nodesAdded;
7353
7673
  edgesAdded += result.edgesAdded;
7354
7674
  };
7355
- add(import_types27.EdgeType.RUNS_ON, "railway", "railway");
7356
- add(import_types27.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7357
- add(import_types27.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7675
+ add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7676
+ add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7677
+ add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7358
7678
  }
7359
7679
  return { nodesAdded, edgesAdded };
7360
7680
  }
@@ -7362,12 +7682,12 @@ async function addRailwayServices(graph, services, scanPath) {
7362
7682
  // src/extract/infra/supabase.ts
7363
7683
  init_cjs_shims();
7364
7684
  var import_node_fs22 = require("fs");
7365
- var import_node_path38 = __toESM(require("path"), 1);
7685
+ var import_node_path39 = __toESM(require("path"), 1);
7366
7686
  var import_smol_toml4 = require("smol-toml");
7367
- var import_types28 = require("@neat.is/types");
7687
+ var import_types29 = require("@neat.is/types");
7368
7688
  async function readSupabaseConfig(dir) {
7369
- const relFile = import_node_path38.default.join("supabase", "config.toml");
7370
- const abs = import_node_path38.default.join(dir, relFile);
7689
+ const relFile = import_node_path39.default.join("supabase", "config.toml");
7690
+ const abs = import_node_path39.default.join(dir, relFile);
7371
7691
  if (!await exists(abs)) return null;
7372
7692
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7373
7693
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7381,7 +7701,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7381
7701
  try {
7382
7702
  read = await readSupabaseConfig(service.dir);
7383
7703
  } catch (err) {
7384
- recordExtractionError("infra supabase", import_node_path38.default.relative(scanPath, service.dir), err);
7704
+ recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
7385
7705
  continue;
7386
7706
  }
7387
7707
  if (!read) continue;
@@ -7396,7 +7716,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7396
7716
  });
7397
7717
  }
7398
7718
  const anchorId = service.node.id;
7399
- const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7719
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7400
7720
  const add = (edgeType, kind, name) => {
7401
7721
  if (!name) return;
7402
7722
  const result = emitPlatformResourceEdge(
@@ -7412,10 +7732,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7412
7732
  nodesAdded += result.nodesAdded;
7413
7733
  edgesAdded += result.edgesAdded;
7414
7734
  };
7415
- add(import_types28.EdgeType.RUNS_ON, "supabase", "supabase");
7416
- for (const fn of Object.keys(config.functions ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "supabase-function", fn);
7417
- if (config.storage) add(import_types28.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7418
- if (config.auth) add(import_types28.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7735
+ add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7736
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7737
+ if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7738
+ if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7419
7739
  }
7420
7740
  return { nodesAdded, edgesAdded };
7421
7741
  }
@@ -7437,17 +7757,17 @@ async function addInfra(graph, scanPath, services) {
7437
7757
  }
7438
7758
 
7439
7759
  // src/extract/index.ts
7440
- var import_node_path40 = __toESM(require("path"), 1);
7760
+ var import_node_path41 = __toESM(require("path"), 1);
7441
7761
 
7442
7762
  // src/extract/retire.ts
7443
7763
  init_cjs_shims();
7444
7764
  var import_node_fs23 = require("fs");
7445
- var import_node_path39 = __toESM(require("path"), 1);
7446
- var import_types29 = require("@neat.is/types");
7765
+ var import_node_path40 = __toESM(require("path"), 1);
7766
+ var import_types30 = require("@neat.is/types");
7447
7767
  function dropOrphanedFileNodes(graph) {
7448
7768
  const orphans = [];
7449
7769
  graph.forEachNode((id, attrs) => {
7450
- if (attrs.type !== import_types29.NodeType.FileNode) return;
7770
+ if (attrs.type !== import_types30.NodeType.FileNode) return;
7451
7771
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7452
7772
  orphans.push(id);
7453
7773
  }
@@ -7460,14 +7780,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7460
7780
  const bases = [scanPath, ...serviceDirs];
7461
7781
  graph.forEachEdge((id, attrs) => {
7462
7782
  const edge = attrs;
7463
- if (edge.provenance !== import_types29.Provenance.EXTRACTED) return;
7783
+ if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
7464
7784
  const evidenceFile = edge.evidence?.file;
7465
7785
  if (!evidenceFile) return;
7466
- if (import_node_path39.default.isAbsolute(evidenceFile)) {
7786
+ if (import_node_path40.default.isAbsolute(evidenceFile)) {
7467
7787
  if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
7468
7788
  return;
7469
7789
  }
7470
- const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path39.default.join(base, evidenceFile)));
7790
+ const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path40.default.join(base, evidenceFile)));
7471
7791
  if (!found) toDrop.push(id);
7472
7792
  });
7473
7793
  for (const id of toDrop) graph.dropEdge(id);
@@ -7509,7 +7829,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7509
7829
  }
7510
7830
  const droppedEntries = drainDroppedExtracted();
7511
7831
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7512
- const rejectedPath = import_node_path40.default.join(import_node_path40.default.dirname(opts.errorsPath), "rejected.ndjson");
7832
+ const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
7513
7833
  try {
7514
7834
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7515
7835
  } catch (err) {
@@ -7544,8 +7864,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7544
7864
  // src/persist.ts
7545
7865
  init_cjs_shims();
7546
7866
  var import_node_fs24 = require("fs");
7547
- var import_node_path41 = __toESM(require("path"), 1);
7548
- var import_types30 = require("@neat.is/types");
7867
+ var import_node_path42 = __toESM(require("path"), 1);
7868
+ var import_types31 = require("@neat.is/types");
7549
7869
  var SCHEMA_VERSION = 4;
7550
7870
  function migrateV1ToV2(payload) {
7551
7871
  const nodes = payload.graph.nodes;
@@ -7567,12 +7887,12 @@ function migrateV2ToV3(payload) {
7567
7887
  for (const edge of edges) {
7568
7888
  const attrs = edge.attributes;
7569
7889
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
7570
- attrs.provenance = import_types30.Provenance.OBSERVED;
7890
+ attrs.provenance = import_types31.Provenance.OBSERVED;
7571
7891
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
7572
7892
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
7573
7893
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
7574
7894
  if (type && source && target) {
7575
- const newId = (0, import_types30.observedEdgeId)(source, target, type);
7895
+ const newId = (0, import_types31.observedEdgeId)(source, target, type);
7576
7896
  attrs.id = newId;
7577
7897
  if (edge.key) edge.key = newId;
7578
7898
  }
@@ -7581,7 +7901,7 @@ function migrateV2ToV3(payload) {
7581
7901
  return { ...payload, schemaVersion: 3 };
7582
7902
  }
7583
7903
  async function ensureDir(filePath) {
7584
- await import_node_fs24.promises.mkdir(import_node_path41.default.dirname(filePath), { recursive: true });
7904
+ await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
7585
7905
  }
7586
7906
  async function saveGraphToDisk(graph, outPath) {
7587
7907
  await ensureDir(outPath);
@@ -7664,19 +7984,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
7664
7984
  init_cjs_shims();
7665
7985
  var import_fastify = __toESM(require("fastify"), 1);
7666
7986
  var import_cors = __toESM(require("@fastify/cors"), 1);
7667
- var import_types33 = require("@neat.is/types");
7987
+ var import_types34 = require("@neat.is/types");
7668
7988
 
7669
7989
  // src/extend/index.ts
7670
7990
  init_cjs_shims();
7671
7991
  var import_node_fs26 = require("fs");
7672
- var import_node_path43 = __toESM(require("path"), 1);
7992
+ var import_node_path44 = __toESM(require("path"), 1);
7673
7993
  var import_node_os2 = __toESM(require("os"), 1);
7674
7994
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
7675
7995
 
7676
7996
  // src/installers/package-manager.ts
7677
7997
  init_cjs_shims();
7678
7998
  var import_node_fs25 = require("fs");
7679
- var import_node_path42 = __toESM(require("path"), 1);
7999
+ var import_node_path43 = __toESM(require("path"), 1);
7680
8000
  var import_node_child_process = require("child_process");
7681
8001
  var LOCKFILE_PRIORITY = [
7682
8002
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -7698,22 +8018,22 @@ async function exists2(p) {
7698
8018
  }
7699
8019
  }
7700
8020
  async function detectPackageManager(serviceDir) {
7701
- let dir = import_node_path42.default.resolve(serviceDir);
8021
+ let dir = import_node_path43.default.resolve(serviceDir);
7702
8022
  const stops = /* @__PURE__ */ new Set();
7703
8023
  for (let i = 0; i < 64; i++) {
7704
8024
  if (stops.has(dir)) break;
7705
8025
  stops.add(dir);
7706
8026
  for (const candidate of LOCKFILE_PRIORITY) {
7707
- const lockPath = import_node_path42.default.join(dir, candidate.lockfile);
8027
+ const lockPath = import_node_path43.default.join(dir, candidate.lockfile);
7708
8028
  if (await exists2(lockPath)) {
7709
8029
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
7710
8030
  }
7711
8031
  }
7712
- const parent = import_node_path42.default.dirname(dir);
8032
+ const parent = import_node_path43.default.dirname(dir);
7713
8033
  if (parent === dir) break;
7714
8034
  dir = parent;
7715
8035
  }
7716
- return { pm: "npm", cwd: import_node_path42.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8036
+ return { pm: "npm", cwd: import_node_path43.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
7717
8037
  }
7718
8038
  async function runPackageManagerInstall(cmd) {
7719
8039
  return new Promise((resolve) => {
@@ -7762,22 +8082,53 @@ async function fileExists2(p) {
7762
8082
  }
7763
8083
  }
7764
8084
  async function readPackageJson(scanPath) {
7765
- const pkgPath = import_node_path43.default.join(scanPath, "package.json");
8085
+ const pkgPath = import_node_path44.default.join(scanPath, "package.json");
7766
8086
  const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
7767
8087
  return JSON.parse(raw);
7768
8088
  }
8089
+ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
8090
+ "node_modules",
8091
+ "dist",
8092
+ "build",
8093
+ "out",
8094
+ "coverage",
8095
+ "neat-out"
8096
+ ]);
7769
8097
  async function findHookFiles(scanPath) {
7770
- const entries = await import_node_fs26.promises.readdir(scanPath);
7771
- return entries.filter(
7772
- (e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
7773
- ).sort();
8098
+ const found = [];
8099
+ const walk3 = async (dir) => {
8100
+ const entries = await import_node_fs26.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
8101
+ for (const entry of entries) {
8102
+ if (entry.isDirectory()) {
8103
+ if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
8104
+ await walk3(import_node_path44.default.join(dir, entry.name));
8105
+ } else if (entry.isFile()) {
8106
+ if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
8107
+ const rel = import_node_path44.default.relative(scanPath, import_node_path44.default.join(dir, entry.name));
8108
+ found.push(rel.split(import_node_path44.default.sep).join("/"));
8109
+ }
8110
+ }
8111
+ }
8112
+ };
8113
+ await walk3(scanPath);
8114
+ return found.sort();
8115
+ }
8116
+ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8117
+ let fallback = null;
8118
+ for (const file of hookFiles) {
8119
+ const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(scanPath, file), "utf8");
8120
+ const patched = splicedContent(content, snippet2);
8121
+ if (patched !== null) return { file, content, patched };
8122
+ if (fallback === null) fallback = { file, content };
8123
+ }
8124
+ return { file: fallback.file, content: fallback.content, patched: null };
7774
8125
  }
7775
8126
  function extendLogPath() {
7776
- return process.env.NEAT_EXTEND_LOG ?? import_node_path43.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8127
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path44.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
7777
8128
  }
7778
8129
  async function appendExtendLog(entry) {
7779
8130
  const logPath = extendLogPath();
7780
- await import_node_fs26.promises.mkdir(import_node_path43.default.dirname(logPath), { recursive: true });
8131
+ await import_node_fs26.promises.mkdir(import_node_path44.default.dirname(logPath), { recursive: true });
7781
8132
  await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
7782
8133
  }
7783
8134
  function splicedContent(fileContent, snippet2) {
@@ -7836,7 +8187,7 @@ function lookupInstrumentation(library, installedVersion) {
7836
8187
  }
7837
8188
  async function describeProjectInstrumentation(ctx) {
7838
8189
  const hookFiles = await findHookFiles(ctx.scanPath);
7839
- const envNeat = await fileExists2(import_node_path43.default.join(ctx.scanPath, ".env.neat"));
8190
+ const envNeat = await fileExists2(import_node_path44.default.join(ctx.scanPath, ".env.neat"));
7840
8191
  const registryInstrPackages = new Set(
7841
8192
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
7842
8193
  );
@@ -7858,16 +8209,22 @@ async function applyExtension(ctx, args, options) {
7858
8209
  );
7859
8210
  }
7860
8211
  for (const file of hookFiles) {
7861
- const content = await import_node_fs26.promises.readFile(import_node_path43.default.join(ctx.scanPath, file), "utf8");
8212
+ const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(ctx.scanPath, file), "utf8");
7862
8213
  if (content.includes(args.registration_snippet)) {
7863
8214
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
7864
8215
  }
7865
8216
  }
7866
- const primaryFile = hookFiles[0];
7867
- const primaryPath = import_node_path43.default.join(ctx.scanPath, primaryFile);
8217
+ const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
8218
+ if (primary.patched === null) {
8219
+ throw new Error(
8220
+ `Could not find instrumentation insertion point in ${hookFiles.join(", ")}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
8221
+ );
8222
+ }
8223
+ const primaryFile = primary.file;
8224
+ const primaryPath = import_node_path44.default.join(ctx.scanPath, primaryFile);
7868
8225
  const filesTouched = [];
7869
8226
  const depsAdded = [];
7870
- const pkgPath = import_node_path43.default.join(ctx.scanPath, "package.json");
8227
+ const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
7871
8228
  const pkg = await readPackageJson(ctx.scanPath);
7872
8229
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
7873
8230
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -7875,14 +8232,7 @@ async function applyExtension(ctx, args, options) {
7875
8232
  filesTouched.push("package.json");
7876
8233
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
7877
8234
  }
7878
- const hookContent = await import_node_fs26.promises.readFile(primaryPath, "utf8");
7879
- const patched = splicedContent(hookContent, args.registration_snippet);
7880
- if (!patched) {
7881
- throw new Error(
7882
- `Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
7883
- );
7884
- }
7885
- await import_node_fs26.promises.writeFile(primaryPath, patched, "utf8");
8235
+ await import_node_fs26.promises.writeFile(primaryPath, primary.patched, "utf8");
7886
8236
  filesTouched.push(primaryFile);
7887
8237
  const cmd = await detectPackageManager(ctx.scanPath);
7888
8238
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -7913,7 +8263,7 @@ async function dryRunExtension(ctx, args) {
7913
8263
  };
7914
8264
  }
7915
8265
  for (const file of hookFiles) {
7916
- const content = await import_node_fs26.promises.readFile(import_node_path43.default.join(ctx.scanPath, file), "utf8");
8266
+ const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(ctx.scanPath, file), "utf8");
7917
8267
  if (content.includes(args.registration_snippet)) {
7918
8268
  return {
7919
8269
  library: args.library,
@@ -7924,7 +8274,7 @@ async function dryRunExtension(ctx, args) {
7924
8274
  };
7925
8275
  }
7926
8276
  }
7927
- const primaryFile = hookFiles[0];
8277
+ const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
7928
8278
  const filesTouched = [];
7929
8279
  const depsToAdd = [];
7930
8280
  let packageJsonPatch = {};
@@ -7935,10 +8285,8 @@ async function dryRunExtension(ctx, args) {
7935
8285
  depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
7936
8286
  filesTouched.push("package.json");
7937
8287
  }
7938
- const hookContent = await import_node_fs26.promises.readFile(import_node_path43.default.join(ctx.scanPath, primaryFile), "utf8");
7939
- const patched = splicedContent(hookContent, args.registration_snippet);
7940
- if (patched) {
7941
- filesTouched.push(primaryFile);
8288
+ if (primary.patched !== null) {
8289
+ filesTouched.push(primary.file);
7942
8290
  templatePatch = `+ ${args.registration_snippet}`;
7943
8291
  } else {
7944
8292
  templatePatch = "Could not find insertion point in hook file.";
@@ -7956,7 +8304,7 @@ async function rollbackExtension(ctx, args) {
7956
8304
  if (!match) {
7957
8305
  return { undone: false, message: "no apply found for library" };
7958
8306
  }
7959
- const pkgPath = import_node_path43.default.join(ctx.scanPath, "package.json");
8307
+ const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
7960
8308
  if (await fileExists2(pkgPath)) {
7961
8309
  const pkg = await readPackageJson(ctx.scanPath);
7962
8310
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -7967,7 +8315,7 @@ async function rollbackExtension(ctx, args) {
7967
8315
  }
7968
8316
  const hookFiles = await findHookFiles(ctx.scanPath);
7969
8317
  for (const file of hookFiles) {
7970
- const filePath = import_node_path43.default.join(ctx.scanPath, file);
8318
+ const filePath = import_node_path44.default.join(ctx.scanPath, file);
7971
8319
  const content = await import_node_fs26.promises.readFile(filePath, "utf8");
7972
8320
  if (content.includes(match.registration_snippet)) {
7973
8321
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -7983,39 +8331,39 @@ async function rollbackExtension(ctx, args) {
7983
8331
 
7984
8332
  // src/divergences.ts
7985
8333
  init_cjs_shims();
7986
- var import_types31 = require("@neat.is/types");
8334
+ var import_types32 = require("@neat.is/types");
7987
8335
  function bucketKey(source, target, type) {
7988
8336
  return `${type}|${source}|${target}`;
7989
8337
  }
7990
8338
  function bucketSourceFor(graph, edge) {
7991
- if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
7992
- const parsed = (0, import_types31.parseFileId)(edge.source);
8339
+ if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
8340
+ const parsed = (0, import_types32.parseFileId)(edge.source);
7993
8341
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
7994
8342
  const target = graph.getNodeAttributes(edge.target);
7995
- if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
7996
- return (0, import_types31.serviceId)(parsed.service);
8343
+ if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
8344
+ return (0, import_types32.serviceId)(parsed.service);
7997
8345
  }
7998
8346
  function bucketEdges(graph) {
7999
8347
  const buckets2 = /* @__PURE__ */ new Map();
8000
8348
  graph.forEachEdge((id, attrs) => {
8001
8349
  const e = attrs;
8002
- const parsed = (0, import_types31.parseEdgeId)(id);
8350
+ const parsed = (0, import_types32.parseEdgeId)(id);
8003
8351
  const provenance = parsed?.provenance ?? e.provenance;
8004
8352
  const source = bucketSourceFor(graph, e);
8005
8353
  const key = bucketKey(source, e.target, e.type);
8006
8354
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
8007
8355
  switch (provenance) {
8008
- case import_types31.Provenance.EXTRACTED:
8356
+ case import_types32.Provenance.EXTRACTED:
8009
8357
  cur.extracted = e;
8010
8358
  break;
8011
- case import_types31.Provenance.OBSERVED:
8359
+ case import_types32.Provenance.OBSERVED:
8012
8360
  cur.observed = e;
8013
8361
  break;
8014
- case import_types31.Provenance.INFERRED:
8362
+ case import_types32.Provenance.INFERRED:
8015
8363
  cur.inferred = e;
8016
8364
  break;
8017
8365
  default:
8018
- if (e.provenance === import_types31.Provenance.STALE) cur.stale = e;
8366
+ if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
8019
8367
  }
8020
8368
  buckets2.set(key, cur);
8021
8369
  });
@@ -8024,12 +8372,12 @@ function bucketEdges(graph) {
8024
8372
  function nodeIsFrontier(graph, nodeId) {
8025
8373
  if (!graph.hasNode(nodeId)) return false;
8026
8374
  const attrs = graph.getNodeAttributes(nodeId);
8027
- return attrs.type === import_types31.NodeType.FrontierNode;
8375
+ return attrs.type === import_types32.NodeType.FrontierNode;
8028
8376
  }
8029
8377
  function nodeIsWebsocketChannel(graph, nodeId) {
8030
8378
  if (!graph.hasNode(nodeId)) return false;
8031
8379
  const attrs = graph.getNodeAttributes(nodeId);
8032
- return attrs.type === import_types31.NodeType.WebSocketChannelNode;
8380
+ return attrs.type === import_types32.NodeType.WebSocketChannelNode;
8033
8381
  }
8034
8382
  function clampConfidence(n) {
8035
8383
  if (!Number.isFinite(n)) return 0;
@@ -8049,14 +8397,14 @@ function gradedConfidence(edge) {
8049
8397
  return clampConfidence(confidenceForEdge(edge));
8050
8398
  }
8051
8399
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
8052
- import_types31.EdgeType.CALLS,
8053
- import_types31.EdgeType.CONNECTS_TO,
8054
- import_types31.EdgeType.PUBLISHES_TO,
8055
- import_types31.EdgeType.CONSUMES_FROM
8400
+ import_types32.EdgeType.CALLS,
8401
+ import_types32.EdgeType.CONNECTS_TO,
8402
+ import_types32.EdgeType.PUBLISHES_TO,
8403
+ import_types32.EdgeType.CONSUMES_FROM
8056
8404
  ]);
8057
8405
  function detectMissingDivergences(graph, bucket) {
8058
8406
  const out = [];
8059
- if (bucket.type === import_types31.EdgeType.CONTAINS) return out;
8407
+ if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
8060
8408
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
8061
8409
  if (!nodeIsFrontier(graph, bucket.target)) {
8062
8410
  out.push({
@@ -8097,7 +8445,7 @@ function declaredHostFor(svc) {
8097
8445
  function hasExtractedConfiguredBy(graph, svcId) {
8098
8446
  for (const edgeId of graph.outboundEdges(svcId)) {
8099
8447
  const e = graph.getEdgeAttributes(edgeId);
8100
- if (e.type === import_types31.EdgeType.CONFIGURED_BY && e.provenance === import_types31.Provenance.EXTRACTED) {
8448
+ if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
8101
8449
  return true;
8102
8450
  }
8103
8451
  }
@@ -8110,10 +8458,10 @@ function detectHostMismatch(graph, svcId, svc) {
8110
8458
  const out = [];
8111
8459
  for (const edgeId of graph.outboundEdges(svcId)) {
8112
8460
  const edge = graph.getEdgeAttributes(edgeId);
8113
- if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
8114
- if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
8461
+ if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8462
+ if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8115
8463
  const target = graph.getNodeAttributes(edge.target);
8116
- if (target.type !== import_types31.NodeType.DatabaseNode) continue;
8464
+ if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8117
8465
  const observedHost = target.host?.trim();
8118
8466
  if (!observedHost) continue;
8119
8467
  if (observedHost === declaredHost) continue;
@@ -8135,10 +8483,10 @@ function detectCompatDivergences(graph, svcId, svc) {
8135
8483
  const deps = svc.dependencies ?? {};
8136
8484
  for (const edgeId of graph.outboundEdges(svcId)) {
8137
8485
  const edge = graph.getEdgeAttributes(edgeId);
8138
- if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
8139
- if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
8486
+ if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8487
+ if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8140
8488
  const target = graph.getNodeAttributes(edge.target);
8141
- if (target.type !== import_types31.NodeType.DatabaseNode) continue;
8489
+ if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8142
8490
  for (const pair of compatPairs()) {
8143
8491
  if (pair.engine !== target.engine) continue;
8144
8492
  const declared = deps[pair.driver];
@@ -8197,7 +8545,7 @@ function suppressHostMismatchHalves(all) {
8197
8545
  for (const d of all) {
8198
8546
  if (d.type !== "host-mismatch") continue;
8199
8547
  observedHalf.add(`${d.source}->${d.target}`);
8200
- declaredHalf.add((0, import_types31.databaseId)(d.extractedHost));
8548
+ declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
8201
8549
  }
8202
8550
  if (observedHalf.size === 0) return all;
8203
8551
  return all.filter((d) => {
@@ -8216,7 +8564,7 @@ function computeDivergences(graph, opts = {}) {
8216
8564
  }
8217
8565
  graph.forEachNode((nodeId, attrs) => {
8218
8566
  const n = attrs;
8219
- if (n.type !== import_types31.NodeType.ServiceNode) return;
8567
+ if (n.type !== import_types32.NodeType.ServiceNode) return;
8220
8568
  const svc = n;
8221
8569
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
8222
8570
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -8250,7 +8598,7 @@ function computeDivergences(graph, opts = {}) {
8250
8598
  if (a.source !== b.source) return a.source.localeCompare(b.source);
8251
8599
  return a.target.localeCompare(b.target);
8252
8600
  });
8253
- return import_types31.DivergenceResultSchema.parse({
8601
+ return import_types32.DivergenceResultSchema.parse({
8254
8602
  divergences: filtered,
8255
8603
  totalAffected: filtered.length,
8256
8604
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -8381,23 +8729,23 @@ function canonicalJson(value) {
8381
8729
 
8382
8730
  // src/projects.ts
8383
8731
  init_cjs_shims();
8384
- var import_node_path44 = __toESM(require("path"), 1);
8732
+ var import_node_path45 = __toESM(require("path"), 1);
8385
8733
  function pathsForProject(project, baseDir) {
8386
8734
  if (project === DEFAULT_PROJECT) {
8387
8735
  return {
8388
- snapshotPath: import_node_path44.default.join(baseDir, "graph.json"),
8389
- errorsPath: import_node_path44.default.join(baseDir, "errors.ndjson"),
8390
- staleEventsPath: import_node_path44.default.join(baseDir, "stale-events.ndjson"),
8391
- embeddingsCachePath: import_node_path44.default.join(baseDir, "embeddings.json"),
8392
- policyViolationsPath: import_node_path44.default.join(baseDir, "policy-violations.ndjson")
8736
+ snapshotPath: import_node_path45.default.join(baseDir, "graph.json"),
8737
+ errorsPath: import_node_path45.default.join(baseDir, "errors.ndjson"),
8738
+ staleEventsPath: import_node_path45.default.join(baseDir, "stale-events.ndjson"),
8739
+ embeddingsCachePath: import_node_path45.default.join(baseDir, "embeddings.json"),
8740
+ policyViolationsPath: import_node_path45.default.join(baseDir, "policy-violations.ndjson")
8393
8741
  };
8394
8742
  }
8395
8743
  return {
8396
- snapshotPath: import_node_path44.default.join(baseDir, `${project}.json`),
8397
- errorsPath: import_node_path44.default.join(baseDir, `errors.${project}.ndjson`),
8398
- staleEventsPath: import_node_path44.default.join(baseDir, `stale-events.${project}.ndjson`),
8399
- embeddingsCachePath: import_node_path44.default.join(baseDir, `embeddings.${project}.json`),
8400
- policyViolationsPath: import_node_path44.default.join(baseDir, `policy-violations.${project}.ndjson`)
8744
+ snapshotPath: import_node_path45.default.join(baseDir, `${project}.json`),
8745
+ errorsPath: import_node_path45.default.join(baseDir, `errors.${project}.ndjson`),
8746
+ staleEventsPath: import_node_path45.default.join(baseDir, `stale-events.${project}.ndjson`),
8747
+ embeddingsCachePath: import_node_path45.default.join(baseDir, `embeddings.${project}.json`),
8748
+ policyViolationsPath: import_node_path45.default.join(baseDir, `policy-violations.${project}.ndjson`)
8401
8749
  };
8402
8750
  }
8403
8751
  var Projects = class {
@@ -8435,23 +8783,23 @@ var Projects = class {
8435
8783
  init_cjs_shims();
8436
8784
  var import_node_fs28 = require("fs");
8437
8785
  var import_node_os3 = __toESM(require("os"), 1);
8438
- var import_node_path45 = __toESM(require("path"), 1);
8439
- var import_types32 = require("@neat.is/types");
8786
+ var import_node_path46 = __toESM(require("path"), 1);
8787
+ var import_types33 = require("@neat.is/types");
8440
8788
  var LOCK_TIMEOUT_MS = 5e3;
8441
8789
  var LOCK_RETRY_MS = 50;
8442
8790
  function neatHome() {
8443
8791
  const override = process.env.NEAT_HOME;
8444
- if (override && override.length > 0) return import_node_path45.default.resolve(override);
8445
- return import_node_path45.default.join(import_node_os3.default.homedir(), ".neat");
8792
+ if (override && override.length > 0) return import_node_path46.default.resolve(override);
8793
+ return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
8446
8794
  }
8447
8795
  function registryPath() {
8448
- return import_node_path45.default.join(neatHome(), "projects.json");
8796
+ return import_node_path46.default.join(neatHome(), "projects.json");
8449
8797
  }
8450
8798
  function registryLockPath() {
8451
- return import_node_path45.default.join(neatHome(), "projects.json.lock");
8799
+ return import_node_path46.default.join(neatHome(), "projects.json.lock");
8452
8800
  }
8453
8801
  function daemonPidPath() {
8454
- return import_node_path45.default.join(neatHome(), "neatd.pid");
8802
+ return import_node_path46.default.join(neatHome(), "neatd.pid");
8455
8803
  }
8456
8804
  function isPidAliveDefault(pid) {
8457
8805
  try {
@@ -8511,7 +8859,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
8511
8859
  }
8512
8860
  }
8513
8861
  async function normalizeProjectPath(input) {
8514
- const resolved = import_node_path45.default.resolve(input);
8862
+ const resolved = import_node_path46.default.resolve(input);
8515
8863
  try {
8516
8864
  return await import_node_fs28.promises.realpath(resolved);
8517
8865
  } catch {
@@ -8519,7 +8867,7 @@ async function normalizeProjectPath(input) {
8519
8867
  }
8520
8868
  }
8521
8869
  async function writeAtomically(target, contents) {
8522
- await import_node_fs28.promises.mkdir(import_node_path45.default.dirname(target), { recursive: true });
8870
+ await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
8523
8871
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
8524
8872
  const fd = await import_node_fs28.promises.open(tmp, "w");
8525
8873
  try {
@@ -8532,7 +8880,7 @@ async function writeAtomically(target, contents) {
8532
8880
  }
8533
8881
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
8534
8882
  const deadline = Date.now() + timeoutMs;
8535
- await import_node_fs28.promises.mkdir(import_node_path45.default.dirname(lockPath), { recursive: true });
8883
+ await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
8536
8884
  let probedHolder = false;
8537
8885
  while (true) {
8538
8886
  try {
@@ -8585,10 +8933,10 @@ async function readRegistry() {
8585
8933
  throw err;
8586
8934
  }
8587
8935
  const parsed = JSON.parse(raw);
8588
- return import_types32.RegistryFileSchema.parse(parsed);
8936
+ return import_types33.RegistryFileSchema.parse(parsed);
8589
8937
  }
8590
8938
  async function writeRegistry(reg) {
8591
- const validated = import_types32.RegistryFileSchema.parse(reg);
8939
+ const validated = import_types33.RegistryFileSchema.parse(reg);
8592
8940
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
8593
8941
  }
8594
8942
  var ProjectNameCollisionError = class extends Error {
@@ -8783,7 +9131,7 @@ init_auth();
8783
9131
  // src/connectors-config.ts
8784
9132
  init_cjs_shims();
8785
9133
  var import_node_os4 = __toESM(require("os"), 1);
8786
- var import_node_path46 = __toESM(require("path"), 1);
9134
+ var import_node_path47 = __toESM(require("path"), 1);
8787
9135
  var import_node_fs29 = require("fs");
8788
9136
  var CONNECTORS_CONFIG_VERSION = 1;
8789
9137
  var EnvRefUnsetError = class extends Error {
@@ -8798,11 +9146,11 @@ var EnvRefUnsetError = class extends Error {
8798
9146
  };
8799
9147
  function neatHome2() {
8800
9148
  const override = process.env.NEAT_HOME;
8801
- if (override && override.length > 0) return import_node_path46.default.resolve(override);
8802
- return import_node_path46.default.join(import_node_os4.default.homedir(), ".neat");
9149
+ if (override && override.length > 0) return import_node_path47.default.resolve(override);
9150
+ return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
8803
9151
  }
8804
9152
  function connectorsConfigPath(home = neatHome2()) {
8805
- return import_node_path46.default.join(home, "connectors.json");
9153
+ return import_node_path47.default.join(home, "connectors.json");
8806
9154
  }
8807
9155
  var MODE_MASK_LOOSER_THAN_0600 = 63;
8808
9156
  async function warnIfModeLooserThan0600(file) {
@@ -9138,11 +9486,11 @@ function registerRoutes(scope, ctx) {
9138
9486
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
9139
9487
  const parsed = [];
9140
9488
  for (const c of candidates) {
9141
- const r = import_types33.DivergenceTypeSchema.safeParse(c);
9489
+ const r = import_types34.DivergenceTypeSchema.safeParse(c);
9142
9490
  if (!r.success) {
9143
9491
  return reply.code(400).send({
9144
9492
  error: `unknown divergence type "${c}"`,
9145
- allowed: import_types33.DivergenceTypeSchema.options
9493
+ allowed: import_types34.DivergenceTypeSchema.options
9146
9494
  });
9147
9495
  }
9148
9496
  parsed.push(r.data);
@@ -9413,7 +9761,7 @@ function registerRoutes(scope, ctx) {
9413
9761
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
9414
9762
  let violations = await log.readAll();
9415
9763
  if (req.query.severity) {
9416
- const sev = import_types33.PolicySeveritySchema.safeParse(req.query.severity);
9764
+ const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
9417
9765
  if (!sev.success) {
9418
9766
  return reply.code(400).send({
9419
9767
  error: "invalid severity",
@@ -9452,7 +9800,7 @@ function registerRoutes(scope, ctx) {
9452
9800
  scope.post("/policies/check", async (req, reply) => {
9453
9801
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
9454
9802
  if (!proj) return;
9455
- const parsed = import_types33.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9803
+ const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9456
9804
  if (!parsed.success) {
9457
9805
  return reply.code(400).send({
9458
9806
  error: "invalid /policies/check body",
@@ -9586,7 +9934,7 @@ function registerRoutes(scope, ctx) {
9586
9934
  });
9587
9935
  }
9588
9936
  async function buildApi(opts) {
9589
- const app = (0, import_fastify.default)({ logger: false });
9937
+ const app = (0, import_fastify.default)({ logger: false, routerOptions: { maxParamLength: 1024 } });
9590
9938
  await app.register(import_cors.default, { origin: true });
9591
9939
  const env = readAuthEnv();
9592
9940
  const authToken = opts.authToken ?? env.authToken;
@@ -9718,21 +10066,21 @@ init_otel_grpc();
9718
10066
  // src/daemon.ts
9719
10067
  init_cjs_shims();
9720
10068
  var import_node_fs31 = require("fs");
9721
- var import_node_path50 = __toESM(require("path"), 1);
10069
+ var import_node_path51 = __toESM(require("path"), 1);
9722
10070
  var import_node_module = require("module");
9723
10071
  init_otel();
9724
10072
 
9725
10073
  // src/connectors/index.ts
9726
10074
  init_cjs_shims();
9727
- var import_types34 = require("@neat.is/types");
10075
+ var import_types35 = require("@neat.is/types");
9728
10076
  var NO_ENV = "unknown";
9729
10077
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
9730
10078
  if (!graph.hasNode(targetNodeId)) return void 0;
9731
10079
  const sites = [];
9732
10080
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
9733
10081
  const edge = graph.getEdgeAttributes(edgeId);
9734
- if (edge.provenance !== import_types34.Provenance.EXTRACTED) continue;
9735
- const parsed = (0, import_types34.parseFileId)(edge.source);
10082
+ if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
10083
+ const parsed = (0, import_types35.parseFileId)(edge.source);
9736
10084
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
9737
10085
  const site = { relPath: edge.evidence.file };
9738
10086
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -9743,7 +10091,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
9743
10091
  function routeCallSiteFor(graph, targetNodeId) {
9744
10092
  if (!graph.hasNode(targetNodeId)) return void 0;
9745
10093
  const attrs = graph.getNodeAttributes(targetNodeId);
9746
- if (attrs.type !== import_types34.NodeType.RouteNode || !attrs.path) return void 0;
10094
+ if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
9747
10095
  const site = { relPath: attrs.path };
9748
10096
  if (attrs.line !== void 0) site.line = attrs.line;
9749
10097
  return site;
@@ -9771,8 +10119,8 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9771
10119
  line: callSite.line
9772
10120
  } : void 0;
9773
10121
  const calls = Math.trunc(signal.callCount);
9774
- if (calls < 1) continue;
9775
- const errors = Math.min(Math.max(Math.trunc(signal.errorCount), 0), calls);
10122
+ if (!Number.isFinite(calls) || calls < 1) continue;
10123
+ const errors = Number.isFinite(signal.errorCount) ? Math.min(Math.max(Math.trunc(signal.errorCount), 0), calls) : 0;
9776
10124
  let created = false;
9777
10125
  let ok = true;
9778
10126
  for (let i = 0; i < calls; i++) {
@@ -9917,7 +10265,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
9917
10265
  supabase: { capacity: 30, refillMs: 1e4 },
9918
10266
  // Not a documented API limit at all — a self-imposed ceiling on the raw
9919
10267
  // pg_stat_statements connection (see module header above).
9920
- "supabase-postgres": { capacity: 20, refillMs: 3e3 }
10268
+ "supabase-postgres": { capacity: 20, refillMs: 3e3 },
10269
+ // Push provider (ADR-146): the Drains REST API is touched only by `neat
10270
+ // connector add/remove/test` (provision/deprovision/validate), never a poll
10271
+ // loop, so this bucket is exercised a handful of times per command. Kept
10272
+ // conservative pending a documented Drains-API rate limit.
10273
+ vercel: { capacity: 20, refillMs: 5e3 }
9921
10274
  };
9922
10275
  var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
9923
10276
  function defaultRateLimitFor(provider) {
@@ -10209,17 +10562,20 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
10209
10562
  // src/connectors/supabase/map.ts
10210
10563
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
10211
10564
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
10212
- function targetFromRestPath(path51) {
10213
- const rpcMatch = REST_RPC_PATH_RE.exec(path51);
10565
+ function targetFromRestPath(path52) {
10566
+ const rpcMatch = REST_RPC_PATH_RE.exec(path52);
10214
10567
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
10215
- const tableMatch = REST_TABLE_PATH_RE.exec(path51);
10568
+ const tableMatch = REST_TABLE_PATH_RE.exec(path52);
10216
10569
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
10217
10570
  return null;
10218
10571
  }
10219
10572
  var ERROR_STATUS_THRESHOLD = 500;
10220
10573
  function mapEdgeLogRowsToSignals(rows) {
10221
10574
  const buckets2 = /* @__PURE__ */ new Map();
10575
+ if (!Array.isArray(rows)) return [];
10222
10576
  for (const row of rows) {
10577
+ if (!row || typeof row !== "object") continue;
10578
+ if (typeof row.path !== "string" || typeof row.timestamp !== "string") continue;
10223
10579
  const target = targetFromRestPath(row.path);
10224
10580
  if (!target) continue;
10225
10581
  const key = `${target.targetKind}:${target.name}`;
@@ -10260,9 +10616,12 @@ function tableNameFromQueryText(query) {
10260
10616
  function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
10261
10617
  const signals = [];
10262
10618
  const seen = /* @__PURE__ */ new Set();
10619
+ if (!Array.isArray(rows)) return signals;
10263
10620
  for (const row of rows) {
10264
- seen.add(row.queryid);
10621
+ if (!row || typeof row !== "object" || typeof row.queryid !== "string") continue;
10265
10622
  const calls = Number(row.calls);
10623
+ if (!Number.isFinite(calls)) continue;
10624
+ seen.add(row.queryid);
10266
10625
  const prior = previous.get(row.queryid);
10267
10626
  previous.set(row.queryid, { calls });
10268
10627
  if (!prior || calls < prior.calls) continue;
@@ -10315,23 +10674,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10315
10674
 
10316
10675
  // src/connectors/supabase/resolve.ts
10317
10676
  init_cjs_shims();
10318
- var import_types36 = require("@neat.is/types");
10677
+ var import_types37 = require("@neat.is/types");
10319
10678
  function createSupabaseResolveTarget(graph, config) {
10320
10679
  return (signal, _ctx) => {
10321
10680
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10322
10681
  return null;
10323
10682
  }
10324
- const subResourceId = (0, import_types36.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10683
+ const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10325
10684
  if (graph.hasNode(subResourceId)) {
10326
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10685
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10327
10686
  }
10328
- const bareResourceId = (0, import_types36.infraId)(signal.targetKind, signal.targetName);
10687
+ const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
10329
10688
  if (graph.hasNode(bareResourceId)) {
10330
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10689
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10331
10690
  }
10332
- const projectLevelId = (0, import_types36.infraId)("supabase", config.nodeRef);
10691
+ const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
10333
10692
  if (graph.hasNode(projectLevelId)) {
10334
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10693
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10335
10694
  }
10336
10695
  return null;
10337
10696
  };
@@ -10424,7 +10783,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10424
10783
 
10425
10784
  // src/connectors/railway/index.ts
10426
10785
  init_cjs_shims();
10427
- var import_types40 = require("@neat.is/types");
10786
+ var import_types41 = require("@neat.is/types");
10428
10787
 
10429
10788
  // src/connectors/railway/client.ts
10430
10789
  init_cjs_shims();
@@ -10550,7 +10909,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
10550
10909
  const out = [];
10551
10910
  graph.forEachNode((_id, attrs) => {
10552
10911
  const node = attrs;
10553
- if (node.type !== import_types40.NodeType.RouteNode) return;
10912
+ if (node.type !== import_types41.NodeType.RouteNode) return;
10554
10913
  const route = attrs;
10555
10914
  if (route.service !== serviceName) return;
10556
10915
  out.push({
@@ -10586,7 +10945,12 @@ function upsertBucket(buckets2, key, isError, timestamp, build) {
10586
10945
  }
10587
10946
  function mapRailwayHttpLogsToSignals(entries, routeIndex) {
10588
10947
  const buckets2 = /* @__PURE__ */ new Map();
10948
+ if (!Array.isArray(entries)) return [];
10589
10949
  for (const entry of entries) {
10950
+ if (!entry || typeof entry !== "object") continue;
10951
+ if (typeof entry.method !== "string" || typeof entry.path !== "string" || typeof entry.timestamp !== "string") {
10952
+ continue;
10953
+ }
10590
10954
  const method = entry.method.toUpperCase();
10591
10955
  const normalizedPath = normalizePathTemplate(entry.path);
10592
10956
  const match = findRailwayRoute(routeIndex, method, normalizedPath);
@@ -10625,8 +10989,11 @@ function mapRailwayHttpLogsToSignals(entries, routeIndex) {
10625
10989
  }
10626
10990
  function mapRailwayNetworkFlowLogsToSignals(entries) {
10627
10991
  const buckets2 = /* @__PURE__ */ new Map();
10992
+ if (!Array.isArray(entries)) return [];
10628
10993
  for (const entry of entries) {
10629
- if (!entry.peerServiceId) continue;
10994
+ if (!entry || typeof entry !== "object") continue;
10995
+ if (typeof entry.peerServiceId !== "string" || entry.peerServiceId.length === 0) continue;
10996
+ if (typeof entry.timestamp !== "string") continue;
10630
10997
  const isError = entry.dropCause !== null && entry.dropCause !== "";
10631
10998
  upsertBucket(buckets2, entry.peerServiceId, isError, entry.timestamp, () => ({
10632
10999
  targetKind: PEER_SERVICE_TARGET_KIND,
@@ -10646,12 +11013,12 @@ function createRailwayResolveTarget(config) {
10646
11013
  const serviceName = config.serviceNameById[config.serviceId];
10647
11014
  if (!serviceName) return null;
10648
11015
  if (signal.targetKind === ROUTE_TARGET_KIND) {
10649
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types40.EdgeType.CALLS };
11016
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
10650
11017
  }
10651
11018
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
10652
11019
  const peerName = config.serviceNameById[signal.targetName];
10653
11020
  if (!peerName) return null;
10654
- return { targetNodeId: (0, import_types40.serviceId)(peerName), serviceName, edgeType: import_types40.EdgeType.CONNECTS_TO };
11021
+ return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
10655
11022
  }
10656
11023
  return null;
10657
11024
  };
@@ -10754,7 +11121,7 @@ async function fetchHttpRequestLogEntries(creds, sinceIso) {
10754
11121
  throw new Error(`Cloud Logging entries.list failed: ${res.status} ${res.statusText}`);
10755
11122
  }
10756
11123
  const json = await res.json();
10757
- out.push(...json.entries ?? []);
11124
+ if (Array.isArray(json.entries)) out.push(...json.entries);
10758
11125
  if (!json.nextPageToken) break;
10759
11126
  pageToken = json.nextPageToken;
10760
11127
  }
@@ -10775,9 +11142,9 @@ function parseFirebaseTargetName(targetName) {
10775
11142
  const secondSep = rest.indexOf(FIELD_SEP);
10776
11143
  if (secondSep === -1) return null;
10777
11144
  const method = rest.slice(0, secondSep);
10778
- const path51 = rest.slice(secondSep + 1);
10779
- if (!resourceName || !method || !path51) return null;
10780
- return { resourceName, method, path: path51 };
11145
+ const path52 = rest.slice(secondSep + 1);
11146
+ if (!resourceName || !method || !path52) return null;
11147
+ return { resourceName, method, path: path52 };
10781
11148
  }
10782
11149
  function resourceNameFor(type, labels) {
10783
11150
  if (!labels) return null;
@@ -10791,7 +11158,7 @@ function resourceNameFor(type, labels) {
10791
11158
  }
10792
11159
  }
10793
11160
  function pathFromRequestUrl(requestUrl) {
10794
- if (!requestUrl) return null;
11161
+ if (typeof requestUrl !== "string" || requestUrl.length === 0) return null;
10795
11162
  if (requestUrl.startsWith("/")) {
10796
11163
  const withoutQuery = requestUrl.split("?")[0];
10797
11164
  return withoutQuery && withoutQuery.length > 0 ? withoutQuery : "/";
@@ -10806,22 +11173,23 @@ function pathFromRequestUrl(requestUrl) {
10806
11173
  }
10807
11174
  var ERROR_STATUS_THRESHOLD2 = 500;
10808
11175
  function mapLogEntryToSignal(entry) {
11176
+ if (!entry || typeof entry !== "object") return null;
10809
11177
  const resourceType = entry.resource?.type;
10810
11178
  if (!resourceType || !isFirebaseResourceType(resourceType)) return null;
10811
11179
  const resourceName = resourceNameFor(resourceType, entry.resource?.labels);
10812
11180
  if (!resourceName) return null;
10813
11181
  const req = entry.httpRequest;
10814
11182
  if (!req) return null;
10815
- if (!req.requestMethod) return null;
11183
+ if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
10816
11184
  const method = req.requestMethod.toUpperCase();
10817
- const path51 = pathFromRequestUrl(req.requestUrl);
10818
- if (path51 === null) return null;
11185
+ const path52 = pathFromRequestUrl(req.requestUrl);
11186
+ if (path52 === null) return null;
10819
11187
  const timestamp = entry.timestamp;
10820
- if (!timestamp) return null;
11188
+ if (typeof timestamp !== "string" || timestamp.length === 0) return null;
10821
11189
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
10822
11190
  return {
10823
11191
  targetKind: resourceType,
10824
- targetName: packFirebaseTargetName({ resourceName, method, path: path51 }),
11192
+ targetName: packFirebaseTargetName({ resourceName, method, path: path52 }),
10825
11193
  callCount: 1,
10826
11194
  errorCount: isError ? 1 : 0,
10827
11195
  lastObservedIso: timestamp
@@ -10838,7 +11206,7 @@ function mapLogEntriesToSignals(entries) {
10838
11206
 
10839
11207
  // src/connectors/firebase/resolve.ts
10840
11208
  init_cjs_shims();
10841
- var import_types41 = require("@neat.is/types");
11209
+ var import_types42 = require("@neat.is/types");
10842
11210
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
10843
11211
  switch (resourceType) {
10844
11212
  case "cloud_function":
@@ -10853,7 +11221,7 @@ function routeEntriesFor(graph, serviceName) {
10853
11221
  const entries = [];
10854
11222
  graph.forEachNode((_id, attrs) => {
10855
11223
  const node = attrs;
10856
- if (node.type !== import_types41.NodeType.RouteNode) return;
11224
+ if (node.type !== import_types42.NodeType.RouteNode) return;
10857
11225
  const route = attrs;
10858
11226
  if (route.service !== serviceName) return;
10859
11227
  entries.push({
@@ -10885,7 +11253,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
10885
11253
  return {
10886
11254
  targetNodeId: match.routeNodeId,
10887
11255
  serviceName,
10888
- edgeType: import_types41.EdgeType.CALLS
11256
+ edgeType: import_types42.EdgeType.CALLS
10889
11257
  };
10890
11258
  };
10891
11259
  }
@@ -10912,7 +11280,7 @@ init_cjs_shims();
10912
11280
 
10913
11281
  // src/connectors/cloudflare/connector.ts
10914
11282
  init_cjs_shims();
10915
- var import_types43 = require("@neat.is/types");
11283
+ var import_types44 = require("@neat.is/types");
10916
11284
 
10917
11285
  // src/connectors/cloudflare/client.ts
10918
11286
  init_cjs_shims();
@@ -10969,7 +11337,7 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
10969
11337
  throw new Error(`cloudflare connector: telemetry query returned an error (${message})`);
10970
11338
  }
10971
11339
  const events = payload.result?.events?.events;
10972
- if (events === void 0) {
11340
+ if (!Array.isArray(events)) {
10973
11341
  console.warn(
10974
11342
  "[neat connector] cloudflare: telemetry query returned success:true but no result.events.events array \u2014 the response shape may have changed; treating as zero events this tick"
10975
11343
  );
@@ -10999,7 +11367,7 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
10999
11367
  ]);
11000
11368
  var LEADING_TOKEN_RE = /^(\S+)\s+\S/;
11001
11369
  function parseHttpMethodFromTrigger(trigger) {
11002
- if (!trigger) return null;
11370
+ if (typeof trigger !== "string") return null;
11003
11371
  const match = LEADING_TOKEN_RE.exec(trigger.trim());
11004
11372
  const token = match?.[1];
11005
11373
  if (!token) return null;
@@ -11015,25 +11383,28 @@ function parsePathFromTrigger(trigger) {
11015
11383
  }
11016
11384
  var ERROR_STATUS_THRESHOLD3 = 500;
11017
11385
  function mapEventToSignal(event) {
11386
+ if (!event || typeof event !== "object") return null;
11018
11387
  const metadata = event.$metadata;
11019
11388
  const workers = event.$workers;
11020
11389
  const method = parseHttpMethodFromTrigger(metadata?.trigger);
11021
11390
  if (!method) return null;
11022
11391
  const scriptName = workers?.scriptName ?? metadata?.service;
11023
- if (!scriptName) return null;
11392
+ if (typeof scriptName !== "string" || scriptName.length === 0) return null;
11024
11393
  const timestampMs = event.timestamp ?? metadata?.startTime;
11025
11394
  if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs)) return null;
11395
+ const observedAt = new Date(timestampMs);
11396
+ if (Number.isNaN(observedAt.getTime())) return null;
11026
11397
  const statusCode = metadata?.statusCode;
11027
11398
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
11028
- const path51 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11399
+ const path52 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11029
11400
  return {
11030
11401
  targetKind: CLOUDFLARE_TARGET_KIND,
11031
11402
  targetName: scriptName,
11032
11403
  callCount: 1,
11033
11404
  errorCount: isError ? 1 : 0,
11034
- lastObservedIso: new Date(timestampMs).toISOString(),
11405
+ lastObservedIso: observedAt.toISOString(),
11035
11406
  method,
11036
- ...path51 ? { path: path51 } : {},
11407
+ ...path52 ? { path: path52 } : {},
11037
11408
  ...typeof statusCode === "number" ? { statusCode } : {},
11038
11409
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
11039
11410
  };
@@ -11073,19 +11444,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
11073
11444
  graph.forEachNode((id, attrs) => {
11074
11445
  if (found) return;
11075
11446
  const a = attrs;
11076
- if (a.type === import_types43.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11447
+ if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11077
11448
  found = id;
11078
11449
  }
11079
11450
  });
11080
11451
  return found;
11081
11452
  }
11082
- function findMatchingRouteNode(graph, serviceName, method, path51) {
11083
- const normalizedPath = normalizePathTemplate(path51);
11453
+ function findMatchingRouteNode(graph, serviceName, method, path52) {
11454
+ const normalizedPath = normalizePathTemplate(path52);
11084
11455
  let found = null;
11085
11456
  graph.forEachNode((id, attrs) => {
11086
11457
  if (found) return;
11087
11458
  const a = attrs;
11088
- if (a.type !== import_types43.NodeType.RouteNode || a.service !== serviceName) return;
11459
+ if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
11089
11460
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11090
11461
  const routeMethod = (a.method ?? "").toUpperCase();
11091
11462
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11097,18 +11468,18 @@ function createCloudflareResolveTarget(config, graph) {
11097
11468
  return (signal) => {
11098
11469
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
11099
11470
  const scriptName = signal.targetName;
11100
- const { method, path: path51 } = signal;
11471
+ const { method, path: path52 } = signal;
11101
11472
  const resolveRouteGrain = (serviceName, wholeFileId) => {
11102
- if (!method || !path51) return wholeFileId;
11103
- return findMatchingRouteNode(graph, serviceName, method, path51) ?? wholeFileId;
11473
+ if (!method || !path52) return wholeFileId;
11474
+ return findMatchingRouteNode(graph, serviceName, method, path52) ?? wholeFileId;
11104
11475
  };
11105
11476
  const mapping = config.workers?.[scriptName];
11106
11477
  if (mapping) {
11107
- const wholeFileId = (0, import_types43.fileId)(mapping.service, mapping.entryFile);
11478
+ const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
11108
11479
  return {
11109
11480
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11110
11481
  serviceName: mapping.service,
11111
- edgeType: import_types43.EdgeType.CALLS
11482
+ edgeType: import_types44.EdgeType.CALLS
11112
11483
  };
11113
11484
  }
11114
11485
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11117,18 +11488,124 @@ function createCloudflareResolveTarget(config, graph) {
11117
11488
  return {
11118
11489
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11119
11490
  serviceName: fileNode.service,
11120
- edgeType: import_types43.EdgeType.CALLS
11491
+ edgeType: import_types44.EdgeType.CALLS
11121
11492
  };
11122
11493
  }
11123
11494
  return {
11124
- targetNodeId: (0, import_types43.infraId)("cloudflare-worker", scriptName),
11495
+ targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
11125
11496
  serviceName: scriptName,
11126
- edgeType: import_types43.EdgeType.CALLS,
11497
+ edgeType: import_types44.EdgeType.CALLS,
11127
11498
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11128
11499
  };
11129
11500
  };
11130
11501
  }
11131
11502
 
11503
+ // src/connectors/vercel/index.ts
11504
+ init_cjs_shims();
11505
+
11506
+ // src/connectors/vercel/client.ts
11507
+ init_cjs_shims();
11508
+ var DEFAULT_API_BASE_URL = "https://api.vercel.com";
11509
+ var DEFAULT_DRAIN_NAME = "neat-otlp";
11510
+ var TRACE_SCHEMAS = { trace: { version: "v1" } };
11511
+ function apiBase(config) {
11512
+ return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
11513
+ }
11514
+ function teamQuery(config) {
11515
+ return `?teamId=${encodeURIComponent(config.teamId)}`;
11516
+ }
11517
+ function drainDelivery(config, otelToken) {
11518
+ return {
11519
+ type: "http",
11520
+ endpoint: config.endpoint,
11521
+ encoding: "json",
11522
+ headers: bearerAuthHeader(otelToken),
11523
+ ...config.secret ? { secret: config.secret } : {}
11524
+ };
11525
+ }
11526
+ async function describeError(res) {
11527
+ try {
11528
+ const data = await res.json();
11529
+ const message = data?.error?.message;
11530
+ return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
11531
+ } catch {
11532
+ return "";
11533
+ }
11534
+ }
11535
+ async function createVercelDrain(config, credentials, fetchImpl = fetch) {
11536
+ const projectIds = config.projectIds ?? [];
11537
+ const body = {
11538
+ name: config.drainName ?? DEFAULT_DRAIN_NAME,
11539
+ projects: projectIds.length > 0 ? "some" : "all",
11540
+ ...projectIds.length > 0 ? { projectIds } : {},
11541
+ schemas: TRACE_SCHEMAS,
11542
+ delivery: drainDelivery(config, credentials.otelToken),
11543
+ source: { kind: "self-served" }
11544
+ };
11545
+ const res = await junctionFetch(
11546
+ `${apiBase(config)}/v1/drains${teamQuery(config)}`,
11547
+ {
11548
+ method: "POST",
11549
+ headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
11550
+ body: JSON.stringify(body)
11551
+ },
11552
+ { provider: "vercel", accountKey: config.teamId, fetchImpl }
11553
+ );
11554
+ if (!res.ok) {
11555
+ throw new Error(
11556
+ `vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
11557
+ );
11558
+ }
11559
+ const payload = await res.json().catch(() => null);
11560
+ if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
11561
+ throw new Error(
11562
+ "vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
11563
+ );
11564
+ }
11565
+ return {
11566
+ id: payload.id,
11567
+ ...payload.status ? { status: payload.status } : {},
11568
+ ...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
11569
+ };
11570
+ }
11571
+ async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
11572
+ const res = await junctionFetch(
11573
+ `${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
11574
+ { method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
11575
+ { provider: "vercel", accountKey: config.teamId, fetchImpl }
11576
+ );
11577
+ if (res.ok || res.status === 404) return;
11578
+ throw new Error(
11579
+ `vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
11580
+ );
11581
+ }
11582
+ async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
11583
+ const res = await junctionFetch(
11584
+ `${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
11585
+ {
11586
+ method: "POST",
11587
+ headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
11588
+ body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
11589
+ },
11590
+ { provider: "vercel", accountKey: config.teamId, fetchImpl }
11591
+ );
11592
+ if (res.status === 401 || res.status === 403) {
11593
+ return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
11594
+ }
11595
+ if (!res.ok) {
11596
+ return {
11597
+ status: "failure",
11598
+ error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
11599
+ };
11600
+ }
11601
+ const payload = await res.json().catch(() => null);
11602
+ return {
11603
+ ...payload?.status ? { status: payload.status } : {},
11604
+ ...payload?.error ? { error: payload.error } : {},
11605
+ ...payload?.endpoint ? { endpoint: payload.endpoint } : {}
11606
+ };
11607
+ }
11608
+
11132
11609
  // src/connectors/registry.ts
11133
11610
  var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
11134
11611
  async function authProbe(input) {
@@ -11280,6 +11757,91 @@ var PROVIDER_DISPATCH = {
11280
11757
  }
11281
11758
  }
11282
11759
  };
11760
+ function vercelCredsFrom(credentials) {
11761
+ return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
11762
+ }
11763
+ function vercelConfigFromOptions(options) {
11764
+ const raw = options.projectIds;
11765
+ let projectIds;
11766
+ if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
11767
+ else if (typeof raw === "string" && raw.trim().length > 0) {
11768
+ projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
11769
+ }
11770
+ return {
11771
+ teamId: String(options.teamId ?? ""),
11772
+ endpoint: String(options.endpoint ?? ""),
11773
+ ...projectIds && projectIds.length > 0 ? { projectIds } : {},
11774
+ ...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
11775
+ ...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
11776
+ ...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
11777
+ ...typeof options.secret === "string" ? { secret: options.secret } : {}
11778
+ };
11779
+ }
11780
+ var PUSH_PROVIDER_DISPATCH = {
11781
+ vercel: {
11782
+ provider: "vercel",
11783
+ // The Vercel access token is the "primary" secret a single `--token`
11784
+ // populates; `otelToken` (the daemon's OTLP bearer) is the second field.
11785
+ primaryCredentialKey: "token",
11786
+ requiredCredentialFields: ["token", "otelToken"],
11787
+ // teamId scopes every Drains call; endpoint is where the drain delivers.
11788
+ // projectIds is optional (absent → the drain covers the whole team).
11789
+ requiredOptionFields: ["teamId", "endpoint"],
11790
+ // POST /v1/drains/test — authenticates the token and pings the endpoint
11791
+ // with a sample event, so `success` means the credential is live *and* the
11792
+ // daemon's OTLP endpoint is reachable and accepted the drain's bearer.
11793
+ async validate({ credentials, options, fetchImpl }) {
11794
+ const result = await testVercelDrainDelivery(
11795
+ vercelConfigFromOptions(options),
11796
+ vercelCredsFrom(credentials),
11797
+ fetchImpl
11798
+ );
11799
+ if (result.status === "success") return { ok: true };
11800
+ return {
11801
+ ok: false,
11802
+ reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
11803
+ };
11804
+ },
11805
+ // POST /v1/drains — creates the trace drain, returns its id to store in
11806
+ // `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
11807
+ // not a failure (the entry still points at a real drain).
11808
+ async provision({ credentials, options, fetchImpl }) {
11809
+ try {
11810
+ const created = await createVercelDrain(
11811
+ vercelConfigFromOptions(options),
11812
+ vercelCredsFrom(credentials),
11813
+ fetchImpl
11814
+ );
11815
+ const note = created.status && created.status !== "enabled" ? `the drain was created but its status is "${created.status}"${created.disabledReason ? ` (${created.disabledReason})` : ""} \u2014 check the Vercel dashboard` : void 0;
11816
+ return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
11817
+ } catch (err) {
11818
+ return { ok: false, reason: err.message };
11819
+ }
11820
+ },
11821
+ // DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
11822
+ // success). No recorded drainId → nothing to delete, still a success.
11823
+ async deprovision({ credentials, options, fetchImpl }) {
11824
+ const drainId = typeof options.drainId === "string" ? options.drainId : "";
11825
+ if (!drainId) {
11826
+ return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
11827
+ }
11828
+ try {
11829
+ await deleteVercelDrain(
11830
+ vercelConfigFromOptions(options),
11831
+ drainId,
11832
+ vercelCredsFrom(credentials),
11833
+ fetchImpl
11834
+ );
11835
+ return { ok: true };
11836
+ } catch (err) {
11837
+ return { ok: false, reason: err.message };
11838
+ }
11839
+ }
11840
+ }
11841
+ };
11842
+ function isPushProvider(provider) {
11843
+ return provider in PUSH_PROVIDER_DISPATCH;
11844
+ }
11283
11845
  function resolveEntryCredentials(dispatch, entry, env) {
11284
11846
  let credentials;
11285
11847
  try {
@@ -11302,6 +11864,13 @@ function resolveEntryCredentials(dispatch, entry, env) {
11302
11864
  function buildRegistration(entry, graph, env = process.env) {
11303
11865
  const dispatch = PROVIDER_DISPATCH[entry.provider];
11304
11866
  if (!dispatch) {
11867
+ if (isPushProvider(entry.provider)) {
11868
+ return {
11869
+ ok: false,
11870
+ push: true,
11871
+ reason: `push provider "${entry.provider}" ingests via the OTLP receiver \u2014 nothing to poll`
11872
+ };
11873
+ }
11305
11874
  return { ok: false, reason: `unknown provider "${entry.provider}"` };
11306
11875
  }
11307
11876
  const creds = resolveEntryCredentials(dispatch, entry, env);
@@ -11352,7 +11921,7 @@ async function loadConnectorRegistrations(input) {
11352
11921
  if (!connectorMatchesProject(entry, project)) continue;
11353
11922
  const result = buildRegistration(entry, graph, env);
11354
11923
  if (result.ok) registrations.push(result.registration);
11355
- else onSkip?.(entry, result.reason);
11924
+ else if (!result.push) onSkip?.(entry, result.reason);
11356
11925
  }
11357
11926
  return registrations;
11358
11927
  }
@@ -11363,7 +11932,7 @@ init_auth();
11363
11932
  // src/unrouted.ts
11364
11933
  init_cjs_shims();
11365
11934
  var import_node_fs30 = require("fs");
11366
- var import_node_path49 = __toESM(require("path"), 1);
11935
+ var import_node_path50 = __toESM(require("path"), 1);
11367
11936
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
11368
11937
  return {
11369
11938
  timestamp: now.toISOString(),
@@ -11373,34 +11942,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
11373
11942
  };
11374
11943
  }
11375
11944
  async function appendUnroutedSpan(neatHome3, record) {
11376
- const target = import_node_path49.default.join(neatHome3, "errors.ndjson");
11945
+ const target = import_node_path50.default.join(neatHome3, "errors.ndjson");
11377
11946
  await import_node_fs30.promises.mkdir(neatHome3, { recursive: true });
11378
11947
  await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
11379
11948
  }
11380
11949
  function unroutedErrorsPath(neatHome3) {
11381
- return import_node_path49.default.join(neatHome3, "errors.ndjson");
11950
+ return import_node_path50.default.join(neatHome3, "errors.ndjson");
11382
11951
  }
11383
11952
 
11384
11953
  // src/daemon.ts
11385
- var import_types46 = require("@neat.is/types");
11954
+ var import_types47 = require("@neat.is/types");
11386
11955
  function daemonJsonPath(scanPath) {
11387
- return import_node_path50.default.join(scanPath, "neat-out", "daemon.json");
11956
+ return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
11388
11957
  }
11389
11958
  function daemonsDiscoveryDir(home) {
11390
11959
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
11391
- return import_node_path50.default.join(base, "daemons");
11960
+ return import_node_path51.default.join(base, "daemons");
11392
11961
  }
11393
11962
  function daemonDiscoveryPath(project, home) {
11394
- return import_node_path50.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
11963
+ return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
11395
11964
  }
11396
11965
  function sanitizeDiscoveryName(project) {
11397
11966
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
11398
11967
  }
11399
11968
  function neatHomeFromEnv() {
11400
11969
  const env = process.env.NEAT_HOME;
11401
- if (env && env.length > 0) return import_node_path50.default.resolve(env);
11970
+ if (env && env.length > 0) return import_node_path51.default.resolve(env);
11402
11971
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11403
- return import_node_path50.default.join(home, ".neat");
11972
+ return import_node_path51.default.join(home, ".neat");
11404
11973
  }
11405
11974
  function resolveNeatVersion() {
11406
11975
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -11455,11 +12024,11 @@ function teardownSlot(slot) {
11455
12024
  }
11456
12025
  }
11457
12026
  function neatHomeFor(opts) {
11458
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path50.default.resolve(opts.neatHome);
12027
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
11459
12028
  const env = process.env.NEAT_HOME;
11460
- if (env && env.length > 0) return import_node_path50.default.resolve(env);
12029
+ if (env && env.length > 0) return import_node_path51.default.resolve(env);
11461
12030
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11462
- return import_node_path50.default.join(home, ".neat");
12031
+ return import_node_path51.default.join(home, ".neat");
11463
12032
  }
11464
12033
  function routeSpanToProject(serviceName, projects) {
11465
12034
  if (!serviceName) return DEFAULT_PROJECT;
@@ -11503,11 +12072,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
11503
12072
  if (!serviceName) return true;
11504
12073
  if (serviceNameMatchesProject(serviceName, project)) return true;
11505
12074
  return graph.someNode(
11506
- (_id, attrs) => attrs.type === import_types46.NodeType.ServiceNode && attrs.name === serviceName
12075
+ (_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
11507
12076
  );
11508
12077
  }
11509
12078
  async function bootstrapProject(entry, connectors = [], neatHome3) {
11510
- const paths = pathsForProject(entry.name, import_node_path50.default.join(entry.path, "neat-out"));
12079
+ const paths = pathsForProject(entry.name, import_node_path51.default.join(entry.path, "neat-out"));
11511
12080
  try {
11512
12081
  const stat = await import_node_fs31.promises.stat(entry.path);
11513
12082
  if (!stat.isDirectory()) {
@@ -11638,7 +12207,7 @@ async function startDaemon(opts = {}) {
11638
12207
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
11639
12208
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
11640
12209
  const singleProject = projectArg;
11641
- const singleProjectPath = singleProject && projectPathArg ? import_node_path50.default.resolve(projectPathArg) : null;
12210
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
11642
12211
  if (singleProject && !singleProjectPath) {
11643
12212
  throw new Error(
11644
12213
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -11653,7 +12222,7 @@ async function startDaemon(opts = {}) {
11653
12222
  );
11654
12223
  }
11655
12224
  }
11656
- const pidPath = import_node_path50.default.join(home, "neatd.pid");
12225
+ const pidPath = import_node_path51.default.join(home, "neatd.pid");
11657
12226
  await writeAtomically(pidPath, `${process.pid}
11658
12227
  `);
11659
12228
  const slots = /* @__PURE__ */ new Map();
@@ -12060,8 +12629,8 @@ async function startDaemon(opts = {}) {
12060
12629
  let registryWatcher = null;
12061
12630
  let reloadTimer = null;
12062
12631
  if (!singleProject) try {
12063
- const regDir = import_node_path50.default.dirname(regPath);
12064
- const regBase = import_node_path50.default.basename(regPath);
12632
+ const regDir = import_node_path51.default.dirname(regPath);
12633
+ const regBase = import_node_path51.default.basename(regPath);
12065
12634
  registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
12066
12635
  if (filename !== null && filename !== regBase) return;
12067
12636
  if (reloadTimer) clearTimeout(reloadTimer);