@neat.is/core 0.5.3 → 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/cli.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req, reply, done) => {
64
- const path62 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path62) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path62)) {
64
+ const path63 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path63) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path63)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
- return import_node_path49.default.resolve(here, "..", "proto");
197
+ const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
+ return import_node_path50.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url2, import_node_path49, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url2, import_node_path50, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url2 = require("url");
272
- import_node_path49 = __toESM(require("path"), 1);
272
+ import_node_path50 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -362,8 +362,8 @@ function websocketChannelPathOf(attrs) {
362
362
  const v = attrs[key];
363
363
  if (typeof v === "string" && v.length > 0) {
364
364
  const q = v.indexOf("?");
365
- const path62 = q === -1 ? v : v.slice(0, q);
366
- if (path62.length > 0) return path62;
365
+ const path63 = q === -1 ? v : v.slice(0, q);
366
+ if (path63.length > 0) return path63;
367
367
  }
368
368
  }
369
369
  return void 0;
@@ -398,6 +398,7 @@ function parseOtlpRequest(body) {
398
398
  attributes: attrs,
399
399
  dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
400
400
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
401
+ dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
401
402
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
402
403
  messagingDestination: messagingDestinationOf(attrs),
403
404
  graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
@@ -417,10 +418,10 @@ function parseOtlpRequest(body) {
417
418
  return out;
418
419
  }
419
420
  function loadProtoRoot() {
420
- const here = import_node_path50.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
421
- const protoRoot = import_node_path50.default.resolve(here, "..", "proto");
421
+ const here = import_node_path51.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
422
+ const protoRoot = import_node_path51.default.resolve(here, "..", "proto");
422
423
  const root = new import_protobufjs.default.Root();
423
- root.resolvePath = (_origin, target) => import_node_path50.default.resolve(protoRoot, target);
424
+ root.resolvePath = (_origin, target) => import_node_path51.default.resolve(protoRoot, target);
424
425
  root.loadSync(
425
426
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
426
427
  { keepCase: true }
@@ -648,12 +649,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
648
649
  }
649
650
  }
650
651
  }
651
- var import_node_path50, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
652
+ var import_node_path51, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
652
653
  var init_otel = __esm({
653
654
  "src/otel.ts"() {
654
655
  "use strict";
655
656
  init_cjs_shims();
656
- import_node_path50 = __toESM(require("path"), 1);
657
+ import_node_path51 = __toESM(require("path"), 1);
657
658
  import_node_url3 = require("url");
658
659
  import_fastify2 = __toESM(require("fastify"), 1);
659
660
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -690,7 +691,7 @@ __export(cli_exports, {
690
691
  });
691
692
  module.exports = __toCommonJS(cli_exports);
692
693
  init_cjs_shims();
693
- var import_node_path61 = __toESM(require("path"), 1);
694
+ var import_node_path62 = __toESM(require("path"), 1);
694
695
  var import_node_fs41 = require("fs");
695
696
 
696
697
  // src/banner.ts
@@ -1256,19 +1257,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1256
1257
  function longestIncomingWalk(graph, start, maxDepth) {
1257
1258
  let best = { path: [start], edges: [] };
1258
1259
  const visited = /* @__PURE__ */ new Set([start]);
1259
- function step(node, path62, edges) {
1260
- if (path62.length > best.path.length) {
1261
- best = { path: [...path62], edges: [...edges] };
1260
+ function step(node, path63, edges) {
1261
+ if (path63.length > best.path.length) {
1262
+ best = { path: [...path63], edges: [...edges] };
1262
1263
  }
1263
- if (path62.length - 1 >= maxDepth) return;
1264
+ if (path63.length - 1 >= maxDepth) return;
1264
1265
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1265
1266
  for (const [srcId, edge] of incoming) {
1266
1267
  if (visited.has(srcId)) continue;
1267
1268
  visited.add(srcId);
1268
- path62.push(srcId);
1269
+ path63.push(srcId);
1269
1270
  edges.push(edge);
1270
- step(srcId, path62, edges);
1271
- path62.pop();
1271
+ step(srcId, path63, edges);
1272
+ path63.pop();
1272
1273
  edges.pop();
1273
1274
  visited.delete(srcId);
1274
1275
  }
@@ -1462,26 +1463,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
1462
1463
  return best;
1463
1464
  }
1464
1465
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1465
- const path62 = [originServiceId];
1466
+ const path63 = [originServiceId];
1466
1467
  const edges = [];
1467
1468
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1468
1469
  let current = originServiceId;
1469
1470
  for (let depth = 0; depth < maxDepth; depth++) {
1470
1471
  const hop = dominantFailingCall(graph, current, visited);
1471
1472
  if (!hop) break;
1472
- path62.push(hop.nextService);
1473
+ path63.push(hop.nextService);
1473
1474
  edges.push(hop.edge);
1474
1475
  visited.add(hop.nextService);
1475
1476
  current = hop.nextService;
1476
1477
  }
1477
1478
  if (edges.length === 0) return null;
1478
- return { path: path62, edges, culprit: current };
1479
+ return { path: path63, edges, culprit: current };
1479
1480
  }
1480
1481
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1481
1482
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1482
1483
  if (!chain) return null;
1483
1484
  const culprit = chain.culprit;
1484
- const path62 = [...chain.path];
1485
+ const path63 = [...chain.path];
1485
1486
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1486
1487
  const baseConfidence = confidenceFromMix(chain.edges);
1487
1488
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1489,14 +1490,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1489
1490
  if (loc) {
1490
1491
  let rootCauseNode = culprit;
1491
1492
  if (loc.fileNode) {
1492
- path62.push(loc.fileNode);
1493
+ path63.push(loc.fileNode);
1493
1494
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1494
1495
  rootCauseNode = loc.fileNode;
1495
1496
  }
1496
1497
  return import_types.RootCauseResultSchema.parse({
1497
1498
  rootCauseNode,
1498
1499
  rootCauseReason: loc.rootCauseReason,
1499
- traversalPath: path62,
1500
+ traversalPath: path63,
1500
1501
  edgeProvenances,
1501
1502
  confidence,
1502
1503
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1508,7 +1509,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1508
1509
  return import_types.RootCauseResultSchema.parse({
1509
1510
  rootCauseNode: culprit,
1510
1511
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1511
- traversalPath: path62,
1512
+ traversalPath: path63,
1512
1513
  edgeProvenances,
1513
1514
  confidence,
1514
1515
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2585,6 +2586,19 @@ function ensureServiceNode(graph, serviceName, env) {
2585
2586
  graph.addNode(id, node);
2586
2587
  return id;
2587
2588
  }
2589
+ function ensureInfraNode(graph, kind, name, provider) {
2590
+ const id = (0, import_types3.infraId)(kind, name);
2591
+ if (graph.hasNode(id)) return id;
2592
+ const node = {
2593
+ id,
2594
+ type: import_types3.NodeType.InfraNode,
2595
+ name,
2596
+ provider,
2597
+ kind
2598
+ };
2599
+ graph.addNode(id, node);
2600
+ return id;
2601
+ }
2588
2602
  function ensureDatabaseNode(graph, host, engine) {
2589
2603
  const id = (0, import_types3.databaseId)(host);
2590
2604
  if (graph.hasNode(id)) return id;
@@ -2922,6 +2936,18 @@ async function handleSpan(ctx, span) {
2922
2936
  callSiteEvidence
2923
2937
  );
2924
2938
  if (result) affectedNode = targetId;
2939
+ if (span.dbSystem === "mongodb" && span.dbCollection) {
2940
+ const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
2941
+ upsertObservedEdge(
2942
+ ctx.graph,
2943
+ import_types3.EdgeType.CALLS,
2944
+ observedSource(),
2945
+ collectionId,
2946
+ ts,
2947
+ isError,
2948
+ callSiteEvidence
2949
+ );
2950
+ }
2925
2951
  }
2926
2952
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
2927
2953
  const targetId = ensureMessagingDestinationNode(
@@ -5781,7 +5807,7 @@ async function addGrpcMethods(graph, services) {
5781
5807
 
5782
5808
  // src/extract/calls/index.ts
5783
5809
  init_cjs_shims();
5784
- var import_types20 = require("@neat.is/types");
5810
+ var import_types21 = require("@neat.is/types");
5785
5811
 
5786
5812
  // src/extract/calls/http.ts
5787
5813
  init_cjs_shims();
@@ -6494,15 +6520,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6494
6520
  return out;
6495
6521
  }
6496
6522
 
6523
+ // src/extract/calls/mongoose.ts
6524
+ init_cjs_shims();
6525
+ var import_node_path32 = __toESM(require("path"), 1);
6526
+ var import_types20 = require("@neat.is/types");
6527
+ var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
6528
+ var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
6529
+ var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
6530
+ var UNCOUNTABLES = /* @__PURE__ */ new Set([
6531
+ "advice",
6532
+ "energy",
6533
+ "excretion",
6534
+ "digestion",
6535
+ "cooperation",
6536
+ "health",
6537
+ "justice",
6538
+ "labour",
6539
+ "machinery",
6540
+ "equipment",
6541
+ "information",
6542
+ "pollution",
6543
+ "sewage",
6544
+ "paper",
6545
+ "money",
6546
+ "species",
6547
+ "series",
6548
+ "rain",
6549
+ "rice",
6550
+ "fish",
6551
+ "sheep",
6552
+ "moose",
6553
+ "deer",
6554
+ "news",
6555
+ "expertise",
6556
+ "status",
6557
+ "media"
6558
+ ]);
6559
+ var PLURALIZE_RULES = [
6560
+ [/human$/gi, "humans"],
6561
+ [/(m)an$/gi, "$1en"],
6562
+ [/(pe)rson$/gi, "$1ople"],
6563
+ [/(child)$/gi, "$1ren"],
6564
+ [/^(ox)$/gi, "$1en"],
6565
+ [/(ax|test)is$/gi, "$1es"],
6566
+ [/(octop|vir)us$/gi, "$1i"],
6567
+ [/(alias|status)$/gi, "$1es"],
6568
+ [/(bu)s$/gi, "$1ses"],
6569
+ [/(buffal|tomat|potat)o$/gi, "$1oes"],
6570
+ [/([ti])um$/gi, "$1a"],
6571
+ [/sis$/gi, "ses"],
6572
+ [/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
6573
+ [/(hive)$/gi, "$1s"],
6574
+ [/([^aeiouy]|qu)y$/gi, "$1ies"],
6575
+ [/(x|ch|ss|sh)$/gi, "$1es"],
6576
+ [/(matr|vert|ind)ix|ex$/gi, "$1ices"],
6577
+ [/([m|l])ouse$/gi, "$1ice"],
6578
+ [/(kn|w|l)ife$/gi, "$1ives"],
6579
+ [/(quiz)$/gi, "$1zes"],
6580
+ [/s$/gi, "s"],
6581
+ [/([^a-z])$/, "$1"],
6582
+ [/$/gi, "s"]
6583
+ ];
6584
+ function pluralizeCollection(name) {
6585
+ const str = name.toLowerCase();
6586
+ if (UNCOUNTABLES.has(str)) return str;
6587
+ for (const [re, repl] of PLURALIZE_RULES) {
6588
+ if (str.match(re)) return str.replace(re, repl);
6589
+ }
6590
+ return str;
6591
+ }
6592
+ var MODEL_METHODS = [
6593
+ "find",
6594
+ "findOne",
6595
+ "findById",
6596
+ "findByIdAndUpdate",
6597
+ "findByIdAndDelete",
6598
+ "findOneAndUpdate",
6599
+ "findOneAndDelete",
6600
+ "findOneAndReplace",
6601
+ "countDocuments",
6602
+ "estimatedDocumentCount",
6603
+ "distinct",
6604
+ "aggregate",
6605
+ "exists",
6606
+ "where",
6607
+ "populate",
6608
+ "watch",
6609
+ "hydrate",
6610
+ "create",
6611
+ "insertMany",
6612
+ "insertOne",
6613
+ "updateOne",
6614
+ "updateMany",
6615
+ "replaceOne",
6616
+ "deleteOne",
6617
+ "deleteMany",
6618
+ "bulkWrite",
6619
+ "bulkSave",
6620
+ "save",
6621
+ // pre-v7, removed in current mongoose but present in older codebases:
6622
+ "count",
6623
+ "update",
6624
+ "remove",
6625
+ "findOneAndRemove",
6626
+ "findByIdAndRemove"
6627
+ ];
6628
+ var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
6629
+ function schemaCollectionVars(content) {
6630
+ const out = /* @__PURE__ */ new Map();
6631
+ const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
6632
+ let m;
6633
+ while ((m = re.exec(content)) !== null) {
6634
+ const windowText = content.slice(m.index, m.index + 2e3);
6635
+ const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
6636
+ if (opt) out.set(m[1], opt[1]);
6637
+ }
6638
+ return out;
6639
+ }
6640
+ function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
6641
+ const trimmed = rest.trim();
6642
+ const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
6643
+ if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
6644
+ const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
6645
+ if (firstIdent && schemaColl.has(firstIdent)) {
6646
+ return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
6647
+ }
6648
+ if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
6649
+ return { name: modelName, kind: "mongodb-model" };
6650
+ }
6651
+ return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
6652
+ }
6653
+ function collectModelDefs(content, pluralizeOn) {
6654
+ const schemaColl = schemaCollectionVars(content);
6655
+ const out = [];
6656
+ 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;
6657
+ let m;
6658
+ while ((m = re.exec(content)) !== null) {
6659
+ out.push({
6660
+ modelName: m[2],
6661
+ varName: m[1] ?? null,
6662
+ resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
6663
+ matchText: m[0]
6664
+ });
6665
+ }
6666
+ return out;
6667
+ }
6668
+ function endpoint(r, file, serviceDir, matchText) {
6669
+ const line = lineOf(file.content, matchText);
6670
+ return {
6671
+ infraId: (0, import_types20.infraId)(r.kind, r.name),
6672
+ name: r.name,
6673
+ kind: r.kind,
6674
+ edgeType: "CALLS",
6675
+ confidenceKind: "verified-call-site",
6676
+ evidence: { file: import_node_path32.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
6677
+ };
6678
+ }
6679
+ function mongooseEndpointsFromFile(file, serviceDir) {
6680
+ const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
6681
+ const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
6682
+ if (!hasMongoose && !hasMongodb) return [];
6683
+ const content = file.content;
6684
+ const out = [];
6685
+ const seen = /* @__PURE__ */ new Set();
6686
+ const push = (r, matchText) => {
6687
+ const key = `${r.kind}/${r.name}`;
6688
+ if (seen.has(key)) return;
6689
+ seen.add(key);
6690
+ out.push(endpoint(r, file, serviceDir, matchText));
6691
+ };
6692
+ if (hasMongoose) {
6693
+ const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
6694
+ for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
6695
+ }
6696
+ const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
6697
+ let c;
6698
+ while ((c = collRe.exec(content)) !== null) {
6699
+ push({ name: c[1], kind: "mongodb-collection" }, c[0]);
6700
+ }
6701
+ return out;
6702
+ }
6703
+ function parseDestructure(inner, specifier, out) {
6704
+ for (const raw of inner.split(",")) {
6705
+ const part = raw.trim();
6706
+ if (!part) continue;
6707
+ const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
6708
+ const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
6709
+ if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
6710
+ else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
6711
+ else {
6712
+ const id = /^(\w+)$/.exec(part);
6713
+ if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
6714
+ }
6715
+ }
6716
+ }
6717
+ function parseImportBindings(content) {
6718
+ const out = [];
6719
+ const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
6720
+ let m;
6721
+ while ((m = esm.exec(content)) !== null) {
6722
+ const clause = m[1].trim();
6723
+ const spec = m[2];
6724
+ const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
6725
+ if (ns) {
6726
+ out.push({ local: ns[1], kind: "namespace", specifier: spec });
6727
+ continue;
6728
+ }
6729
+ const named = /\{([^}]*)\}/.exec(clause);
6730
+ if (named) parseDestructure(named[1], spec, out);
6731
+ const def = /^(\w+)\s*(?:,|$)/.exec(clause);
6732
+ if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
6733
+ }
6734
+ const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
6735
+ while ((m = cjs.exec(content)) !== null) {
6736
+ const lhs = m[1];
6737
+ const spec = m[2];
6738
+ if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
6739
+ else out.push({ local: lhs, kind: "whole", specifier: spec });
6740
+ }
6741
+ return out;
6742
+ }
6743
+ function fileExportsOf(content, pluralizeOn) {
6744
+ const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
6745
+ if (defs.length === 0) return null;
6746
+ const byVar = /* @__PURE__ */ new Map();
6747
+ for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
6748
+ const byName = new Map(byVar);
6749
+ let def;
6750
+ const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
6751
+ if (named && byVar.has(named[1])) def = byVar.get(named[1]);
6752
+ if (!def) {
6753
+ const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
6754
+ if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
6755
+ }
6756
+ if (!def && byVar.size === 1) def = [...byVar.values()][0];
6757
+ return { byName, ...def ? { default: def } : {} };
6758
+ }
6759
+ async function mongooseCrossFileEndpoints(files, serviceDir) {
6760
+ const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
6761
+ if (mongooseFiles.length === 0) return [];
6762
+ const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
6763
+ const registry = /* @__PURE__ */ new Map();
6764
+ for (const f of mongooseFiles) {
6765
+ const fx = fileExportsOf(f.content, pluralizeOn);
6766
+ if (fx) registry.set(toPosix2(import_node_path32.default.relative(serviceDir, f.path)), fx);
6767
+ }
6768
+ if (registry.size === 0) return [];
6769
+ const out = [];
6770
+ const seen = /* @__PURE__ */ new Set();
6771
+ for (const f of files) {
6772
+ const bindings = parseImportBindings(f.content);
6773
+ if (bindings.length === 0) continue;
6774
+ const directColl = /* @__PURE__ */ new Map();
6775
+ const nsExports = /* @__PURE__ */ new Map();
6776
+ for (const b of bindings) {
6777
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path32.default.dirname(f.path), serviceDir, null);
6778
+ if (!resolvedRel) continue;
6779
+ const fx = registry.get(resolvedRel);
6780
+ if (!fx) continue;
6781
+ if (b.kind === "named" && b.exportName) {
6782
+ const coll = fx.byName.get(b.exportName);
6783
+ if (coll) directColl.set(b.local, coll);
6784
+ } else if (b.kind === "default") {
6785
+ if (fx.default) directColl.set(b.local, fx.default);
6786
+ } else {
6787
+ if (fx.default) directColl.set(b.local, fx.default);
6788
+ nsExports.set(b.local, fx);
6789
+ }
6790
+ }
6791
+ if (directColl.size === 0 && nsExports.size === 0) continue;
6792
+ const localDefs = new Set(
6793
+ collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
6794
+ );
6795
+ const emit = (collection, matchText) => {
6796
+ const key = `${f.path}::${collection}`;
6797
+ if (seen.has(key)) return;
6798
+ seen.add(key);
6799
+ out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
6800
+ };
6801
+ for (const [local, collection] of directColl) {
6802
+ if (localDefs.has(local)) continue;
6803
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
6804
+ let qm;
6805
+ while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
6806
+ }
6807
+ for (const [local, fx] of nsExports) {
6808
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
6809
+ let qm;
6810
+ while ((qm = qre.exec(f.content)) !== null) {
6811
+ const coll = fx.byName.get(qm[1]);
6812
+ if (coll) emit(coll, qm[0]);
6813
+ }
6814
+ }
6815
+ }
6816
+ return out;
6817
+ }
6818
+
6497
6819
  // src/extract/calls/index.ts
6498
6820
  function edgeTypeFromEndpoint(ep) {
6499
6821
  switch (ep.edgeType) {
6500
6822
  case "PUBLISHES_TO":
6501
- return import_types20.EdgeType.PUBLISHES_TO;
6823
+ return import_types21.EdgeType.PUBLISHES_TO;
6502
6824
  case "CONSUMES_FROM":
6503
- return import_types20.EdgeType.CONSUMES_FROM;
6825
+ return import_types21.EdgeType.CONSUMES_FROM;
6504
6826
  default:
6505
- return import_types20.EdgeType.CALLS;
6827
+ return import_types21.EdgeType.CALLS;
6506
6828
  }
6507
6829
  }
6508
6830
  function isAwsKind(kind) {
@@ -6514,23 +6836,27 @@ async function addExternalEndpointEdges(graph, services) {
6514
6836
  for (const service of services) {
6515
6837
  const files = await loadSourceFiles(service.dir);
6516
6838
  const endpoints = [];
6839
+ const maskedFiles = [];
6517
6840
  for (const file of files) {
6518
6841
  if (isTestPath(file.path)) continue;
6519
6842
  const masked = maskCommentsInSource(file.content);
6520
6843
  const maskedFile = { path: file.path, content: masked };
6844
+ maskedFiles.push(maskedFile);
6521
6845
  endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
6522
6846
  endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
6523
6847
  endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
6524
6848
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
6525
6849
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
6850
+ endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
6526
6851
  }
6852
+ endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
6527
6853
  if (endpoints.length === 0) continue;
6528
6854
  const seenEdges = /* @__PURE__ */ new Set();
6529
6855
  for (const ep of endpoints) {
6530
6856
  if (!graph.hasNode(ep.infraId)) {
6531
6857
  const node = {
6532
6858
  id: ep.infraId,
6533
- type: import_types20.NodeType.InfraNode,
6859
+ type: import_types21.NodeType.InfraNode,
6534
6860
  name: ep.name,
6535
6861
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
6536
6862
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -6542,7 +6868,7 @@ async function addExternalEndpointEdges(graph, services) {
6542
6868
  nodesAdded++;
6543
6869
  }
6544
6870
  const edgeType = edgeTypeFromEndpoint(ep);
6545
- const confidence = (0, import_types20.confidenceForExtracted)(ep.confidenceKind);
6871
+ const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
6546
6872
  const relFile = toPosix2(ep.evidence.file);
6547
6873
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
6548
6874
  graph,
@@ -6552,7 +6878,7 @@ async function addExternalEndpointEdges(graph, services) {
6552
6878
  );
6553
6879
  nodesAdded += n;
6554
6880
  edgesAdded += e;
6555
- if (!(0, import_types20.passesExtractedFloor)(confidence)) {
6881
+ if (!(0, import_types21.passesExtractedFloor)(confidence)) {
6556
6882
  noteExtractedDropped({
6557
6883
  source: fileNodeId,
6558
6884
  target: ep.infraId,
@@ -6572,7 +6898,7 @@ async function addExternalEndpointEdges(graph, services) {
6572
6898
  source: fileNodeId,
6573
6899
  target: ep.infraId,
6574
6900
  type: edgeType,
6575
- provenance: import_types20.Provenance.EXTRACTED,
6901
+ provenance: import_types21.Provenance.EXTRACTED,
6576
6902
  confidence,
6577
6903
  evidence: ep.evidence
6578
6904
  };
@@ -6598,16 +6924,16 @@ init_cjs_shims();
6598
6924
 
6599
6925
  // src/extract/infra/docker-compose.ts
6600
6926
  init_cjs_shims();
6601
- var import_node_path32 = __toESM(require("path"), 1);
6602
- var import_types22 = require("@neat.is/types");
6927
+ var import_node_path33 = __toESM(require("path"), 1);
6928
+ var import_types23 = require("@neat.is/types");
6603
6929
 
6604
6930
  // src/extract/infra/shared.ts
6605
6931
  init_cjs_shims();
6606
- var import_types21 = require("@neat.is/types");
6932
+ var import_types22 = require("@neat.is/types");
6607
6933
  function makeInfraNode(kind, name, provider = "self", extras) {
6608
6934
  return {
6609
- id: (0, import_types21.infraId)(kind, name),
6610
- type: import_types21.NodeType.InfraNode,
6935
+ id: (0, import_types22.infraId)(kind, name),
6936
+ type: import_types22.NodeType.InfraNode,
6611
6937
  name,
6612
6938
  provider,
6613
6939
  kind,
@@ -6651,8 +6977,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
6651
6977
  source: anchorId,
6652
6978
  target: node.id,
6653
6979
  type: edgeType,
6654
- provenance: import_types21.Provenance.EXTRACTED,
6655
- confidence: (0, import_types21.confidenceForExtracted)("structural"),
6980
+ provenance: import_types22.Provenance.EXTRACTED,
6981
+ confidence: (0, import_types22.confidenceForExtracted)("structural"),
6656
6982
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
6657
6983
  };
6658
6984
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -6669,7 +6995,7 @@ function dependsOnList(value) {
6669
6995
  }
6670
6996
  function serviceNameToServiceNode(name, services) {
6671
6997
  for (const s of services) {
6672
- if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
6998
+ if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
6673
6999
  }
6674
7000
  return null;
6675
7001
  }
@@ -6678,7 +7004,7 @@ async function addComposeInfra(graph, scanPath, services) {
6678
7004
  let edgesAdded = 0;
6679
7005
  let composePath = null;
6680
7006
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
6681
- const abs = import_node_path32.default.join(scanPath, name);
7007
+ const abs = import_node_path33.default.join(scanPath, name);
6682
7008
  if (await exists(abs)) {
6683
7009
  composePath = abs;
6684
7010
  break;
@@ -6691,13 +7017,13 @@ async function addComposeInfra(graph, scanPath, services) {
6691
7017
  } catch (err) {
6692
7018
  recordExtractionError(
6693
7019
  "infra docker-compose",
6694
- import_node_path32.default.relative(scanPath, composePath),
7020
+ import_node_path33.default.relative(scanPath, composePath),
6695
7021
  err
6696
7022
  );
6697
7023
  return { nodesAdded, edgesAdded };
6698
7024
  }
6699
7025
  if (!compose?.services) return { nodesAdded, edgesAdded };
6700
- const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
7026
+ const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
6701
7027
  const composeNameToNodeId = /* @__PURE__ */ new Map();
6702
7028
  for (const [composeName, svc] of Object.entries(compose.services)) {
6703
7029
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -6719,15 +7045,15 @@ async function addComposeInfra(graph, scanPath, services) {
6719
7045
  for (const dep of dependsOnList(svc.depends_on)) {
6720
7046
  const targetId = composeNameToNodeId.get(dep);
6721
7047
  if (!targetId) continue;
6722
- const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types22.EdgeType.DEPENDS_ON);
7048
+ const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
6723
7049
  if (graph.hasEdge(edgeId)) continue;
6724
7050
  const edge = {
6725
7051
  id: edgeId,
6726
7052
  source: sourceId,
6727
7053
  target: targetId,
6728
- type: import_types22.EdgeType.DEPENDS_ON,
6729
- provenance: import_types22.Provenance.EXTRACTED,
6730
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7054
+ type: import_types23.EdgeType.DEPENDS_ON,
7055
+ provenance: import_types23.Provenance.EXTRACTED,
7056
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
6731
7057
  evidence: { file: evidenceFile }
6732
7058
  };
6733
7059
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -6739,9 +7065,9 @@ async function addComposeInfra(graph, scanPath, services) {
6739
7065
 
6740
7066
  // src/extract/infra/dockerfile.ts
6741
7067
  init_cjs_shims();
6742
- var import_node_path33 = __toESM(require("path"), 1);
7068
+ var import_node_path34 = __toESM(require("path"), 1);
6743
7069
  var import_node_fs17 = require("fs");
6744
- var import_types23 = require("@neat.is/types");
7070
+ var import_types24 = require("@neat.is/types");
6745
7071
  function readDockerfile(content) {
6746
7072
  let image = null;
6747
7073
  const ports = [];
@@ -6770,7 +7096,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6770
7096
  let nodesAdded = 0;
6771
7097
  let edgesAdded = 0;
6772
7098
  for (const service of services) {
6773
- const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
7099
+ const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
6774
7100
  if (!await exists(dockerfilePath)) continue;
6775
7101
  let content;
6776
7102
  try {
@@ -6778,7 +7104,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6778
7104
  } catch (err) {
6779
7105
  recordExtractionError(
6780
7106
  "infra dockerfile",
6781
- import_node_path33.default.relative(scanPath, dockerfilePath),
7107
+ import_node_path34.default.relative(scanPath, dockerfilePath),
6782
7108
  err
6783
7109
  );
6784
7110
  continue;
@@ -6790,8 +7116,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6790
7116
  graph.addNode(node.id, node);
6791
7117
  nodesAdded++;
6792
7118
  }
6793
- const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
6794
- const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
7119
+ const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
7120
+ const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
6795
7121
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
6796
7122
  graph,
6797
7123
  service.pkg.name,
@@ -6800,15 +7126,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6800
7126
  );
6801
7127
  nodesAdded += fn;
6802
7128
  edgesAdded += fe;
6803
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types23.EdgeType.RUNS_ON);
7129
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
6804
7130
  if (!graph.hasEdge(edgeId)) {
6805
7131
  const edge = {
6806
7132
  id: edgeId,
6807
7133
  source: fileNodeId,
6808
7134
  target: node.id,
6809
- type: import_types23.EdgeType.RUNS_ON,
6810
- provenance: import_types23.Provenance.EXTRACTED,
6811
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7135
+ type: import_types24.EdgeType.RUNS_ON,
7136
+ provenance: import_types24.Provenance.EXTRACTED,
7137
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
6812
7138
  evidence: {
6813
7139
  file: evidenceFile,
6814
7140
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -6823,15 +7149,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6823
7149
  graph.addNode(portNode.id, portNode);
6824
7150
  nodesAdded++;
6825
7151
  }
6826
- const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types23.EdgeType.CONNECTS_TO);
7152
+ const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
6827
7153
  if (graph.hasEdge(portEdgeId)) continue;
6828
7154
  const portEdge = {
6829
7155
  id: portEdgeId,
6830
7156
  source: fileNodeId,
6831
7157
  target: portNode.id,
6832
- type: import_types23.EdgeType.CONNECTS_TO,
6833
- provenance: import_types23.Provenance.EXTRACTED,
6834
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7158
+ type: import_types24.EdgeType.CONNECTS_TO,
7159
+ provenance: import_types24.Provenance.EXTRACTED,
7160
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
6835
7161
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
6836
7162
  };
6837
7163
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -6844,8 +7170,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
6844
7170
  // src/extract/infra/terraform.ts
6845
7171
  init_cjs_shims();
6846
7172
  var import_node_fs18 = require("fs");
6847
- var import_node_path34 = __toESM(require("path"), 1);
6848
- var import_types24 = require("@neat.is/types");
7173
+ var import_node_path35 = __toESM(require("path"), 1);
7174
+ var import_types25 = require("@neat.is/types");
6849
7175
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
6850
7176
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
6851
7177
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -6855,11 +7181,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
6855
7181
  for (const entry2 of entries) {
6856
7182
  if (entry2.isDirectory()) {
6857
7183
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
6858
- const child = import_node_path34.default.join(start, entry2.name);
7184
+ const child = import_node_path35.default.join(start, entry2.name);
6859
7185
  if (await isPythonVenvDir(child)) continue;
6860
7186
  out.push(...await walkTfFiles(child, depth + 1, max));
6861
7187
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
6862
- out.push(import_node_path34.default.join(start, entry2.name));
7188
+ out.push(import_node_path35.default.join(start, entry2.name));
6863
7189
  }
6864
7190
  }
6865
7191
  return out;
@@ -6891,7 +7217,7 @@ async function addTerraformResources(graph, scanPath) {
6891
7217
  const files = await walkTfFiles(scanPath);
6892
7218
  for (const file of files) {
6893
7219
  const content = await import_node_fs18.promises.readFile(file, "utf8");
6894
- const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
7220
+ const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
6895
7221
  const resources = [];
6896
7222
  const byKey = /* @__PURE__ */ new Map();
6897
7223
  RESOURCE_RE.lastIndex = 0;
@@ -6926,16 +7252,16 @@ async function addTerraformResources(graph, scanPath) {
6926
7252
  if (!target) continue;
6927
7253
  if (seen.has(target.nodeId)) continue;
6928
7254
  seen.add(target.nodeId);
6929
- const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types24.EdgeType.DEPENDS_ON);
7255
+ const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
6930
7256
  if (graph.hasEdge(edgeId)) continue;
6931
7257
  const line = lineAt2(content, resource.bodyOffset + ref.index);
6932
7258
  const edge = {
6933
7259
  id: edgeId,
6934
7260
  source: resource.nodeId,
6935
7261
  target: target.nodeId,
6936
- type: import_types24.EdgeType.DEPENDS_ON,
6937
- provenance: import_types24.Provenance.EXTRACTED,
6938
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7262
+ type: import_types25.EdgeType.DEPENDS_ON,
7263
+ provenance: import_types25.Provenance.EXTRACTED,
7264
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
6939
7265
  evidence: { file: evidenceFile, line, snippet: key }
6940
7266
  };
6941
7267
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -6949,7 +7275,7 @@ async function addTerraformResources(graph, scanPath) {
6949
7275
  // src/extract/infra/k8s.ts
6950
7276
  init_cjs_shims();
6951
7277
  var import_node_fs19 = require("fs");
6952
- var import_node_path35 = __toESM(require("path"), 1);
7278
+ var import_node_path36 = __toESM(require("path"), 1);
6953
7279
  var import_yaml3 = require("yaml");
6954
7280
  var K8S_KIND_TO_INFRA_KIND = {
6955
7281
  Service: "k8s-service",
@@ -6967,11 +7293,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
6967
7293
  for (const entry2 of entries) {
6968
7294
  if (entry2.isDirectory()) {
6969
7295
  if (IGNORED_DIRS.has(entry2.name)) continue;
6970
- const child = import_node_path35.default.join(start, entry2.name);
7296
+ const child = import_node_path36.default.join(start, entry2.name);
6971
7297
  if (await isPythonVenvDir(child)) continue;
6972
7298
  out.push(...await walkYamlFiles2(child, depth + 1, max));
6973
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry2.name))) {
6974
- out.push(import_node_path35.default.join(start, entry2.name));
7299
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
7300
+ out.push(import_node_path36.default.join(start, entry2.name));
6975
7301
  }
6976
7302
  }
6977
7303
  return out;
@@ -7005,13 +7331,13 @@ async function addK8sResources(graph, scanPath) {
7005
7331
  // src/extract/infra/cloudflare.ts
7006
7332
  init_cjs_shims();
7007
7333
  var import_node_fs20 = require("fs");
7008
- var import_node_path36 = __toESM(require("path"), 1);
7334
+ var import_node_path37 = __toESM(require("path"), 1);
7009
7335
  var import_smol_toml2 = require("smol-toml");
7010
- var import_types25 = require("@neat.is/types");
7336
+ var import_types26 = require("@neat.is/types");
7011
7337
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7012
7338
  async function readWranglerConfig(dir) {
7013
7339
  for (const filename of WRANGLER_FILENAMES) {
7014
- const abs = import_node_path36.default.join(dir, filename);
7340
+ const abs = import_node_path37.default.join(dir, filename);
7015
7341
  if (!await exists(abs)) continue;
7016
7342
  const raw = await import_node_fs20.promises.readFile(abs, "utf8");
7017
7343
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7055,8 +7381,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7055
7381
  source: anchorId,
7056
7382
  target: node.id,
7057
7383
  type: edgeType,
7058
- provenance: import_types25.Provenance.EXTRACTED,
7059
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7384
+ provenance: import_types26.Provenance.EXTRACTED,
7385
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7060
7386
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7061
7387
  };
7062
7388
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7074,11 +7400,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7074
7400
  try {
7075
7401
  read = await readWranglerConfig(service.dir);
7076
7402
  } catch (err) {
7077
- recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
7403
+ recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
7078
7404
  continue;
7079
7405
  }
7080
7406
  if (!read || !read.config.name) continue;
7081
- const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
7407
+ const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
7082
7408
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7083
7409
  }
7084
7410
  for (const worker of discovered) {
@@ -7090,7 +7416,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7090
7416
  }
7091
7417
  let anchorId = service.node.id;
7092
7418
  if (config.main) {
7093
- const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
7419
+ const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
7094
7420
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7095
7421
  graph,
7096
7422
  service.pkg.name,
@@ -7117,15 +7443,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7117
7443
  nodesAdded++;
7118
7444
  }
7119
7445
  if (runtimeNode.id !== anchorId) {
7120
- const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types25.EdgeType.RUNS_ON);
7446
+ const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
7121
7447
  if (!graph.hasEdge(runsOnId)) {
7122
7448
  const edge = {
7123
7449
  id: runsOnId,
7124
7450
  source: anchorId,
7125
7451
  target: runtimeNode.id,
7126
- type: import_types25.EdgeType.RUNS_ON,
7127
- provenance: import_types25.Provenance.EXTRACTED,
7128
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7452
+ type: import_types26.EdgeType.RUNS_ON,
7453
+ provenance: import_types26.Provenance.EXTRACTED,
7454
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7129
7455
  evidence: {
7130
7456
  file: evidenceFile,
7131
7457
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7139,7 +7465,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7139
7465
  const result = addResourceEdge(
7140
7466
  graph,
7141
7467
  anchorId,
7142
- import_types25.EdgeType.CONNECTS_TO,
7468
+ import_types26.EdgeType.CONNECTS_TO,
7143
7469
  "cloudflare-route",
7144
7470
  route,
7145
7471
  evidenceFile,
@@ -7163,7 +7489,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7163
7489
  const result = addResourceEdge(
7164
7490
  graph,
7165
7491
  anchorId,
7166
- import_types25.EdgeType.DEPENDS_ON,
7492
+ import_types26.EdgeType.DEPENDS_ON,
7167
7493
  group.kind,
7168
7494
  name,
7169
7495
  evidenceFile,
@@ -7177,7 +7503,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7177
7503
  const result = addResourceEdge(
7178
7504
  graph,
7179
7505
  anchorId,
7180
- import_types25.EdgeType.DEPENDS_ON,
7506
+ import_types26.EdgeType.DEPENDS_ON,
7181
7507
  "cloudflare-cron",
7182
7508
  cron,
7183
7509
  evidenceFile,
@@ -7190,7 +7516,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7190
7516
  const result = addResourceEdge(
7191
7517
  graph,
7192
7518
  anchorId,
7193
- import_types25.EdgeType.DEPENDS_ON,
7519
+ import_types26.EdgeType.DEPENDS_ON,
7194
7520
  "cloudflare-env-var",
7195
7521
  varName,
7196
7522
  evidenceFile,
@@ -7203,15 +7529,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7203
7529
  if (!svc.service) continue;
7204
7530
  const target = workerIndex.get(svc.service);
7205
7531
  if (target && target.anchorId !== anchorId) {
7206
- const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types25.EdgeType.CALLS);
7532
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
7207
7533
  if (!graph.hasEdge(edgeId)) {
7208
7534
  const edge = {
7209
7535
  id: edgeId,
7210
7536
  source: anchorId,
7211
7537
  target: target.anchorId,
7212
- type: import_types25.EdgeType.CALLS,
7213
- provenance: import_types25.Provenance.EXTRACTED,
7214
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7538
+ type: import_types26.EdgeType.CALLS,
7539
+ provenance: import_types26.Provenance.EXTRACTED,
7540
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7215
7541
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7216
7542
  };
7217
7543
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7222,7 +7548,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7222
7548
  const result = addResourceEdge(
7223
7549
  graph,
7224
7550
  anchorId,
7225
- import_types25.EdgeType.DEPENDS_ON,
7551
+ import_types26.EdgeType.DEPENDS_ON,
7226
7552
  "cloudflare-service-binding",
7227
7553
  svc.service,
7228
7554
  evidenceFile,
@@ -7238,12 +7564,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7238
7564
  // src/extract/infra/vercel.ts
7239
7565
  init_cjs_shims();
7240
7566
  var import_node_fs21 = require("fs");
7241
- var import_node_path37 = __toESM(require("path"), 1);
7242
- var import_types26 = require("@neat.is/types");
7567
+ var import_node_path38 = __toESM(require("path"), 1);
7568
+ var import_types27 = require("@neat.is/types");
7243
7569
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7244
7570
  async function readVercelConfig(dir) {
7245
7571
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7246
- const abs = import_node_path37.default.join(dir, filename);
7572
+ const abs = import_node_path38.default.join(dir, filename);
7247
7573
  if (!await exists(abs)) continue;
7248
7574
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7249
7575
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7252,7 +7578,7 @@ async function readVercelConfig(dir) {
7252
7578
  return null;
7253
7579
  }
7254
7580
  async function readLinkedProjectName(dir) {
7255
- const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
7581
+ const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
7256
7582
  if (!await exists(abs)) return void 0;
7257
7583
  const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
7258
7584
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7270,7 +7596,7 @@ async function addVercelServices(graph, services, scanPath) {
7270
7596
  read = await readVercelConfig(service.dir);
7271
7597
  projectName = await readLinkedProjectName(service.dir);
7272
7598
  } catch (err) {
7273
- recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
7599
+ recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
7274
7600
  continue;
7275
7601
  }
7276
7602
  if (!read && !projectName) continue;
@@ -7286,7 +7612,7 @@ async function addVercelServices(graph, services, scanPath) {
7286
7612
  const anchorId = service.node.id;
7287
7613
  if (!read) continue;
7288
7614
  const { config, relFile, raw } = read;
7289
- const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
7615
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7290
7616
  const add = (edgeType, kind, name) => {
7291
7617
  if (!name) return;
7292
7618
  const result = emitPlatformResourceEdge(
@@ -7302,12 +7628,12 @@ async function addVercelServices(graph, services, scanPath) {
7302
7628
  nodesAdded += result.nodesAdded;
7303
7629
  edgesAdded += result.edgesAdded;
7304
7630
  };
7305
- add(import_types26.EdgeType.RUNS_ON, "vercel", "vercel");
7306
- for (const cron of config.crons ?? []) add(import_types26.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7307
- for (const varName of Object.keys(config.env ?? {})) add(import_types26.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7308
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types26.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7631
+ add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7632
+ for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7633
+ for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7634
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7309
7635
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7310
- add(import_types26.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7636
+ add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7311
7637
  }
7312
7638
  }
7313
7639
  return { nodesAdded, edgesAdded };
@@ -7316,13 +7642,13 @@ async function addVercelServices(graph, services, scanPath) {
7316
7642
  // src/extract/infra/railway.ts
7317
7643
  init_cjs_shims();
7318
7644
  var import_node_fs22 = require("fs");
7319
- var import_node_path38 = __toESM(require("path"), 1);
7645
+ var import_node_path39 = __toESM(require("path"), 1);
7320
7646
  var import_smol_toml3 = require("smol-toml");
7321
- var import_types27 = require("@neat.is/types");
7647
+ var import_types28 = require("@neat.is/types");
7322
7648
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7323
7649
  async function readRailwayConfig(dir) {
7324
7650
  for (const filename of RAILWAY_FILENAMES) {
7325
- const abs = import_node_path38.default.join(dir, filename);
7651
+ const abs = import_node_path39.default.join(dir, filename);
7326
7652
  if (!await exists(abs)) continue;
7327
7653
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7328
7654
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7338,7 +7664,7 @@ async function addRailwayServices(graph, services, scanPath) {
7338
7664
  try {
7339
7665
  read = await readRailwayConfig(service.dir);
7340
7666
  } catch (err) {
7341
- recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
7667
+ recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
7342
7668
  continue;
7343
7669
  }
7344
7670
  if (!read) continue;
@@ -7348,7 +7674,7 @@ async function addRailwayServices(graph, services, scanPath) {
7348
7674
  }
7349
7675
  const anchorId = service.node.id;
7350
7676
  const { config, relFile, raw } = read;
7351
- const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7677
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7352
7678
  const add = (edgeType, kind, name) => {
7353
7679
  if (!name) return;
7354
7680
  const result = emitPlatformResourceEdge(
@@ -7364,9 +7690,9 @@ async function addRailwayServices(graph, services, scanPath) {
7364
7690
  nodesAdded += result.nodesAdded;
7365
7691
  edgesAdded += result.edgesAdded;
7366
7692
  };
7367
- add(import_types27.EdgeType.RUNS_ON, "railway", "railway");
7368
- add(import_types27.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7369
- add(import_types27.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7693
+ add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7694
+ add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7695
+ add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7370
7696
  }
7371
7697
  return { nodesAdded, edgesAdded };
7372
7698
  }
@@ -7374,12 +7700,12 @@ async function addRailwayServices(graph, services, scanPath) {
7374
7700
  // src/extract/infra/supabase.ts
7375
7701
  init_cjs_shims();
7376
7702
  var import_node_fs23 = require("fs");
7377
- var import_node_path39 = __toESM(require("path"), 1);
7703
+ var import_node_path40 = __toESM(require("path"), 1);
7378
7704
  var import_smol_toml4 = require("smol-toml");
7379
- var import_types28 = require("@neat.is/types");
7705
+ var import_types29 = require("@neat.is/types");
7380
7706
  async function readSupabaseConfig(dir) {
7381
- const relFile = import_node_path39.default.join("supabase", "config.toml");
7382
- const abs = import_node_path39.default.join(dir, relFile);
7707
+ const relFile = import_node_path40.default.join("supabase", "config.toml");
7708
+ const abs = import_node_path40.default.join(dir, relFile);
7383
7709
  if (!await exists(abs)) return null;
7384
7710
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
7385
7711
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7393,7 +7719,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7393
7719
  try {
7394
7720
  read = await readSupabaseConfig(service.dir);
7395
7721
  } catch (err) {
7396
- recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
7722
+ recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
7397
7723
  continue;
7398
7724
  }
7399
7725
  if (!read) continue;
@@ -7408,7 +7734,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7408
7734
  });
7409
7735
  }
7410
7736
  const anchorId = service.node.id;
7411
- const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7737
+ const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
7412
7738
  const add = (edgeType, kind, name) => {
7413
7739
  if (!name) return;
7414
7740
  const result = emitPlatformResourceEdge(
@@ -7424,10 +7750,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7424
7750
  nodesAdded += result.nodesAdded;
7425
7751
  edgesAdded += result.edgesAdded;
7426
7752
  };
7427
- add(import_types28.EdgeType.RUNS_ON, "supabase", "supabase");
7428
- for (const fn of Object.keys(config.functions ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "supabase-function", fn);
7429
- if (config.storage) add(import_types28.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7430
- if (config.auth) add(import_types28.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7753
+ add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7754
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7755
+ if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7756
+ if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7431
7757
  }
7432
7758
  return { nodesAdded, edgesAdded };
7433
7759
  }
@@ -7449,17 +7775,17 @@ async function addInfra(graph, scanPath, services) {
7449
7775
  }
7450
7776
 
7451
7777
  // src/extract/index.ts
7452
- var import_node_path41 = __toESM(require("path"), 1);
7778
+ var import_node_path42 = __toESM(require("path"), 1);
7453
7779
 
7454
7780
  // src/extract/retire.ts
7455
7781
  init_cjs_shims();
7456
7782
  var import_node_fs24 = require("fs");
7457
- var import_node_path40 = __toESM(require("path"), 1);
7458
- var import_types29 = require("@neat.is/types");
7783
+ var import_node_path41 = __toESM(require("path"), 1);
7784
+ var import_types30 = require("@neat.is/types");
7459
7785
  function dropOrphanedFileNodes(graph) {
7460
7786
  const orphans = [];
7461
7787
  graph.forEachNode((id, attrs) => {
7462
- if (attrs.type !== import_types29.NodeType.FileNode) return;
7788
+ if (attrs.type !== import_types30.NodeType.FileNode) return;
7463
7789
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7464
7790
  orphans.push(id);
7465
7791
  }
@@ -7472,7 +7798,7 @@ function retireEdgesByFile(graph, file) {
7472
7798
  const toDrop = [];
7473
7799
  graph.forEachEdge((id, attrs) => {
7474
7800
  const edge = attrs;
7475
- if (edge.provenance !== import_types29.Provenance.EXTRACTED) return;
7801
+ if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
7476
7802
  if (!edge.evidence?.file) return;
7477
7803
  if (edge.evidence.file === normalized) toDrop.push(id);
7478
7804
  });
@@ -7485,14 +7811,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7485
7811
  const bases = [scanPath, ...serviceDirs];
7486
7812
  graph.forEachEdge((id, attrs) => {
7487
7813
  const edge = attrs;
7488
- if (edge.provenance !== import_types29.Provenance.EXTRACTED) return;
7814
+ if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
7489
7815
  const evidenceFile = edge.evidence?.file;
7490
7816
  if (!evidenceFile) return;
7491
- if (import_node_path40.default.isAbsolute(evidenceFile)) {
7817
+ if (import_node_path41.default.isAbsolute(evidenceFile)) {
7492
7818
  if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
7493
7819
  return;
7494
7820
  }
7495
- const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path40.default.join(base, evidenceFile)));
7821
+ const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path41.default.join(base, evidenceFile)));
7496
7822
  if (!found) toDrop.push(id);
7497
7823
  });
7498
7824
  for (const id of toDrop) graph.dropEdge(id);
@@ -7534,7 +7860,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7534
7860
  }
7535
7861
  const droppedEntries = drainDroppedExtracted();
7536
7862
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7537
- const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
7863
+ const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
7538
7864
  try {
7539
7865
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7540
7866
  } catch (err) {
@@ -7568,39 +7894,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7568
7894
 
7569
7895
  // src/divergences.ts
7570
7896
  init_cjs_shims();
7571
- var import_types30 = require("@neat.is/types");
7897
+ var import_types31 = require("@neat.is/types");
7572
7898
  function bucketKey(source, target, type) {
7573
7899
  return `${type}|${source}|${target}`;
7574
7900
  }
7575
7901
  function bucketSourceFor(graph, edge) {
7576
- if (edge.type !== import_types30.EdgeType.CONNECTS_TO) return edge.source;
7577
- const parsed = (0, import_types30.parseFileId)(edge.source);
7902
+ if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
7903
+ const parsed = (0, import_types31.parseFileId)(edge.source);
7578
7904
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
7579
7905
  const target = graph.getNodeAttributes(edge.target);
7580
- if (target.type !== import_types30.NodeType.DatabaseNode) return edge.source;
7581
- return (0, import_types30.serviceId)(parsed.service);
7906
+ if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
7907
+ return (0, import_types31.serviceId)(parsed.service);
7582
7908
  }
7583
7909
  function bucketEdges(graph) {
7584
7910
  const buckets2 = /* @__PURE__ */ new Map();
7585
7911
  graph.forEachEdge((id, attrs) => {
7586
7912
  const e = attrs;
7587
- const parsed = (0, import_types30.parseEdgeId)(id);
7913
+ const parsed = (0, import_types31.parseEdgeId)(id);
7588
7914
  const provenance = parsed?.provenance ?? e.provenance;
7589
7915
  const source = bucketSourceFor(graph, e);
7590
7916
  const key = bucketKey(source, e.target, e.type);
7591
7917
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
7592
7918
  switch (provenance) {
7593
- case import_types30.Provenance.EXTRACTED:
7919
+ case import_types31.Provenance.EXTRACTED:
7594
7920
  cur.extracted = e;
7595
7921
  break;
7596
- case import_types30.Provenance.OBSERVED:
7922
+ case import_types31.Provenance.OBSERVED:
7597
7923
  cur.observed = e;
7598
7924
  break;
7599
- case import_types30.Provenance.INFERRED:
7925
+ case import_types31.Provenance.INFERRED:
7600
7926
  cur.inferred = e;
7601
7927
  break;
7602
7928
  default:
7603
- if (e.provenance === import_types30.Provenance.STALE) cur.stale = e;
7929
+ if (e.provenance === import_types31.Provenance.STALE) cur.stale = e;
7604
7930
  }
7605
7931
  buckets2.set(key, cur);
7606
7932
  });
@@ -7609,12 +7935,12 @@ function bucketEdges(graph) {
7609
7935
  function nodeIsFrontier(graph, nodeId) {
7610
7936
  if (!graph.hasNode(nodeId)) return false;
7611
7937
  const attrs = graph.getNodeAttributes(nodeId);
7612
- return attrs.type === import_types30.NodeType.FrontierNode;
7938
+ return attrs.type === import_types31.NodeType.FrontierNode;
7613
7939
  }
7614
7940
  function nodeIsWebsocketChannel(graph, nodeId) {
7615
7941
  if (!graph.hasNode(nodeId)) return false;
7616
7942
  const attrs = graph.getNodeAttributes(nodeId);
7617
- return attrs.type === import_types30.NodeType.WebSocketChannelNode;
7943
+ return attrs.type === import_types31.NodeType.WebSocketChannelNode;
7618
7944
  }
7619
7945
  function clampConfidence(n) {
7620
7946
  if (!Number.isFinite(n)) return 0;
@@ -7634,14 +7960,14 @@ function gradedConfidence(edge) {
7634
7960
  return clampConfidence(confidenceForEdge(edge));
7635
7961
  }
7636
7962
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
7637
- import_types30.EdgeType.CALLS,
7638
- import_types30.EdgeType.CONNECTS_TO,
7639
- import_types30.EdgeType.PUBLISHES_TO,
7640
- import_types30.EdgeType.CONSUMES_FROM
7963
+ import_types31.EdgeType.CALLS,
7964
+ import_types31.EdgeType.CONNECTS_TO,
7965
+ import_types31.EdgeType.PUBLISHES_TO,
7966
+ import_types31.EdgeType.CONSUMES_FROM
7641
7967
  ]);
7642
7968
  function detectMissingDivergences(graph, bucket) {
7643
7969
  const out = [];
7644
- if (bucket.type === import_types30.EdgeType.CONTAINS) return out;
7970
+ if (bucket.type === import_types31.EdgeType.CONTAINS) return out;
7645
7971
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
7646
7972
  if (!nodeIsFrontier(graph, bucket.target)) {
7647
7973
  out.push({
@@ -7682,7 +8008,7 @@ function declaredHostFor(svc) {
7682
8008
  function hasExtractedConfiguredBy(graph, svcId) {
7683
8009
  for (const edgeId of graph.outboundEdges(svcId)) {
7684
8010
  const e = graph.getEdgeAttributes(edgeId);
7685
- if (e.type === import_types30.EdgeType.CONFIGURED_BY && e.provenance === import_types30.Provenance.EXTRACTED) {
8011
+ if (e.type === import_types31.EdgeType.CONFIGURED_BY && e.provenance === import_types31.Provenance.EXTRACTED) {
7686
8012
  return true;
7687
8013
  }
7688
8014
  }
@@ -7695,10 +8021,10 @@ function detectHostMismatch(graph, svcId, svc) {
7695
8021
  const out = [];
7696
8022
  for (const edgeId of graph.outboundEdges(svcId)) {
7697
8023
  const edge = graph.getEdgeAttributes(edgeId);
7698
- if (edge.type !== import_types30.EdgeType.CONNECTS_TO) continue;
7699
- if (edge.provenance !== import_types30.Provenance.OBSERVED) continue;
8024
+ if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
8025
+ if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
7700
8026
  const target = graph.getNodeAttributes(edge.target);
7701
- if (target.type !== import_types30.NodeType.DatabaseNode) continue;
8027
+ if (target.type !== import_types31.NodeType.DatabaseNode) continue;
7702
8028
  const observedHost = target.host?.trim();
7703
8029
  if (!observedHost) continue;
7704
8030
  if (observedHost === declaredHost) continue;
@@ -7720,10 +8046,10 @@ function detectCompatDivergences(graph, svcId, svc) {
7720
8046
  const deps = svc.dependencies ?? {};
7721
8047
  for (const edgeId of graph.outboundEdges(svcId)) {
7722
8048
  const edge = graph.getEdgeAttributes(edgeId);
7723
- if (edge.type !== import_types30.EdgeType.CONNECTS_TO) continue;
7724
- if (edge.provenance !== import_types30.Provenance.OBSERVED) continue;
8049
+ if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
8050
+ if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
7725
8051
  const target = graph.getNodeAttributes(edge.target);
7726
- if (target.type !== import_types30.NodeType.DatabaseNode) continue;
8052
+ if (target.type !== import_types31.NodeType.DatabaseNode) continue;
7727
8053
  for (const pair of compatPairs()) {
7728
8054
  if (pair.engine !== target.engine) continue;
7729
8055
  const declared = deps[pair.driver];
@@ -7782,7 +8108,7 @@ function suppressHostMismatchHalves(all) {
7782
8108
  for (const d of all) {
7783
8109
  if (d.type !== "host-mismatch") continue;
7784
8110
  observedHalf.add(`${d.source}->${d.target}`);
7785
- declaredHalf.add((0, import_types30.databaseId)(d.extractedHost));
8111
+ declaredHalf.add((0, import_types31.databaseId)(d.extractedHost));
7786
8112
  }
7787
8113
  if (observedHalf.size === 0) return all;
7788
8114
  return all.filter((d) => {
@@ -7801,7 +8127,7 @@ function computeDivergences(graph, opts = {}) {
7801
8127
  }
7802
8128
  graph.forEachNode((nodeId, attrs) => {
7803
8129
  const n = attrs;
7804
- if (n.type !== import_types30.NodeType.ServiceNode) return;
8130
+ if (n.type !== import_types31.NodeType.ServiceNode) return;
7805
8131
  const svc = n;
7806
8132
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
7807
8133
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -7835,7 +8161,7 @@ function computeDivergences(graph, opts = {}) {
7835
8161
  if (a.source !== b.source) return a.source.localeCompare(b.source);
7836
8162
  return a.target.localeCompare(b.target);
7837
8163
  });
7838
- return import_types30.DivergenceResultSchema.parse({
8164
+ return import_types31.DivergenceResultSchema.parse({
7839
8165
  divergences: filtered,
7840
8166
  totalAffected: filtered.length,
7841
8167
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -7845,8 +8171,8 @@ function computeDivergences(graph, opts = {}) {
7845
8171
  // src/persist.ts
7846
8172
  init_cjs_shims();
7847
8173
  var import_node_fs25 = require("fs");
7848
- var import_node_path42 = __toESM(require("path"), 1);
7849
- var import_types31 = require("@neat.is/types");
8174
+ var import_node_path43 = __toESM(require("path"), 1);
8175
+ var import_types32 = require("@neat.is/types");
7850
8176
  var SCHEMA_VERSION = 4;
7851
8177
  function migrateV1ToV2(payload) {
7852
8178
  const nodes = payload.graph.nodes;
@@ -7868,12 +8194,12 @@ function migrateV2ToV3(payload) {
7868
8194
  for (const edge of edges) {
7869
8195
  const attrs = edge.attributes;
7870
8196
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
7871
- attrs.provenance = import_types31.Provenance.OBSERVED;
8197
+ attrs.provenance = import_types32.Provenance.OBSERVED;
7872
8198
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
7873
8199
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
7874
8200
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
7875
8201
  if (type && source && target) {
7876
- const newId = (0, import_types31.observedEdgeId)(source, target, type);
8202
+ const newId = (0, import_types32.observedEdgeId)(source, target, type);
7877
8203
  attrs.id = newId;
7878
8204
  if (edge.key) edge.key = newId;
7879
8205
  }
@@ -7882,7 +8208,7 @@ function migrateV2ToV3(payload) {
7882
8208
  return { ...payload, schemaVersion: 3 };
7883
8209
  }
7884
8210
  async function ensureDir(filePath) {
7885
- await import_node_fs25.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
8211
+ await import_node_fs25.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
7886
8212
  }
7887
8213
  async function saveGraphToDisk(graph, outPath) {
7888
8214
  await ensureDir(outPath);
@@ -7964,7 +8290,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
7964
8290
  // src/gitignore.ts
7965
8291
  init_cjs_shims();
7966
8292
  var import_node_fs26 = require("fs");
7967
- var import_node_path43 = __toESM(require("path"), 1);
8293
+ var import_node_path44 = __toESM(require("path"), 1);
7968
8294
  var NEAT_OUT_LINE = "neat-out/";
7969
8295
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
7970
8296
  function isNeatOutLine(line) {
@@ -7972,7 +8298,7 @@ function isNeatOutLine(line) {
7972
8298
  return trimmed === "neat-out/" || trimmed === "neat-out";
7973
8299
  }
7974
8300
  async function ensureNeatOutIgnored(projectDir) {
7975
- const file = import_node_path43.default.join(projectDir, ".gitignore");
8301
+ const file = import_node_path44.default.join(projectDir, ".gitignore");
7976
8302
  let existing = null;
7977
8303
  try {
7978
8304
  existing = await import_node_fs26.promises.readFile(file, "utf8");
@@ -7999,7 +8325,7 @@ ${NEAT_OUT_LINE}
7999
8325
 
8000
8326
  // src/summary.ts
8001
8327
  init_cjs_shims();
8002
- var import_types32 = require("@neat.is/types");
8328
+ var import_types33 = require("@neat.is/types");
8003
8329
  function renderOtelEnvBlock() {
8004
8330
  return [
8005
8331
  "for prod OTel routing, set these in your deploy platform's env:",
@@ -8009,19 +8335,19 @@ function renderOtelEnvBlock() {
8009
8335
  }
8010
8336
  function findIncompatServices(nodes) {
8011
8337
  return nodes.filter(
8012
- (n) => n.type === import_types32.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
8338
+ (n) => n.type === import_types33.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
8013
8339
  );
8014
8340
  }
8015
8341
  function servicesWithoutObserved(nodes, edges) {
8016
8342
  const seen = /* @__PURE__ */ new Set();
8017
8343
  for (const e of edges) {
8018
- if (e.provenance === import_types32.Provenance.OBSERVED) {
8344
+ if (e.provenance === import_types33.Provenance.OBSERVED) {
8019
8345
  seen.add(e.source);
8020
8346
  seen.add(e.target);
8021
8347
  }
8022
8348
  }
8023
8349
  return nodes.filter(
8024
- (n) => n.type === import_types32.NodeType.ServiceNode && !seen.has(n.id)
8350
+ (n) => n.type === import_types33.NodeType.ServiceNode && !seen.has(n.id)
8025
8351
  );
8026
8352
  }
8027
8353
  function formatDivergence(d) {
@@ -8096,26 +8422,26 @@ function formatIncompat(inc) {
8096
8422
  // src/watch.ts
8097
8423
  init_cjs_shims();
8098
8424
  var import_node_fs35 = __toESM(require("fs"), 1);
8099
- var import_node_path54 = __toESM(require("path"), 1);
8425
+ var import_node_path55 = __toESM(require("path"), 1);
8100
8426
  var import_chokidar = __toESM(require("chokidar"), 1);
8101
8427
 
8102
8428
  // src/api.ts
8103
8429
  init_cjs_shims();
8104
8430
  var import_fastify = __toESM(require("fastify"), 1);
8105
8431
  var import_cors = __toESM(require("@fastify/cors"), 1);
8106
- var import_types34 = require("@neat.is/types");
8432
+ var import_types35 = require("@neat.is/types");
8107
8433
 
8108
8434
  // src/extend/index.ts
8109
8435
  init_cjs_shims();
8110
8436
  var import_node_fs28 = require("fs");
8111
- var import_node_path45 = __toESM(require("path"), 1);
8437
+ var import_node_path46 = __toESM(require("path"), 1);
8112
8438
  var import_node_os2 = __toESM(require("os"), 1);
8113
8439
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
8114
8440
 
8115
8441
  // src/installers/package-manager.ts
8116
8442
  init_cjs_shims();
8117
8443
  var import_node_fs27 = require("fs");
8118
- var import_node_path44 = __toESM(require("path"), 1);
8444
+ var import_node_path45 = __toESM(require("path"), 1);
8119
8445
  var import_node_child_process = require("child_process");
8120
8446
  var LOCKFILE_PRIORITY = [
8121
8447
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -8137,22 +8463,22 @@ async function exists2(p) {
8137
8463
  }
8138
8464
  }
8139
8465
  async function detectPackageManager(serviceDir) {
8140
- let dir = import_node_path44.default.resolve(serviceDir);
8466
+ let dir = import_node_path45.default.resolve(serviceDir);
8141
8467
  const stops = /* @__PURE__ */ new Set();
8142
8468
  for (let i = 0; i < 64; i++) {
8143
8469
  if (stops.has(dir)) break;
8144
8470
  stops.add(dir);
8145
8471
  for (const candidate of LOCKFILE_PRIORITY) {
8146
- const lockPath = import_node_path44.default.join(dir, candidate.lockfile);
8472
+ const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
8147
8473
  if (await exists2(lockPath)) {
8148
8474
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
8149
8475
  }
8150
8476
  }
8151
- const parent = import_node_path44.default.dirname(dir);
8477
+ const parent = import_node_path45.default.dirname(dir);
8152
8478
  if (parent === dir) break;
8153
8479
  dir = parent;
8154
8480
  }
8155
- return { pm: "npm", cwd: import_node_path44.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8481
+ return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8156
8482
  }
8157
8483
  async function runPackageManagerInstall(cmd) {
8158
8484
  return new Promise((resolve) => {
@@ -8201,7 +8527,7 @@ async function fileExists2(p) {
8201
8527
  }
8202
8528
  }
8203
8529
  async function readPackageJson(scanPath) {
8204
- const pkgPath = import_node_path45.default.join(scanPath, "package.json");
8530
+ const pkgPath = import_node_path46.default.join(scanPath, "package.json");
8205
8531
  const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
8206
8532
  return JSON.parse(raw);
8207
8533
  }
@@ -8220,11 +8546,11 @@ async function findHookFiles(scanPath) {
8220
8546
  for (const entry2 of entries) {
8221
8547
  if (entry2.isDirectory()) {
8222
8548
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
8223
- await walk3(import_node_path45.default.join(dir, entry2.name));
8549
+ await walk3(import_node_path46.default.join(dir, entry2.name));
8224
8550
  } else if (entry2.isFile()) {
8225
8551
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
8226
- const rel = import_node_path45.default.relative(scanPath, import_node_path45.default.join(dir, entry2.name));
8227
- found.push(rel.split(import_node_path45.default.sep).join("/"));
8552
+ const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
8553
+ found.push(rel.split(import_node_path46.default.sep).join("/"));
8228
8554
  }
8229
8555
  }
8230
8556
  }
@@ -8235,7 +8561,7 @@ async function findHookFiles(scanPath) {
8235
8561
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8236
8562
  let fallback = null;
8237
8563
  for (const file of hookFiles) {
8238
- const content = await import_node_fs28.promises.readFile(import_node_path45.default.join(scanPath, file), "utf8");
8564
+ const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
8239
8565
  const patched = splicedContent(content, snippet2);
8240
8566
  if (patched !== null) return { file, content, patched };
8241
8567
  if (fallback === null) fallback = { file, content };
@@ -8243,11 +8569,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8243
8569
  return { file: fallback.file, content: fallback.content, patched: null };
8244
8570
  }
8245
8571
  function extendLogPath() {
8246
- return process.env.NEAT_EXTEND_LOG ?? import_node_path45.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8572
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8247
8573
  }
8248
8574
  async function appendExtendLog(entry2) {
8249
8575
  const logPath = extendLogPath();
8250
- await import_node_fs28.promises.mkdir(import_node_path45.default.dirname(logPath), { recursive: true });
8576
+ await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
8251
8577
  await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
8252
8578
  }
8253
8579
  function splicedContent(fileContent, snippet2) {
@@ -8306,7 +8632,7 @@ function lookupInstrumentation(library, installedVersion) {
8306
8632
  }
8307
8633
  async function describeProjectInstrumentation(ctx) {
8308
8634
  const hookFiles = await findHookFiles(ctx.scanPath);
8309
- const envNeat = await fileExists2(import_node_path45.default.join(ctx.scanPath, ".env.neat"));
8635
+ const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
8310
8636
  const registryInstrPackages = new Set(
8311
8637
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
8312
8638
  );
@@ -8328,7 +8654,7 @@ async function applyExtension(ctx, args, options) {
8328
8654
  );
8329
8655
  }
8330
8656
  for (const file of hookFiles) {
8331
- const content = await import_node_fs28.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
8657
+ const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
8332
8658
  if (content.includes(args.registration_snippet)) {
8333
8659
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
8334
8660
  }
@@ -8340,10 +8666,10 @@ async function applyExtension(ctx, args, options) {
8340
8666
  );
8341
8667
  }
8342
8668
  const primaryFile = primary.file;
8343
- const primaryPath = import_node_path45.default.join(ctx.scanPath, primaryFile);
8669
+ const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
8344
8670
  const filesTouched = [];
8345
8671
  const depsAdded = [];
8346
- const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
8672
+ const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
8347
8673
  const pkg = await readPackageJson(ctx.scanPath);
8348
8674
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
8349
8675
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -8382,7 +8708,7 @@ async function dryRunExtension(ctx, args) {
8382
8708
  };
8383
8709
  }
8384
8710
  for (const file of hookFiles) {
8385
- const content = await import_node_fs28.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
8711
+ const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
8386
8712
  if (content.includes(args.registration_snippet)) {
8387
8713
  return {
8388
8714
  library: args.library,
@@ -8423,7 +8749,7 @@ async function rollbackExtension(ctx, args) {
8423
8749
  if (!match) {
8424
8750
  return { undone: false, message: "no apply found for library" };
8425
8751
  }
8426
- const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
8752
+ const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
8427
8753
  if (await fileExists2(pkgPath)) {
8428
8754
  const pkg = await readPackageJson(ctx.scanPath);
8429
8755
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -8434,7 +8760,7 @@ async function rollbackExtension(ctx, args) {
8434
8760
  }
8435
8761
  const hookFiles = await findHookFiles(ctx.scanPath);
8436
8762
  for (const file of hookFiles) {
8437
- const filePath = import_node_path45.default.join(ctx.scanPath, file);
8763
+ const filePath = import_node_path46.default.join(ctx.scanPath, file);
8438
8764
  const content = await import_node_fs28.promises.readFile(filePath, "utf8");
8439
8765
  if (content.includes(match.registration_snippet)) {
8440
8766
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -8572,23 +8898,23 @@ function canonicalJson(value) {
8572
8898
 
8573
8899
  // src/projects.ts
8574
8900
  init_cjs_shims();
8575
- var import_node_path46 = __toESM(require("path"), 1);
8901
+ var import_node_path47 = __toESM(require("path"), 1);
8576
8902
  function pathsForProject(project, baseDir) {
8577
8903
  if (project === DEFAULT_PROJECT) {
8578
8904
  return {
8579
- snapshotPath: import_node_path46.default.join(baseDir, "graph.json"),
8580
- errorsPath: import_node_path46.default.join(baseDir, "errors.ndjson"),
8581
- staleEventsPath: import_node_path46.default.join(baseDir, "stale-events.ndjson"),
8582
- embeddingsCachePath: import_node_path46.default.join(baseDir, "embeddings.json"),
8583
- policyViolationsPath: import_node_path46.default.join(baseDir, "policy-violations.ndjson")
8905
+ snapshotPath: import_node_path47.default.join(baseDir, "graph.json"),
8906
+ errorsPath: import_node_path47.default.join(baseDir, "errors.ndjson"),
8907
+ staleEventsPath: import_node_path47.default.join(baseDir, "stale-events.ndjson"),
8908
+ embeddingsCachePath: import_node_path47.default.join(baseDir, "embeddings.json"),
8909
+ policyViolationsPath: import_node_path47.default.join(baseDir, "policy-violations.ndjson")
8584
8910
  };
8585
8911
  }
8586
8912
  return {
8587
- snapshotPath: import_node_path46.default.join(baseDir, `${project}.json`),
8588
- errorsPath: import_node_path46.default.join(baseDir, `errors.${project}.ndjson`),
8589
- staleEventsPath: import_node_path46.default.join(baseDir, `stale-events.${project}.ndjson`),
8590
- embeddingsCachePath: import_node_path46.default.join(baseDir, `embeddings.${project}.json`),
8591
- policyViolationsPath: import_node_path46.default.join(baseDir, `policy-violations.${project}.ndjson`)
8913
+ snapshotPath: import_node_path47.default.join(baseDir, `${project}.json`),
8914
+ errorsPath: import_node_path47.default.join(baseDir, `errors.${project}.ndjson`),
8915
+ staleEventsPath: import_node_path47.default.join(baseDir, `stale-events.${project}.ndjson`),
8916
+ embeddingsCachePath: import_node_path47.default.join(baseDir, `embeddings.${project}.json`),
8917
+ policyViolationsPath: import_node_path47.default.join(baseDir, `policy-violations.${project}.ndjson`)
8592
8918
  };
8593
8919
  }
8594
8920
  var Projects = class {
@@ -8626,26 +8952,26 @@ var Projects = class {
8626
8952
  init_cjs_shims();
8627
8953
  var import_node_fs30 = require("fs");
8628
8954
  var import_node_os3 = __toESM(require("os"), 1);
8629
- var import_node_path47 = __toESM(require("path"), 1);
8630
- var import_types33 = require("@neat.is/types");
8955
+ var import_node_path48 = __toESM(require("path"), 1);
8956
+ var import_types34 = require("@neat.is/types");
8631
8957
  var LOCK_TIMEOUT_MS = 5e3;
8632
8958
  var LOCK_RETRY_MS = 50;
8633
8959
  function neatHome() {
8634
8960
  const override = process.env.NEAT_HOME;
8635
- if (override && override.length > 0) return import_node_path47.default.resolve(override);
8636
- return import_node_path47.default.join(import_node_os3.default.homedir(), ".neat");
8961
+ if (override && override.length > 0) return import_node_path48.default.resolve(override);
8962
+ return import_node_path48.default.join(import_node_os3.default.homedir(), ".neat");
8637
8963
  }
8638
8964
  function registryPath() {
8639
- return import_node_path47.default.join(neatHome(), "projects.json");
8965
+ return import_node_path48.default.join(neatHome(), "projects.json");
8640
8966
  }
8641
8967
  function registryLockPath() {
8642
- return import_node_path47.default.join(neatHome(), "projects.json.lock");
8968
+ return import_node_path48.default.join(neatHome(), "projects.json.lock");
8643
8969
  }
8644
8970
  function daemonPidPath() {
8645
- return import_node_path47.default.join(neatHome(), "neatd.pid");
8971
+ return import_node_path48.default.join(neatHome(), "neatd.pid");
8646
8972
  }
8647
8973
  function daemonsDir() {
8648
- return import_node_path47.default.join(neatHome(), "daemons");
8974
+ return import_node_path48.default.join(neatHome(), "daemons");
8649
8975
  }
8650
8976
  function isFiniteInt(v) {
8651
8977
  return typeof v === "number" && Number.isFinite(v);
@@ -8686,7 +9012,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
8686
9012
  const out = [];
8687
9013
  for (const name of names) {
8688
9014
  if (!name.endsWith(".json")) continue;
8689
- const file = import_node_path47.default.join(dir, name);
9015
+ const file = import_node_path48.default.join(dir, name);
8690
9016
  let raw;
8691
9017
  try {
8692
9018
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -8807,7 +9133,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
8807
9133
  }
8808
9134
  }
8809
9135
  async function normalizeProjectPath(input) {
8810
- const resolved = import_node_path47.default.resolve(input);
9136
+ const resolved = import_node_path48.default.resolve(input);
8811
9137
  try {
8812
9138
  return await import_node_fs30.promises.realpath(resolved);
8813
9139
  } catch {
@@ -8815,7 +9141,7 @@ async function normalizeProjectPath(input) {
8815
9141
  }
8816
9142
  }
8817
9143
  async function writeAtomically(target, contents) {
8818
- await import_node_fs30.promises.mkdir(import_node_path47.default.dirname(target), { recursive: true });
9144
+ await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
8819
9145
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
8820
9146
  const fd = await import_node_fs30.promises.open(tmp, "w");
8821
9147
  try {
@@ -8828,7 +9154,7 @@ async function writeAtomically(target, contents) {
8828
9154
  }
8829
9155
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
8830
9156
  const deadline = Date.now() + timeoutMs;
8831
- await import_node_fs30.promises.mkdir(import_node_path47.default.dirname(lockPath), { recursive: true });
9157
+ await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(lockPath), { recursive: true });
8832
9158
  let probedHolder = false;
8833
9159
  while (true) {
8834
9160
  try {
@@ -8881,10 +9207,10 @@ async function readRegistry() {
8881
9207
  throw err;
8882
9208
  }
8883
9209
  const parsed = JSON.parse(raw);
8884
- return import_types33.RegistryFileSchema.parse(parsed);
9210
+ return import_types34.RegistryFileSchema.parse(parsed);
8885
9211
  }
8886
9212
  async function writeRegistry(reg) {
8887
- const validated = import_types33.RegistryFileSchema.parse(reg);
9213
+ const validated = import_types34.RegistryFileSchema.parse(reg);
8888
9214
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
8889
9215
  }
8890
9216
  var ProjectNameCollisionError = class extends Error {
@@ -9070,7 +9396,7 @@ init_auth();
9070
9396
  // src/connectors-config.ts
9071
9397
  init_cjs_shims();
9072
9398
  var import_node_os4 = __toESM(require("os"), 1);
9073
- var import_node_path48 = __toESM(require("path"), 1);
9399
+ var import_node_path49 = __toESM(require("path"), 1);
9074
9400
  var import_node_fs31 = require("fs");
9075
9401
  var CONNECTORS_CONFIG_VERSION = 1;
9076
9402
  var EnvRefUnsetError = class extends Error {
@@ -9085,11 +9411,11 @@ var EnvRefUnsetError = class extends Error {
9085
9411
  };
9086
9412
  function neatHome2() {
9087
9413
  const override = process.env.NEAT_HOME;
9088
- if (override && override.length > 0) return import_node_path48.default.resolve(override);
9089
- return import_node_path48.default.join(import_node_os4.default.homedir(), ".neat");
9414
+ if (override && override.length > 0) return import_node_path49.default.resolve(override);
9415
+ return import_node_path49.default.join(import_node_os4.default.homedir(), ".neat");
9090
9416
  }
9091
9417
  function connectorsConfigPath(home = neatHome2()) {
9092
- return import_node_path48.default.join(home, "connectors.json");
9418
+ return import_node_path49.default.join(home, "connectors.json");
9093
9419
  }
9094
9420
  var MODE_MASK_LOOSER_THAN_0600 = 63;
9095
9421
  async function warnIfModeLooserThan0600(file) {
@@ -9220,7 +9546,7 @@ function connectorMatchesProject(entry2, project) {
9220
9546
  var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
9221
9547
  var CONNECTORS_LOCK_RETRY_MS = 50;
9222
9548
  function connectorsConfigLockPath(home = neatHome2()) {
9223
- return import_node_path48.default.join(home, "connectors.json.lock");
9549
+ return import_node_path49.default.join(home, "connectors.json.lock");
9224
9550
  }
9225
9551
  function isEnvRef(value) {
9226
9552
  return value.length > 1 && value.startsWith("$");
@@ -9233,7 +9559,7 @@ function redactCredentialRef(ref) {
9233
9559
  return out;
9234
9560
  }
9235
9561
  async function writeConfigAtomically0600(file, contents) {
9236
- await import_node_fs31.promises.mkdir(import_node_path48.default.dirname(file), { recursive: true });
9562
+ await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(file), { recursive: true });
9237
9563
  const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
9238
9564
  const fd = await import_node_fs31.promises.open(tmp, "w", 384);
9239
9565
  try {
@@ -9247,7 +9573,7 @@ async function writeConfigAtomically0600(file, contents) {
9247
9573
  }
9248
9574
  async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
9249
9575
  const deadline = Date.now() + timeoutMs;
9250
- await import_node_fs31.promises.mkdir(import_node_path48.default.dirname(lockPath), { recursive: true });
9576
+ await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
9251
9577
  for (; ; ) {
9252
9578
  try {
9253
9579
  const fd = await import_node_fs31.promises.open(lockPath, "wx");
@@ -9530,11 +9856,11 @@ function registerRoutes(scope, ctx) {
9530
9856
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
9531
9857
  const parsed = [];
9532
9858
  for (const c of candidates) {
9533
- const r = import_types34.DivergenceTypeSchema.safeParse(c);
9859
+ const r = import_types35.DivergenceTypeSchema.safeParse(c);
9534
9860
  if (!r.success) {
9535
9861
  return reply.code(400).send({
9536
9862
  error: `unknown divergence type "${c}"`,
9537
- allowed: import_types34.DivergenceTypeSchema.options
9863
+ allowed: import_types35.DivergenceTypeSchema.options
9538
9864
  });
9539
9865
  }
9540
9866
  parsed.push(r.data);
@@ -9805,7 +10131,7 @@ function registerRoutes(scope, ctx) {
9805
10131
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
9806
10132
  let violations = await log.readAll();
9807
10133
  if (req.query.severity) {
9808
- const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
10134
+ const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
9809
10135
  if (!sev.success) {
9810
10136
  return reply.code(400).send({
9811
10137
  error: "invalid severity",
@@ -9844,7 +10170,7 @@ function registerRoutes(scope, ctx) {
9844
10170
  scope.post("/policies/check", async (req, reply) => {
9845
10171
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
9846
10172
  if (!proj) return;
9847
- const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
10173
+ const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9848
10174
  if (!parsed.success) {
9849
10175
  return reply.code(400).send({
9850
10176
  error: "invalid /policies/check body",
@@ -10110,13 +10436,13 @@ init_otel();
10110
10436
  // src/daemon.ts
10111
10437
  init_cjs_shims();
10112
10438
  var import_node_fs33 = require("fs");
10113
- var import_node_path52 = __toESM(require("path"), 1);
10439
+ var import_node_path53 = __toESM(require("path"), 1);
10114
10440
  var import_node_module = require("module");
10115
10441
  init_otel();
10116
10442
 
10117
10443
  // src/connectors/index.ts
10118
10444
  init_cjs_shims();
10119
- var import_types35 = require("@neat.is/types");
10445
+ var import_types36 = require("@neat.is/types");
10120
10446
 
10121
10447
  // src/connectors/registry.ts
10122
10448
  init_cjs_shims();
@@ -10194,7 +10520,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
10194
10520
  supabase: { capacity: 30, refillMs: 1e4 },
10195
10521
  // Not a documented API limit at all — a self-imposed ceiling on the raw
10196
10522
  // pg_stat_statements connection (see module header above).
10197
- "supabase-postgres": { capacity: 20, refillMs: 3e3 }
10523
+ "supabase-postgres": { capacity: 20, refillMs: 3e3 },
10524
+ // Push provider (ADR-146): the Drains REST API is touched only by `neat
10525
+ // connector add/remove/test` (provision/deprovision/validate), never a poll
10526
+ // loop, so this bucket is exercised a handful of times per command. Kept
10527
+ // conservative pending a documented Drains-API rate limit.
10528
+ vercel: { capacity: 20, refillMs: 5e3 }
10198
10529
  };
10199
10530
  var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
10200
10531
  function defaultRateLimitFor(provider) {
@@ -10486,10 +10817,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
10486
10817
  // src/connectors/supabase/map.ts
10487
10818
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
10488
10819
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
10489
- function targetFromRestPath(path62) {
10490
- const rpcMatch = REST_RPC_PATH_RE.exec(path62);
10820
+ function targetFromRestPath(path63) {
10821
+ const rpcMatch = REST_RPC_PATH_RE.exec(path63);
10491
10822
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
10492
- const tableMatch = REST_TABLE_PATH_RE.exec(path62);
10823
+ const tableMatch = REST_TABLE_PATH_RE.exec(path63);
10493
10824
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
10494
10825
  return null;
10495
10826
  }
@@ -10598,23 +10929,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10598
10929
 
10599
10930
  // src/connectors/supabase/resolve.ts
10600
10931
  init_cjs_shims();
10601
- var import_types37 = require("@neat.is/types");
10932
+ var import_types38 = require("@neat.is/types");
10602
10933
  function createSupabaseResolveTarget(graph, config) {
10603
10934
  return (signal, _ctx) => {
10604
10935
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10605
10936
  return null;
10606
10937
  }
10607
- const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10938
+ const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10608
10939
  if (graph.hasNode(subResourceId)) {
10609
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10940
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10610
10941
  }
10611
- const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
10942
+ const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
10612
10943
  if (graph.hasNode(bareResourceId)) {
10613
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10944
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10614
10945
  }
10615
- const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
10946
+ const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
10616
10947
  if (graph.hasNode(projectLevelId)) {
10617
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10948
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10618
10949
  }
10619
10950
  return null;
10620
10951
  };
@@ -10707,7 +11038,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10707
11038
 
10708
11039
  // src/connectors/railway/index.ts
10709
11040
  init_cjs_shims();
10710
- var import_types41 = require("@neat.is/types");
11041
+ var import_types42 = require("@neat.is/types");
10711
11042
 
10712
11043
  // src/connectors/railway/client.ts
10713
11044
  init_cjs_shims();
@@ -10833,7 +11164,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
10833
11164
  const out = [];
10834
11165
  graph.forEachNode((_id, attrs) => {
10835
11166
  const node = attrs;
10836
- if (node.type !== import_types41.NodeType.RouteNode) return;
11167
+ if (node.type !== import_types42.NodeType.RouteNode) return;
10837
11168
  const route = attrs;
10838
11169
  if (route.service !== serviceName) return;
10839
11170
  out.push({
@@ -10937,12 +11268,12 @@ function createRailwayResolveTarget(config) {
10937
11268
  const serviceName = config.serviceNameById[config.serviceId];
10938
11269
  if (!serviceName) return null;
10939
11270
  if (signal.targetKind === ROUTE_TARGET_KIND) {
10940
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
11271
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
10941
11272
  }
10942
11273
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
10943
11274
  const peerName = config.serviceNameById[signal.targetName];
10944
11275
  if (!peerName) return null;
10945
- return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
11276
+ return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
10946
11277
  }
10947
11278
  return null;
10948
11279
  };
@@ -11066,9 +11397,9 @@ function parseFirebaseTargetName(targetName) {
11066
11397
  const secondSep = rest.indexOf(FIELD_SEP);
11067
11398
  if (secondSep === -1) return null;
11068
11399
  const method = rest.slice(0, secondSep);
11069
- const path62 = rest.slice(secondSep + 1);
11070
- if (!resourceName || !method || !path62) return null;
11071
- return { resourceName, method, path: path62 };
11400
+ const path63 = rest.slice(secondSep + 1);
11401
+ if (!resourceName || !method || !path63) return null;
11402
+ return { resourceName, method, path: path63 };
11072
11403
  }
11073
11404
  function resourceNameFor(type, labels) {
11074
11405
  if (!labels) return null;
@@ -11106,14 +11437,14 @@ function mapLogEntryToSignal(entry2) {
11106
11437
  if (!req) return null;
11107
11438
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
11108
11439
  const method = req.requestMethod.toUpperCase();
11109
- const path62 = pathFromRequestUrl(req.requestUrl);
11110
- if (path62 === null) return null;
11440
+ const path63 = pathFromRequestUrl(req.requestUrl);
11441
+ if (path63 === null) return null;
11111
11442
  const timestamp = entry2.timestamp;
11112
11443
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
11113
11444
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
11114
11445
  return {
11115
11446
  targetKind: resourceType,
11116
- targetName: packFirebaseTargetName({ resourceName, method, path: path62 }),
11447
+ targetName: packFirebaseTargetName({ resourceName, method, path: path63 }),
11117
11448
  callCount: 1,
11118
11449
  errorCount: isError ? 1 : 0,
11119
11450
  lastObservedIso: timestamp
@@ -11130,7 +11461,7 @@ function mapLogEntriesToSignals(entries) {
11130
11461
 
11131
11462
  // src/connectors/firebase/resolve.ts
11132
11463
  init_cjs_shims();
11133
- var import_types42 = require("@neat.is/types");
11464
+ var import_types43 = require("@neat.is/types");
11134
11465
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
11135
11466
  switch (resourceType) {
11136
11467
  case "cloud_function":
@@ -11145,7 +11476,7 @@ function routeEntriesFor(graph, serviceName) {
11145
11476
  const entries = [];
11146
11477
  graph.forEachNode((_id, attrs) => {
11147
11478
  const node = attrs;
11148
- if (node.type !== import_types42.NodeType.RouteNode) return;
11479
+ if (node.type !== import_types43.NodeType.RouteNode) return;
11149
11480
  const route = attrs;
11150
11481
  if (route.service !== serviceName) return;
11151
11482
  entries.push({
@@ -11177,7 +11508,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
11177
11508
  return {
11178
11509
  targetNodeId: match.routeNodeId,
11179
11510
  serviceName,
11180
- edgeType: import_types42.EdgeType.CALLS
11511
+ edgeType: import_types43.EdgeType.CALLS
11181
11512
  };
11182
11513
  };
11183
11514
  }
@@ -11204,7 +11535,7 @@ init_cjs_shims();
11204
11535
 
11205
11536
  // src/connectors/cloudflare/connector.ts
11206
11537
  init_cjs_shims();
11207
- var import_types44 = require("@neat.is/types");
11538
+ var import_types45 = require("@neat.is/types");
11208
11539
 
11209
11540
  // src/connectors/cloudflare/client.ts
11210
11541
  init_cjs_shims();
@@ -11320,7 +11651,7 @@ function mapEventToSignal(event) {
11320
11651
  if (Number.isNaN(observedAt.getTime())) return null;
11321
11652
  const statusCode = metadata?.statusCode;
11322
11653
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
11323
- const path62 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11654
+ const path63 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11324
11655
  return {
11325
11656
  targetKind: CLOUDFLARE_TARGET_KIND,
11326
11657
  targetName: scriptName,
@@ -11328,7 +11659,7 @@ function mapEventToSignal(event) {
11328
11659
  errorCount: isError ? 1 : 0,
11329
11660
  lastObservedIso: observedAt.toISOString(),
11330
11661
  method,
11331
- ...path62 ? { path: path62 } : {},
11662
+ ...path63 ? { path: path63 } : {},
11332
11663
  ...typeof statusCode === "number" ? { statusCode } : {},
11333
11664
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
11334
11665
  };
@@ -11368,19 +11699,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
11368
11699
  graph.forEachNode((id, attrs) => {
11369
11700
  if (found) return;
11370
11701
  const a = attrs;
11371
- if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11702
+ if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11372
11703
  found = id;
11373
11704
  }
11374
11705
  });
11375
11706
  return found;
11376
11707
  }
11377
- function findMatchingRouteNode(graph, serviceName, method, path62) {
11378
- const normalizedPath = normalizePathTemplate(path62);
11708
+ function findMatchingRouteNode(graph, serviceName, method, path63) {
11709
+ const normalizedPath = normalizePathTemplate(path63);
11379
11710
  let found = null;
11380
11711
  graph.forEachNode((id, attrs) => {
11381
11712
  if (found) return;
11382
11713
  const a = attrs;
11383
- if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
11714
+ if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
11384
11715
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11385
11716
  const routeMethod = (a.method ?? "").toUpperCase();
11386
11717
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11392,18 +11723,18 @@ function createCloudflareResolveTarget(config, graph) {
11392
11723
  return (signal) => {
11393
11724
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
11394
11725
  const scriptName = signal.targetName;
11395
- const { method, path: path62 } = signal;
11726
+ const { method, path: path63 } = signal;
11396
11727
  const resolveRouteGrain = (serviceName, wholeFileId) => {
11397
- if (!method || !path62) return wholeFileId;
11398
- return findMatchingRouteNode(graph, serviceName, method, path62) ?? wholeFileId;
11728
+ if (!method || !path63) return wholeFileId;
11729
+ return findMatchingRouteNode(graph, serviceName, method, path63) ?? wholeFileId;
11399
11730
  };
11400
11731
  const mapping = config.workers?.[scriptName];
11401
11732
  if (mapping) {
11402
- const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
11733
+ const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
11403
11734
  return {
11404
11735
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11405
11736
  serviceName: mapping.service,
11406
- edgeType: import_types44.EdgeType.CALLS
11737
+ edgeType: import_types45.EdgeType.CALLS
11407
11738
  };
11408
11739
  }
11409
11740
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11412,18 +11743,124 @@ function createCloudflareResolveTarget(config, graph) {
11412
11743
  return {
11413
11744
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11414
11745
  serviceName: fileNode.service,
11415
- edgeType: import_types44.EdgeType.CALLS
11746
+ edgeType: import_types45.EdgeType.CALLS
11416
11747
  };
11417
11748
  }
11418
11749
  return {
11419
- targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
11750
+ targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
11420
11751
  serviceName: scriptName,
11421
- edgeType: import_types44.EdgeType.CALLS,
11752
+ edgeType: import_types45.EdgeType.CALLS,
11422
11753
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11423
11754
  };
11424
11755
  };
11425
11756
  }
11426
11757
 
11758
+ // src/connectors/vercel/index.ts
11759
+ init_cjs_shims();
11760
+
11761
+ // src/connectors/vercel/client.ts
11762
+ init_cjs_shims();
11763
+ var DEFAULT_API_BASE_URL = "https://api.vercel.com";
11764
+ var DEFAULT_DRAIN_NAME = "neat-otlp";
11765
+ var TRACE_SCHEMAS = { trace: { version: "v1" } };
11766
+ function apiBase(config) {
11767
+ return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
11768
+ }
11769
+ function teamQuery(config) {
11770
+ return `?teamId=${encodeURIComponent(config.teamId)}`;
11771
+ }
11772
+ function drainDelivery(config, otelToken) {
11773
+ return {
11774
+ type: "http",
11775
+ endpoint: config.endpoint,
11776
+ encoding: "json",
11777
+ headers: bearerAuthHeader(otelToken),
11778
+ ...config.secret ? { secret: config.secret } : {}
11779
+ };
11780
+ }
11781
+ async function describeError(res) {
11782
+ try {
11783
+ const data = await res.json();
11784
+ const message = data?.error?.message;
11785
+ return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
11786
+ } catch {
11787
+ return "";
11788
+ }
11789
+ }
11790
+ async function createVercelDrain(config, credentials, fetchImpl = fetch) {
11791
+ const projectIds = config.projectIds ?? [];
11792
+ const body = {
11793
+ name: config.drainName ?? DEFAULT_DRAIN_NAME,
11794
+ projects: projectIds.length > 0 ? "some" : "all",
11795
+ ...projectIds.length > 0 ? { projectIds } : {},
11796
+ schemas: TRACE_SCHEMAS,
11797
+ delivery: drainDelivery(config, credentials.otelToken),
11798
+ source: { kind: "self-served" }
11799
+ };
11800
+ const res = await junctionFetch(
11801
+ `${apiBase(config)}/v1/drains${teamQuery(config)}`,
11802
+ {
11803
+ method: "POST",
11804
+ headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
11805
+ body: JSON.stringify(body)
11806
+ },
11807
+ { provider: "vercel", accountKey: config.teamId, fetchImpl }
11808
+ );
11809
+ if (!res.ok) {
11810
+ throw new Error(
11811
+ `vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
11812
+ );
11813
+ }
11814
+ const payload = await res.json().catch(() => null);
11815
+ if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
11816
+ throw new Error(
11817
+ "vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
11818
+ );
11819
+ }
11820
+ return {
11821
+ id: payload.id,
11822
+ ...payload.status ? { status: payload.status } : {},
11823
+ ...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
11824
+ };
11825
+ }
11826
+ async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
11827
+ const res = await junctionFetch(
11828
+ `${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
11829
+ { method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
11830
+ { provider: "vercel", accountKey: config.teamId, fetchImpl }
11831
+ );
11832
+ if (res.ok || res.status === 404) return;
11833
+ throw new Error(
11834
+ `vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
11835
+ );
11836
+ }
11837
+ async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
11838
+ const res = await junctionFetch(
11839
+ `${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
11840
+ {
11841
+ method: "POST",
11842
+ headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
11843
+ body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
11844
+ },
11845
+ { provider: "vercel", accountKey: config.teamId, fetchImpl }
11846
+ );
11847
+ if (res.status === 401 || res.status === 403) {
11848
+ return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
11849
+ }
11850
+ if (!res.ok) {
11851
+ return {
11852
+ status: "failure",
11853
+ error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
11854
+ };
11855
+ }
11856
+ const payload = await res.json().catch(() => null);
11857
+ return {
11858
+ ...payload?.status ? { status: payload.status } : {},
11859
+ ...payload?.error ? { error: payload.error } : {},
11860
+ ...payload?.endpoint ? { endpoint: payload.endpoint } : {}
11861
+ };
11862
+ }
11863
+
11427
11864
  // src/connectors/registry.ts
11428
11865
  var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
11429
11866
  async function authProbe(input) {
@@ -11575,8 +12012,96 @@ var PROVIDER_DISPATCH = {
11575
12012
  }
11576
12013
  }
11577
12014
  };
11578
- function getProviderDispatch(provider) {
11579
- return PROVIDER_DISPATCH[provider];
12015
+ function vercelCredsFrom(credentials) {
12016
+ return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
12017
+ }
12018
+ function vercelConfigFromOptions(options) {
12019
+ const raw = options.projectIds;
12020
+ let projectIds;
12021
+ if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
12022
+ else if (typeof raw === "string" && raw.trim().length > 0) {
12023
+ projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
12024
+ }
12025
+ return {
12026
+ teamId: String(options.teamId ?? ""),
12027
+ endpoint: String(options.endpoint ?? ""),
12028
+ ...projectIds && projectIds.length > 0 ? { projectIds } : {},
12029
+ ...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
12030
+ ...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
12031
+ ...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
12032
+ ...typeof options.secret === "string" ? { secret: options.secret } : {}
12033
+ };
12034
+ }
12035
+ var PUSH_PROVIDER_DISPATCH = {
12036
+ vercel: {
12037
+ provider: "vercel",
12038
+ // The Vercel access token is the "primary" secret a single `--token`
12039
+ // populates; `otelToken` (the daemon's OTLP bearer) is the second field.
12040
+ primaryCredentialKey: "token",
12041
+ requiredCredentialFields: ["token", "otelToken"],
12042
+ // teamId scopes every Drains call; endpoint is where the drain delivers.
12043
+ // projectIds is optional (absent → the drain covers the whole team).
12044
+ requiredOptionFields: ["teamId", "endpoint"],
12045
+ // POST /v1/drains/test — authenticates the token and pings the endpoint
12046
+ // with a sample event, so `success` means the credential is live *and* the
12047
+ // daemon's OTLP endpoint is reachable and accepted the drain's bearer.
12048
+ async validate({ credentials, options, fetchImpl }) {
12049
+ const result = await testVercelDrainDelivery(
12050
+ vercelConfigFromOptions(options),
12051
+ vercelCredsFrom(credentials),
12052
+ fetchImpl
12053
+ );
12054
+ if (result.status === "success") return { ok: true };
12055
+ return {
12056
+ ok: false,
12057
+ reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
12058
+ };
12059
+ },
12060
+ // POST /v1/drains — creates the trace drain, returns its id to store in
12061
+ // `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
12062
+ // not a failure (the entry still points at a real drain).
12063
+ async provision({ credentials, options, fetchImpl }) {
12064
+ try {
12065
+ const created = await createVercelDrain(
12066
+ vercelConfigFromOptions(options),
12067
+ vercelCredsFrom(credentials),
12068
+ fetchImpl
12069
+ );
12070
+ 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;
12071
+ return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
12072
+ } catch (err) {
12073
+ return { ok: false, reason: err.message };
12074
+ }
12075
+ },
12076
+ // DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
12077
+ // success). No recorded drainId → nothing to delete, still a success.
12078
+ async deprovision({ credentials, options, fetchImpl }) {
12079
+ const drainId = typeof options.drainId === "string" ? options.drainId : "";
12080
+ if (!drainId) {
12081
+ return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
12082
+ }
12083
+ try {
12084
+ await deleteVercelDrain(
12085
+ vercelConfigFromOptions(options),
12086
+ drainId,
12087
+ vercelCredsFrom(credentials),
12088
+ fetchImpl
12089
+ );
12090
+ return { ok: true };
12091
+ } catch (err) {
12092
+ return { ok: false, reason: err.message };
12093
+ }
12094
+ }
12095
+ }
12096
+ };
12097
+ function isPushProvider(provider) {
12098
+ return provider in PUSH_PROVIDER_DISPATCH;
12099
+ }
12100
+ function getProviderFieldSchema(provider) {
12101
+ return PROVIDER_DISPATCH[provider] ?? PUSH_PROVIDER_DISPATCH[provider];
12102
+ }
12103
+ function knownProviderNames() {
12104
+ return [...Object.keys(PROVIDER_DISPATCH), ...Object.keys(PUSH_PROVIDER_DISPATCH)].sort();
11580
12105
  }
11581
12106
  function resolveEntryCredentials(dispatch, entry2, env) {
11582
12107
  let credentials;
@@ -11598,7 +12123,7 @@ function resolveEntryCredentials(dispatch, entry2, env) {
11598
12123
  return { ok: true, credentials };
11599
12124
  }
11600
12125
  async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
11601
- const dispatch = PROVIDER_DISPATCH[entry2.provider];
12126
+ const dispatch = PROVIDER_DISPATCH[entry2.provider] ?? PUSH_PROVIDER_DISPATCH[entry2.provider];
11602
12127
  if (!dispatch) {
11603
12128
  return { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` };
11604
12129
  }
@@ -11622,6 +12147,54 @@ async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
11622
12147
  });
11623
12148
  return result.ok ? { status: "ok" } : { status: "rejected", reason: result.reason };
11624
12149
  }
12150
+ function resolvePushEntry(entry2, env) {
12151
+ const dispatch = PUSH_PROVIDER_DISPATCH[entry2.provider];
12152
+ if (!dispatch) {
12153
+ return PROVIDER_DISPATCH[entry2.provider] ? {
12154
+ ok: false,
12155
+ outcome: {
12156
+ status: "not-push",
12157
+ reason: `provider "${entry2.provider}" is polled, not provisioned \u2014 there is no drain to manage`
12158
+ }
12159
+ } : { ok: false, outcome: { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` } };
12160
+ }
12161
+ const creds = resolveEntryCredentials(dispatch, entry2, env);
12162
+ if (!creds.ok) {
12163
+ const status2 = creds.kind === "unset-env" ? "unset-env" : creds.kind === "missing-field" ? "missing-field" : "failed";
12164
+ return { ok: false, outcome: { status: status2, reason: creds.reason } };
12165
+ }
12166
+ const options = entry2.options ?? {};
12167
+ const missingOpts = dispatch.requiredOptionFields.filter((k) => !(k in options));
12168
+ if (missingOpts.length > 0) {
12169
+ return {
12170
+ ok: false,
12171
+ outcome: { status: "missing-field", reason: `options missing required field(s): ${missingOpts.join(", ")}` }
12172
+ };
12173
+ }
12174
+ return { ok: true, dispatch, credentials: creds.credentials, options };
12175
+ }
12176
+ async function provisionConnector(entry2, env = process.env, fetchImpl) {
12177
+ const resolved = resolvePushEntry(entry2, env);
12178
+ if (!resolved.ok) return resolved.outcome;
12179
+ const result = await resolved.dispatch.provision({
12180
+ credentials: resolved.credentials,
12181
+ options: resolved.options,
12182
+ ...fetchImpl ? { fetchImpl } : {}
12183
+ });
12184
+ if (!result.ok) return { status: "failed", reason: result.reason };
12185
+ return { status: "ok", ...result.options ? { options: result.options } : {}, ...result.note ? { note: result.note } : {} };
12186
+ }
12187
+ async function deprovisionConnector(entry2, env = process.env, fetchImpl) {
12188
+ const resolved = resolvePushEntry(entry2, env);
12189
+ if (!resolved.ok) return resolved.outcome;
12190
+ const result = await resolved.dispatch.deprovision({
12191
+ credentials: resolved.credentials,
12192
+ options: resolved.options,
12193
+ ...fetchImpl ? { fetchImpl } : {}
12194
+ });
12195
+ if (!result.ok) return { status: "failed", reason: result.reason };
12196
+ return { status: "ok", ...result.note ? { note: result.note } : {} };
12197
+ }
11625
12198
 
11626
12199
  // src/daemon.ts
11627
12200
  init_auth();
@@ -11629,28 +12202,28 @@ init_auth();
11629
12202
  // src/unrouted.ts
11630
12203
  init_cjs_shims();
11631
12204
  var import_node_fs32 = require("fs");
11632
- var import_node_path51 = __toESM(require("path"), 1);
12205
+ var import_node_path52 = __toESM(require("path"), 1);
11633
12206
 
11634
12207
  // src/daemon.ts
11635
- var import_types47 = require("@neat.is/types");
12208
+ var import_types48 = require("@neat.is/types");
11636
12209
  function daemonJsonPath(scanPath) {
11637
- return import_node_path52.default.join(scanPath, "neat-out", "daemon.json");
12210
+ return import_node_path53.default.join(scanPath, "neat-out", "daemon.json");
11638
12211
  }
11639
12212
  function daemonsDiscoveryDir(home) {
11640
12213
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
11641
- return import_node_path52.default.join(base, "daemons");
12214
+ return import_node_path53.default.join(base, "daemons");
11642
12215
  }
11643
12216
  function daemonDiscoveryPath(project, home) {
11644
- return import_node_path52.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12217
+ return import_node_path53.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
11645
12218
  }
11646
12219
  function sanitizeDiscoveryName(project) {
11647
12220
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
11648
12221
  }
11649
12222
  function neatHomeFromEnv() {
11650
12223
  const env = process.env.NEAT_HOME;
11651
- if (env && env.length > 0) return import_node_path52.default.resolve(env);
12224
+ if (env && env.length > 0) return import_node_path53.default.resolve(env);
11652
12225
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11653
- return import_node_path52.default.join(home, ".neat");
12226
+ return import_node_path53.default.join(home, ".neat");
11654
12227
  }
11655
12228
  async function readDaemonRecord(scanPath) {
11656
12229
  try {
@@ -11721,7 +12294,7 @@ init_otel_grpc();
11721
12294
  // src/search.ts
11722
12295
  init_cjs_shims();
11723
12296
  var import_node_fs34 = require("fs");
11724
- var import_node_path53 = __toESM(require("path"), 1);
12297
+ var import_node_path54 = __toESM(require("path"), 1);
11725
12298
  var import_node_crypto4 = require("crypto");
11726
12299
  var DEFAULT_LIMIT = 10;
11727
12300
  var NOMIC_DIM = 768;
@@ -11884,7 +12457,7 @@ async function readCache(cachePath) {
11884
12457
  }
11885
12458
  }
11886
12459
  async function writeCache(cachePath, cache) {
11887
- await import_node_fs34.promises.mkdir(import_node_path53.default.dirname(cachePath), { recursive: true });
12460
+ await import_node_fs34.promises.mkdir(import_node_path54.default.dirname(cachePath), { recursive: true });
11888
12461
  await import_node_fs34.promises.writeFile(cachePath, JSON.stringify(cache));
11889
12462
  }
11890
12463
  var VectorIndex = class {
@@ -12042,8 +12615,8 @@ var ALL_PHASES = [
12042
12615
  ];
12043
12616
  function classifyChange(relPath) {
12044
12617
  const phases = /* @__PURE__ */ new Set();
12045
- const base = import_node_path54.default.basename(relPath).toLowerCase();
12046
- const segments = relPath.split(import_node_path54.default.sep).map((s) => s.toLowerCase());
12618
+ const base = import_node_path55.default.basename(relPath).toLowerCase();
12619
+ const segments = relPath.split(import_node_path55.default.sep).map((s) => s.toLowerCase());
12047
12620
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
12048
12621
  phases.add("services");
12049
12622
  phases.add("aliases");
@@ -12171,9 +12744,9 @@ function countWatchableDirs(scanPath, limit) {
12171
12744
  for (const e of entries) {
12172
12745
  if (count >= limit) return;
12173
12746
  if (!e.isDirectory()) continue;
12174
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path54.default.join(dir, e.name) + import_node_path54.default.sep))) continue;
12747
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path55.default.join(dir, e.name) + import_node_path55.default.sep))) continue;
12175
12748
  count++;
12176
- if (depth < 2) visit(import_node_path54.default.join(dir, e.name), depth + 1);
12749
+ if (depth < 2) visit(import_node_path55.default.join(dir, e.name), depth + 1);
12177
12750
  }
12178
12751
  };
12179
12752
  visit(scanPath, 0);
@@ -12191,8 +12764,8 @@ async function startWatch(graph, opts) {
12191
12764
  const projectName = opts.project ?? DEFAULT_PROJECT;
12192
12765
  await loadGraphFromDisk(graph, opts.outPath);
12193
12766
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
12194
- const policyFilePath = import_node_path54.default.join(opts.scanPath, "policy.json");
12195
- const policyViolationsPath = import_node_path54.default.join(import_node_path54.default.dirname(opts.outPath), "policy-violations.ndjson");
12767
+ const policyFilePath = import_node_path55.default.join(opts.scanPath, "policy.json");
12768
+ const policyViolationsPath = import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "policy-violations.ndjson");
12196
12769
  let policies = [];
12197
12770
  try {
12198
12771
  policies = await loadPolicyFile(policyFilePath);
@@ -12243,7 +12816,7 @@ async function startWatch(graph, opts) {
12243
12816
  assertBindAuthority(host, auth.authToken);
12244
12817
  const port = opts.port ?? 8080;
12245
12818
  const otelPort = opts.otelPort ?? 4318;
12246
- const cachePath = opts.embeddingsCachePath ?? import_node_path54.default.join(import_node_path54.default.dirname(opts.outPath), "embeddings.json");
12819
+ const cachePath = opts.embeddingsCachePath ?? import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "embeddings.json");
12247
12820
  let searchIndex;
12248
12821
  try {
12249
12822
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -12261,7 +12834,7 @@ async function startWatch(graph, opts) {
12261
12834
  // Paths are derived from the explicit options the watch caller passes
12262
12835
  // — pathsForProject is only used to fill in the embeddings/snapshot
12263
12836
  // fields so the registry shape is complete.
12264
- ...pathsForProject(projectName, import_node_path54.default.dirname(opts.outPath)),
12837
+ ...pathsForProject(projectName, import_node_path55.default.dirname(opts.outPath)),
12265
12838
  snapshotPath: opts.outPath,
12266
12839
  errorsPath: opts.errorsPath,
12267
12840
  staleEventsPath: opts.staleEventsPath
@@ -12379,9 +12952,9 @@ async function startWatch(graph, opts) {
12379
12952
  };
12380
12953
  const onPath = (absPath) => {
12381
12954
  if (shouldIgnore(absPath)) return;
12382
- const rel = import_node_path54.default.relative(opts.scanPath, absPath);
12955
+ const rel = import_node_path55.default.relative(opts.scanPath, absPath);
12383
12956
  if (!rel || rel.startsWith("..")) return;
12384
- pendingPaths.add(rel.split(import_node_path54.default.sep).join("/"));
12957
+ pendingPaths.add(rel.split(import_node_path55.default.sep).join("/"));
12385
12958
  const phases = classifyChange(rel);
12386
12959
  if (phases.size === 0) {
12387
12960
  for (const p of ALL_PHASES) pending.add(p);
@@ -12438,7 +13011,7 @@ async function startWatch(graph, opts) {
12438
13011
  // src/deploy/detect.ts
12439
13012
  init_cjs_shims();
12440
13013
  var import_node_fs36 = require("fs");
12441
- var import_node_path55 = __toESM(require("path"), 1);
13014
+ var import_node_path56 = __toESM(require("path"), 1);
12442
13015
  var import_node_child_process2 = require("child_process");
12443
13016
  var import_node_crypto5 = require("crypto");
12444
13017
  function generateToken() {
@@ -12538,7 +13111,7 @@ async function runDeploy(opts = {}) {
12538
13111
  const token = generateToken();
12539
13112
  switch (substrate) {
12540
13113
  case "docker-compose": {
12541
- const artifactPath = import_node_path55.default.join(cwd, "docker-compose.neat.yml");
13114
+ const artifactPath = import_node_path56.default.join(cwd, "docker-compose.neat.yml");
12542
13115
  const contents = emitDockerCompose(cwd);
12543
13116
  await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
12544
13117
  return {
@@ -12546,11 +13119,11 @@ async function runDeploy(opts = {}) {
12546
13119
  artifactPath,
12547
13120
  token,
12548
13121
  contents,
12549
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path55.default.basename(artifactPath)} up -d`
13122
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path56.default.basename(artifactPath)} up -d`
12550
13123
  };
12551
13124
  }
12552
13125
  case "systemd": {
12553
- const artifactPath = import_node_path55.default.join(cwd, "neat.service");
13126
+ const artifactPath = import_node_path56.default.join(cwd, "neat.service");
12554
13127
  const contents = emitSystemdUnit(cwd);
12555
13128
  await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
12556
13129
  return {
@@ -12586,7 +13159,7 @@ init_cjs_shims();
12586
13159
  // src/installers/javascript.ts
12587
13160
  init_cjs_shims();
12588
13161
  var import_node_fs37 = require("fs");
12589
- var import_node_path56 = __toESM(require("path"), 1);
13162
+ var import_node_path57 = __toESM(require("path"), 1);
12590
13163
  var import_semver2 = __toESM(require("semver"), 1);
12591
13164
 
12592
13165
  // src/installers/templates.ts
@@ -13217,11 +13790,11 @@ var OTEL_ENV = {
13217
13790
  value: "http://localhost:4318/projects/<project>/v1/traces"
13218
13791
  };
13219
13792
  function serviceNodeName(pkg, serviceDir) {
13220
- return pkg.name ?? import_node_path56.default.basename(serviceDir);
13793
+ return pkg.name ?? import_node_path57.default.basename(serviceDir);
13221
13794
  }
13222
13795
  function projectToken(pkg, serviceDir, project) {
13223
13796
  if (project && project.length > 0) return project;
13224
- return pkg.name ?? import_node_path56.default.basename(serviceDir);
13797
+ return pkg.name ?? import_node_path57.default.basename(serviceDir);
13225
13798
  }
13226
13799
  async function readJsonFile(p) {
13227
13800
  try {
@@ -13234,16 +13807,16 @@ async function readJsonFile(p) {
13234
13807
  async function detectRuntimeKind(pkgRoot, pkg) {
13235
13808
  const deps = allDeps(pkg);
13236
13809
  if ("react-native" in deps || "expo" in deps) return "react-native";
13237
- const appJson = await readJsonFile(import_node_path56.default.join(pkgRoot, "app.json"));
13810
+ const appJson = await readJsonFile(import_node_path57.default.join(pkgRoot, "app.json"));
13238
13811
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
13239
13812
  return "react-native";
13240
13813
  }
13241
- if (await exists3(import_node_path56.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path56.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path56.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
13814
+ if (await exists3(import_node_path57.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
13242
13815
  return "browser-bundle";
13243
13816
  }
13244
- if (await exists3(import_node_path56.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
13245
- if (await exists3(import_node_path56.default.join(pkgRoot, "bun.lockb"))) return "bun";
13246
- if (await exists3(import_node_path56.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path56.default.join(pkgRoot, "deno.lock"))) {
13817
+ if (await exists3(import_node_path57.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
13818
+ if (await exists3(import_node_path57.default.join(pkgRoot, "bun.lockb"))) return "bun";
13819
+ if (await exists3(import_node_path57.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path57.default.join(pkgRoot, "deno.lock"))) {
13247
13820
  return "deno";
13248
13821
  }
13249
13822
  const engines = pkg.engines ?? {};
@@ -13252,7 +13825,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
13252
13825
  }
13253
13826
  async function readPackageJson2(serviceDir) {
13254
13827
  try {
13255
- const raw = await import_node_fs37.promises.readFile(import_node_path56.default.join(serviceDir, "package.json"), "utf8");
13828
+ const raw = await import_node_fs37.promises.readFile(import_node_path57.default.join(serviceDir, "package.json"), "utf8");
13256
13829
  return JSON.parse(raw);
13257
13830
  } catch {
13258
13831
  return null;
@@ -13296,7 +13869,7 @@ function needsVersionUpgrade(installed, expected) {
13296
13869
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
13297
13870
  async function findNextConfig(serviceDir) {
13298
13871
  for (const name of NEXT_CONFIG_CANDIDATES) {
13299
- const candidate = import_node_path56.default.join(serviceDir, name);
13872
+ const candidate = import_node_path57.default.join(serviceDir, name);
13300
13873
  if (await exists3(candidate)) return candidate;
13301
13874
  }
13302
13875
  return null;
@@ -13365,7 +13938,7 @@ function hasRemixDependency(pkg) {
13365
13938
  }
13366
13939
  async function findRemixEntry(serviceDir) {
13367
13940
  for (const rel of REMIX_ENTRY_CANDIDATES) {
13368
- const candidate = import_node_path56.default.join(serviceDir, rel);
13941
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13369
13942
  if (await exists3(candidate)) return candidate;
13370
13943
  }
13371
13944
  return null;
@@ -13377,14 +13950,14 @@ function hasSvelteKitDependency(pkg) {
13377
13950
  }
13378
13951
  async function findSvelteKitHooks(serviceDir) {
13379
13952
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
13380
- const candidate = import_node_path56.default.join(serviceDir, rel);
13953
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13381
13954
  if (await exists3(candidate)) return candidate;
13382
13955
  }
13383
13956
  return null;
13384
13957
  }
13385
13958
  async function findSvelteKitConfig(serviceDir) {
13386
13959
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
13387
- const candidate = import_node_path56.default.join(serviceDir, rel);
13960
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13388
13961
  if (await exists3(candidate)) return candidate;
13389
13962
  }
13390
13963
  return null;
@@ -13395,7 +13968,7 @@ function hasNuxtDependency(pkg) {
13395
13968
  }
13396
13969
  async function findNuxtConfig(serviceDir) {
13397
13970
  for (const name of NUXT_CONFIG_CANDIDATES) {
13398
- const candidate = import_node_path56.default.join(serviceDir, name);
13971
+ const candidate = import_node_path57.default.join(serviceDir, name);
13399
13972
  if (await exists3(candidate)) return candidate;
13400
13973
  }
13401
13974
  return null;
@@ -13406,7 +13979,7 @@ function hasAstroDependency(pkg) {
13406
13979
  }
13407
13980
  async function findAstroConfig(serviceDir) {
13408
13981
  for (const name of ASTRO_CONFIG_CANDIDATES) {
13409
- const candidate = import_node_path56.default.join(serviceDir, name);
13982
+ const candidate = import_node_path57.default.join(serviceDir, name);
13410
13983
  if (await exists3(candidate)) return candidate;
13411
13984
  }
13412
13985
  return null;
@@ -13420,7 +13993,7 @@ function parseNextMajor(range) {
13420
13993
  return Number.isFinite(n) ? n : null;
13421
13994
  }
13422
13995
  async function isTypeScriptProject(serviceDir) {
13423
- return exists3(import_node_path56.default.join(serviceDir, "tsconfig.json"));
13996
+ return exists3(import_node_path57.default.join(serviceDir, "tsconfig.json"));
13424
13997
  }
13425
13998
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
13426
13999
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -13469,7 +14042,7 @@ function entryFromScript(script) {
13469
14042
  }
13470
14043
  async function resolveEntry(serviceDir, pkg) {
13471
14044
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
13472
- const candidate = import_node_path56.default.resolve(serviceDir, pkg.main);
14045
+ const candidate = import_node_path57.default.resolve(serviceDir, pkg.main);
13473
14046
  if (await exists3(candidate)) return candidate;
13474
14047
  }
13475
14048
  if (pkg.bin) {
@@ -13483,40 +14056,40 @@ async function resolveEntry(serviceDir, pkg) {
13483
14056
  if (typeof first === "string") binEntry = first;
13484
14057
  }
13485
14058
  if (binEntry) {
13486
- const candidate = import_node_path56.default.resolve(serviceDir, binEntry);
14059
+ const candidate = import_node_path57.default.resolve(serviceDir, binEntry);
13487
14060
  if (await exists3(candidate)) return candidate;
13488
14061
  }
13489
14062
  }
13490
14063
  const startEntry = entryFromScript(pkg.scripts?.start);
13491
14064
  if (startEntry) {
13492
- const candidate = import_node_path56.default.resolve(serviceDir, startEntry);
14065
+ const candidate = import_node_path57.default.resolve(serviceDir, startEntry);
13493
14066
  if (await exists3(candidate)) return candidate;
13494
14067
  }
13495
14068
  const devEntry = entryFromScript(pkg.scripts?.dev);
13496
14069
  if (devEntry) {
13497
- const candidate = import_node_path56.default.resolve(serviceDir, devEntry);
14070
+ const candidate = import_node_path57.default.resolve(serviceDir, devEntry);
13498
14071
  if (await exists3(candidate)) return candidate;
13499
14072
  }
13500
14073
  for (const rel of SRC_INDEX_CANDIDATES) {
13501
- const candidate = import_node_path56.default.join(serviceDir, rel);
14074
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13502
14075
  if (await exists3(candidate)) return candidate;
13503
14076
  }
13504
14077
  for (const rel of SRC_NAMED_CANDIDATES) {
13505
- const candidate = import_node_path56.default.join(serviceDir, rel);
14078
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13506
14079
  if (await exists3(candidate)) return candidate;
13507
14080
  }
13508
14081
  for (const rel of ROOT_NAMED_CANDIDATES) {
13509
- const candidate = import_node_path56.default.join(serviceDir, rel);
14082
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13510
14083
  if (await exists3(candidate)) return candidate;
13511
14084
  }
13512
14085
  for (const name of INDEX_CANDIDATES) {
13513
- const candidate = import_node_path56.default.join(serviceDir, name);
14086
+ const candidate = import_node_path57.default.join(serviceDir, name);
13514
14087
  if (await exists3(candidate)) return candidate;
13515
14088
  }
13516
14089
  return null;
13517
14090
  }
13518
14091
  function dispatchEntry(entryFile, pkg) {
13519
- const ext = import_node_path56.default.extname(entryFile).toLowerCase();
14092
+ const ext = import_node_path57.default.extname(entryFile).toLowerCase();
13520
14093
  if (ext === ".ts" || ext === ".tsx") return "ts";
13521
14094
  if (ext === ".mjs") return "esm";
13522
14095
  if (ext === ".cjs") return "cjs";
@@ -13533,9 +14106,9 @@ function otelInitContents(flavor) {
13533
14106
  return OTEL_INIT_CJS;
13534
14107
  }
13535
14108
  function injectionLine(flavor, entryFile, otelInitFile) {
13536
- let rel = import_node_path56.default.relative(import_node_path56.default.dirname(entryFile), otelInitFile);
14109
+ let rel = import_node_path57.default.relative(import_node_path57.default.dirname(entryFile), otelInitFile);
13537
14110
  if (!rel.startsWith(".")) rel = `./${rel}`;
13538
- rel = rel.split(import_node_path56.default.sep).join("/");
14111
+ rel = rel.split(import_node_path57.default.sep).join("/");
13539
14112
  if (flavor === "cjs") return `require('${rel}')`;
13540
14113
  if (flavor === "esm") return `import '${rel}'`;
13541
14114
  const tsRel = rel.replace(/\.ts$/, "");
@@ -13548,27 +14121,27 @@ function lineIsOtelInjection(line) {
13548
14121
  }
13549
14122
  async function detectsSrcLayout(serviceDir) {
13550
14123
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
13551
- exists3(import_node_path56.default.join(serviceDir, "src", "app")),
13552
- exists3(import_node_path56.default.join(serviceDir, "src", "pages")),
13553
- exists3(import_node_path56.default.join(serviceDir, "app")),
13554
- exists3(import_node_path56.default.join(serviceDir, "pages"))
14124
+ exists3(import_node_path57.default.join(serviceDir, "src", "app")),
14125
+ exists3(import_node_path57.default.join(serviceDir, "src", "pages")),
14126
+ exists3(import_node_path57.default.join(serviceDir, "app")),
14127
+ exists3(import_node_path57.default.join(serviceDir, "pages"))
13555
14128
  ]);
13556
14129
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
13557
14130
  }
13558
14131
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
13559
14132
  const useTs = await isTypeScriptProject(serviceDir);
13560
14133
  const srcLayout = await detectsSrcLayout(serviceDir);
13561
- const baseDir = srcLayout ? import_node_path56.default.join(serviceDir, "src") : serviceDir;
13562
- const instrumentationFile = import_node_path56.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
13563
- const instrumentationNodeFile = import_node_path56.default.join(
14134
+ const baseDir = srcLayout ? import_node_path57.default.join(serviceDir, "src") : serviceDir;
14135
+ const instrumentationFile = import_node_path57.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
14136
+ const instrumentationNodeFile = import_node_path57.default.join(
13564
14137
  baseDir,
13565
14138
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
13566
14139
  );
13567
- const instrumentationEdgeFile = import_node_path56.default.join(
14140
+ const instrumentationEdgeFile = import_node_path57.default.join(
13568
14141
  baseDir,
13569
14142
  useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
13570
14143
  );
13571
- const envNeatFile = import_node_path56.default.join(baseDir, ".env.neat");
14144
+ const envNeatFile = import_node_path57.default.join(baseDir, ".env.neat");
13572
14145
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
13573
14146
  const dependencyEdits = [];
13574
14147
  for (const sdk of SDK_PACKAGES) {
@@ -13693,7 +14266,7 @@ function buildDependencyEdits(pkg, manifestPath) {
13693
14266
  return edits;
13694
14267
  }
13695
14268
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
13696
- const envNeatFile = import_node_path56.default.join(serviceDir, ".env.neat");
14269
+ const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
13697
14270
  if (!await exists3(envNeatFile)) {
13698
14271
  generatedFiles.push({
13699
14272
  file: envNeatFile,
@@ -13728,7 +14301,7 @@ function fileImportsOtelHook(raw, specifiers) {
13728
14301
  }
13729
14302
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
13730
14303
  const useTs = await isTypeScriptProject(serviceDir);
13731
- const otelServerFile = import_node_path56.default.join(
14304
+ const otelServerFile = import_node_path57.default.join(
13732
14305
  serviceDir,
13733
14306
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
13734
14307
  );
@@ -13785,11 +14358,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
13785
14358
  }
13786
14359
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
13787
14360
  const useTs = await isTypeScriptProject(serviceDir);
13788
- const otelInitFile = import_node_path56.default.join(
14361
+ const otelInitFile = import_node_path57.default.join(
13789
14362
  serviceDir,
13790
14363
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
13791
14364
  );
13792
- const resolvedHooksFile = hooksFile ?? import_node_path56.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
14365
+ const resolvedHooksFile = hooksFile ?? import_node_path57.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
13793
14366
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
13794
14367
  const generatedFiles = [];
13795
14368
  const entrypointEdits = [];
@@ -13851,11 +14424,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
13851
14424
  }
13852
14425
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
13853
14426
  const useTs = await isTypeScriptProject(serviceDir);
13854
- const otelPluginFile = import_node_path56.default.join(
14427
+ const otelPluginFile = import_node_path57.default.join(
13855
14428
  serviceDir,
13856
14429
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
13857
14430
  );
13858
- const otelInitFile = import_node_path56.default.join(
14431
+ const otelInitFile = import_node_path57.default.join(
13859
14432
  serviceDir,
13860
14433
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
13861
14434
  );
@@ -13906,19 +14479,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
13906
14479
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
13907
14480
  async function findAstroMiddleware(serviceDir) {
13908
14481
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
13909
- const candidate = import_node_path56.default.join(serviceDir, rel);
14482
+ const candidate = import_node_path57.default.join(serviceDir, rel);
13910
14483
  if (await exists3(candidate)) return candidate;
13911
14484
  }
13912
14485
  return null;
13913
14486
  }
13914
14487
  async function planAstro(serviceDir, pkg, manifestPath, project) {
13915
14488
  const useTs = await isTypeScriptProject(serviceDir);
13916
- const otelInitFile = import_node_path56.default.join(
14489
+ const otelInitFile = import_node_path57.default.join(
13917
14490
  serviceDir,
13918
14491
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
13919
14492
  );
13920
14493
  const existingMiddleware = await findAstroMiddleware(serviceDir);
13921
- const middlewareFile = existingMiddleware ?? import_node_path56.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
14494
+ const middlewareFile = existingMiddleware ?? import_node_path57.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
13922
14495
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
13923
14496
  const generatedFiles = [];
13924
14497
  const entrypointEdits = [];
@@ -14014,7 +14587,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
14014
14587
  }
14015
14588
  async function plan(serviceDir, opts) {
14016
14589
  const pkg = await readPackageJson2(serviceDir);
14017
- const manifestPath = import_node_path56.default.join(serviceDir, "package.json");
14590
+ const manifestPath = import_node_path57.default.join(serviceDir, "package.json");
14018
14591
  const project = opts?.project;
14019
14592
  const empty = {
14020
14593
  language: "javascript",
@@ -14049,8 +14622,8 @@ async function plan(serviceDir, opts) {
14049
14622
  return { ...empty, libOnly: true };
14050
14623
  }
14051
14624
  const flavor = dispatchEntry(entryFile, pkg);
14052
- const otelInitFile = import_node_path56.default.join(import_node_path56.default.dirname(entryFile), otelInitFilename(flavor));
14053
- const envNeatFile = import_node_path56.default.join(serviceDir, ".env.neat");
14625
+ const otelInitFile = import_node_path57.default.join(import_node_path57.default.dirname(entryFile), otelInitFilename(flavor));
14626
+ const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
14054
14627
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
14055
14628
  const dependencyEdits = [];
14056
14629
  for (const sdk of SDK_PACKAGES) {
@@ -14119,13 +14692,13 @@ async function plan(serviceDir, opts) {
14119
14692
  };
14120
14693
  }
14121
14694
  function isAllowedWritePath(serviceDir, target) {
14122
- const rel = import_node_path56.default.relative(serviceDir, target);
14695
+ const rel = import_node_path57.default.relative(serviceDir, target);
14123
14696
  if (rel.startsWith("..")) return false;
14124
- const base = import_node_path56.default.basename(target);
14697
+ const base = import_node_path57.default.basename(target);
14125
14698
  if (base === "package.json") return true;
14126
14699
  if (base === ".env.neat") return true;
14127
14700
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
14128
- const relPosix = rel.split(import_node_path56.default.sep).join("/");
14701
+ const relPosix = rel.split(import_node_path57.default.sep).join("/");
14129
14702
  if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
14130
14703
  if (relPosix === base) return true;
14131
14704
  if (relPosix === `src/${base}`) return true;
@@ -14142,7 +14715,7 @@ function isAllowedWritePath(serviceDir, target) {
14142
14715
  return false;
14143
14716
  }
14144
14717
  async function writeAtomic(file, contents) {
14145
- await import_node_fs37.promises.mkdir(import_node_path56.default.dirname(file), { recursive: true });
14718
+ await import_node_fs37.promises.mkdir(import_node_path57.default.dirname(file), { recursive: true });
14146
14719
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
14147
14720
  await import_node_fs37.promises.writeFile(tmp, contents, "utf8");
14148
14721
  await import_node_fs37.promises.rename(tmp, file);
@@ -14326,7 +14899,7 @@ async function rollback(installPlan, originals, createdFiles) {
14326
14899
  ...removed.map((f) => `removed: ${f}`),
14327
14900
  ""
14328
14901
  ];
14329
- const rollbackPath = import_node_path56.default.join(installPlan.serviceDir, "neat-rollback.patch");
14902
+ const rollbackPath = import_node_path57.default.join(installPlan.serviceDir, "neat-rollback.patch");
14330
14903
  await import_node_fs37.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
14331
14904
  }
14332
14905
  function injectInstrumentationHook(raw) {
@@ -14357,7 +14930,7 @@ var javascriptInstaller = {
14357
14930
  // src/installers/python.ts
14358
14931
  init_cjs_shims();
14359
14932
  var import_node_fs38 = require("fs");
14360
- var import_node_path57 = __toESM(require("path"), 1);
14933
+ var import_node_path58 = __toESM(require("path"), 1);
14361
14934
  var SDK_PACKAGES2 = [
14362
14935
  { name: "opentelemetry-distro", version: ">=0.49b0" },
14363
14936
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -14378,7 +14951,7 @@ async function exists4(p) {
14378
14951
  async function detect2(serviceDir) {
14379
14952
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
14380
14953
  for (const m of markers) {
14381
- if (await exists4(import_node_path57.default.join(serviceDir, m))) return true;
14954
+ if (await exists4(import_node_path58.default.join(serviceDir, m))) return true;
14382
14955
  }
14383
14956
  return false;
14384
14957
  }
@@ -14388,7 +14961,7 @@ function reqPackageName(line) {
14388
14961
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
14389
14962
  }
14390
14963
  async function planRequirementsTxtEdits(serviceDir) {
14391
- const file = import_node_path57.default.join(serviceDir, "requirements.txt");
14964
+ const file = import_node_path58.default.join(serviceDir, "requirements.txt");
14392
14965
  if (!await exists4(file)) return null;
14393
14966
  const raw = await import_node_fs38.promises.readFile(file, "utf8");
14394
14967
  const presentNames = new Set(
@@ -14398,7 +14971,7 @@ async function planRequirementsTxtEdits(serviceDir) {
14398
14971
  return { manifest: file, missing: [...missing] };
14399
14972
  }
14400
14973
  async function planProcfileEdits(serviceDir) {
14401
- const procfile = import_node_path57.default.join(serviceDir, "Procfile");
14974
+ const procfile = import_node_path58.default.join(serviceDir, "Procfile");
14402
14975
  if (!await exists4(procfile)) return [];
14403
14976
  const raw = await import_node_fs38.promises.readFile(procfile, "utf8");
14404
14977
  const edits = [];
@@ -14487,7 +15060,7 @@ async function apply2(installPlan) {
14487
15060
  if (raw === void 0) {
14488
15061
  throw new Error(`python installer: cannot read ${file} during apply`);
14489
15062
  }
14490
- const base = import_node_path57.default.basename(file);
15063
+ const base = import_node_path58.default.basename(file);
14491
15064
  if (base === "requirements.txt") {
14492
15065
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
14493
15066
  if (edits.length > 0) {
@@ -14526,7 +15099,7 @@ async function rollback2(installPlan, originals) {
14526
15099
  ...restored.map((f) => `restored: ${f}`),
14527
15100
  ""
14528
15101
  ];
14529
- const rollbackPath = import_node_path57.default.join(installPlan.serviceDir, "neat-rollback.patch");
15102
+ const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
14530
15103
  await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
14531
15104
  }
14532
15105
  var pythonInstaller = {
@@ -14653,7 +15226,7 @@ init_cjs_shims();
14653
15226
  var import_node_fs39 = require("fs");
14654
15227
  var import_node_http = __toESM(require("http"), 1);
14655
15228
  var import_node_net = __toESM(require("net"), 1);
14656
- var import_node_path58 = __toESM(require("path"), 1);
15229
+ var import_node_path59 = __toESM(require("path"), 1);
14657
15230
  var import_node_url4 = require("url");
14658
15231
  var import_node_child_process3 = require("child_process");
14659
15232
  var import_node_readline = __toESM(require("readline"), 1);
@@ -14663,7 +15236,7 @@ async function extractAndPersist(opts) {
14663
15236
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
14664
15237
  resetGraph(graphKey);
14665
15238
  const graph = getGraph(graphKey);
14666
- const projectPaths = pathsForProject(graphKey, import_node_path58.default.join(opts.scanPath, "neat-out"));
15239
+ const projectPaths = pathsForProject(graphKey, import_node_path59.default.join(opts.scanPath, "neat-out"));
14667
15240
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
14668
15241
  errorsPath: projectPaths.errorsPath
14669
15242
  });
@@ -14715,7 +15288,7 @@ async function applyInstallersOver(services, project, options = {}) {
14715
15288
  libOnly++;
14716
15289
  const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
14717
15290
  if (appDeps.length > 0) {
14718
- const svcName = import_node_path58.default.basename(svc.dir);
15291
+ const svcName = import_node_path59.default.basename(svc.dir);
14719
15292
  const list = appDeps.join(", ");
14720
15293
  console.warn(
14721
15294
  `neat: runtime layer won't engage for ${svcName}: no entry point found.
@@ -14728,7 +15301,7 @@ async function applyInstallersOver(services, project, options = {}) {
14728
15301
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
14729
15302
  } else if (outcome.outcome === "react-native") {
14730
15303
  reactNative++;
14731
- const svcName = import_node_path58.default.basename(svc.dir);
15304
+ const svcName = import_node_path59.default.basename(svc.dir);
14732
15305
  console.log(
14733
15306
  `neat: ${svc.dir} detected as React Native / Expo
14734
15307
  The installer doesn't cover this runtime deterministically.
@@ -14739,7 +15312,7 @@ async function applyInstallersOver(services, project, options = {}) {
14739
15312
  );
14740
15313
  } else if (outcome.outcome === "bun") {
14741
15314
  bun++;
14742
- const svcName = import_node_path58.default.basename(svc.dir);
15315
+ const svcName = import_node_path59.default.basename(svc.dir);
14743
15316
  console.log(
14744
15317
  `neat: ${svc.dir} detected as Bun
14745
15318
  The installer doesn't cover this runtime deterministically.
@@ -14750,7 +15323,7 @@ async function applyInstallersOver(services, project, options = {}) {
14750
15323
  );
14751
15324
  } else if (outcome.outcome === "deno") {
14752
15325
  deno++;
14753
- const svcName = import_node_path58.default.basename(svc.dir);
15326
+ const svcName = import_node_path59.default.basename(svc.dir);
14754
15327
  console.log(
14755
15328
  `neat: ${svc.dir} detected as Deno
14756
15329
  The installer doesn't cover this runtime deterministically.
@@ -14761,7 +15334,7 @@ async function applyInstallersOver(services, project, options = {}) {
14761
15334
  );
14762
15335
  } else if (outcome.outcome === "cloudflare-workers") {
14763
15336
  cloudflareWorkers++;
14764
- const svcName = import_node_path58.default.basename(svc.dir);
15337
+ const svcName = import_node_path59.default.basename(svc.dir);
14765
15338
  console.log(
14766
15339
  `neat: ${svc.dir} detected as Cloudflare Workers
14767
15340
  The installer doesn't cover this runtime deterministically.
@@ -14772,7 +15345,7 @@ async function applyInstallersOver(services, project, options = {}) {
14772
15345
  );
14773
15346
  } else if (outcome.outcome === "electron") {
14774
15347
  electron++;
14775
- const svcName = import_node_path58.default.basename(svc.dir);
15348
+ const svcName = import_node_path59.default.basename(svc.dir);
14776
15349
  console.log(
14777
15350
  `neat: ${svc.dir} detected as Electron
14778
15351
  The installer doesn't cover this runtime deterministically.
@@ -14785,7 +15358,7 @@ async function applyInstallersOver(services, project, options = {}) {
14785
15358
  if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
14786
15359
  const gaps = uninstrumentedLibraries(svc.pkg);
14787
15360
  if (gaps.length > 0) {
14788
- const svcName = import_node_path58.default.basename(svc.dir);
15361
+ const svcName = import_node_path59.default.basename(svc.dir);
14789
15362
  const list = gaps.join(", ");
14790
15363
  const subject = gaps.length === 1 ? "this library" : "these libraries";
14791
15364
  const aux = gaps.length === 1 ? "isn't" : "aren't";
@@ -14991,8 +15564,8 @@ async function persistedPortsFor(scanPath) {
14991
15564
  return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
14992
15565
  }
14993
15566
  async function acquireSpawnLock(scanPath) {
14994
- const lockPath = import_node_path58.default.join(scanPath, "neat-out", "daemon.spawn.lock");
14995
- await import_node_fs39.promises.mkdir(import_node_path58.default.dirname(lockPath), { recursive: true });
15567
+ const lockPath = import_node_path59.default.join(scanPath, "neat-out", "daemon.spawn.lock");
15568
+ await import_node_fs39.promises.mkdir(import_node_path59.default.dirname(lockPath), { recursive: true });
14996
15569
  const STALE_LOCK_MS = 6e4;
14997
15570
  try {
14998
15571
  const fd = await import_node_fs39.promises.open(lockPath, "wx");
@@ -15037,13 +15610,13 @@ async function healthIsForProject(restPort, project) {
15037
15610
  return false;
15038
15611
  }
15039
15612
  function daemonLogPath(projectPath2) {
15040
- return import_node_path58.default.join(projectPath2, "neat-out", "daemon.log");
15613
+ return import_node_path59.default.join(projectPath2, "neat-out", "daemon.log");
15041
15614
  }
15042
15615
  function spawnDaemonDetached(spec) {
15043
- const here = import_node_path58.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
15616
+ const here = import_node_path59.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
15044
15617
  const candidates = [
15045
- import_node_path58.default.join(here, "neatd.cjs"),
15046
- import_node_path58.default.join(here, "neatd.js")
15618
+ import_node_path59.default.join(here, "neatd.cjs"),
15619
+ import_node_path59.default.join(here, "neatd.js")
15047
15620
  ];
15048
15621
  let entry2 = null;
15049
15622
  const fsSync = require("fs");
@@ -15073,7 +15646,7 @@ function spawnDaemonDetached(spec) {
15073
15646
  let logFd = null;
15074
15647
  if (spec) {
15075
15648
  const logPath = daemonLogPath(spec.projectPath);
15076
- fsSync.mkdirSync(import_node_path58.default.dirname(logPath), { recursive: true });
15649
+ fsSync.mkdirSync(import_node_path59.default.dirname(logPath), { recursive: true });
15077
15650
  logFd = fsSync.openSync(logPath, "a");
15078
15651
  }
15079
15652
  const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
@@ -15272,7 +15845,7 @@ async function runOrchestrator(opts) {
15272
15845
  result.steps.browser = openBrowser(dashboardUrl);
15273
15846
  }
15274
15847
  const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
15275
- const daemonLog = daemonRunning ? import_node_path58.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
15848
+ const daemonLog = daemonRunning ? import_node_path59.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
15276
15849
  printSummary(result, graph, dashboardUrl, daemonLog);
15277
15850
  return result;
15278
15851
  }
@@ -15352,6 +15925,8 @@ function parseConnectorArgs(args) {
15352
15925
  positional: [],
15353
15926
  skipValidate: false,
15354
15927
  plaintext: false,
15928
+ help: false,
15929
+ json: false,
15355
15930
  fields: {}
15356
15931
  };
15357
15932
  const positional = [];
@@ -15365,6 +15940,14 @@ function parseConnectorArgs(args) {
15365
15940
  out.plaintext = true;
15366
15941
  continue;
15367
15942
  }
15943
+ if (arg === "--help" || arg === "-h") {
15944
+ out.help = true;
15945
+ continue;
15946
+ }
15947
+ if (arg === "--json") {
15948
+ out.json = true;
15949
+ continue;
15950
+ }
15368
15951
  if (arg.startsWith("--")) {
15369
15952
  let flag = arg;
15370
15953
  let value;
@@ -15441,19 +16024,20 @@ function plaintextFields(ref) {
15441
16024
  async function connectorAdd(args, deps) {
15442
16025
  let provider = args.positional[0];
15443
16026
  if (!provider && deps.interactive) {
15444
- provider = (await deps.prompt(`Provider (${Object.keys(PROVIDER_DISPATCH).sort().join(", ")}):`)).trim();
16027
+ provider = (await deps.prompt(`Provider (${knownProviderNames().join(", ")}):`)).trim();
15445
16028
  }
15446
16029
  if (!provider) {
15447
16030
  deps.err("neat connector add: a provider is required (e.g. `neat connector add supabase`)");
15448
16031
  return 2;
15449
16032
  }
15450
- const dispatch = getProviderDispatch(provider);
16033
+ const dispatch = getProviderFieldSchema(provider);
15451
16034
  if (!dispatch) {
15452
16035
  deps.err(
15453
- `neat connector add: unknown provider "${provider}". known providers: ${Object.keys(PROVIDER_DISPATCH).sort().join(", ")}`
16036
+ `neat connector add: unknown provider "${provider}". known providers: ${knownProviderNames().join(", ")}`
15454
16037
  );
15455
16038
  return 2;
15456
16039
  }
16040
+ const push = isPushProvider(provider);
15457
16041
  let project = args.project;
15458
16042
  if (project === void 0 && deps.interactive) {
15459
16043
  const answer = (await deps.prompt("Project (blank = the project the daemon is bootstrapping):")).trim();
@@ -15492,7 +16076,7 @@ async function connectorAdd(args, deps) {
15492
16076
  credential,
15493
16077
  ...Object.keys(options).length > 0 ? { options } : {}
15494
16078
  };
15495
- if (!args.skipValidate) {
16079
+ if (!push && !args.skipValidate) {
15496
16080
  const outcome = await validateConnectorEntry(entry2, deps.env, deps.fetchImpl);
15497
16081
  const code = reportPreWriteValidation(outcome, deps);
15498
16082
  if (code !== 0) return code;
@@ -15503,14 +16087,32 @@ async function connectorAdd(args, deps) {
15503
16087
  `neat connector add: storing a literal secret at rest for ${plaintext.join(", ")} \u2014 prefer an env-var reference like \`$PROVIDER_TOKEN\` (pass --plaintext to silence this).`
15504
16088
  );
15505
16089
  }
16090
+ if (push) {
16091
+ if (args.skipValidate) {
16092
+ deps.out("note: --skip-validate has no effect for a push provider \u2014 provisioning the drain is the add.");
16093
+ }
16094
+ const outcome = await provisionConnector(entry2, deps.env, deps.fetchImpl);
16095
+ if (outcome.status !== "ok") {
16096
+ deps.err(
16097
+ `neat connector add: could not provision the ${provider} connector \u2014 ${outcome.reason}. Nothing was written.`
16098
+ );
16099
+ return 1;
16100
+ }
16101
+ if (outcome.options) entry2.options = { ...entry2.options ?? {}, ...outcome.options };
16102
+ if (outcome.note) deps.out(`note: ${outcome.note}`);
16103
+ }
15506
16104
  const { replaced } = await upsertConnectorEntry(entry2, deps.home);
15507
16105
  const verb = replaced ? "updated" : "added";
15508
16106
  const where = project ? `project "${project}"` : "the bootstrapping project";
15509
16107
  deps.out(`${verb} connector "${id}" (${provider}) for ${where}.`);
15510
- if (args.skipValidate) {
15511
- deps.out("skipped validation (--skip-validate) \u2014 the credential is checked at the next daemon poll.");
16108
+ if (push) {
16109
+ deps.out("The drain is live \u2014 traces arrive at the daemon's OTLP receiver. No poll or restart needed.");
16110
+ } else {
16111
+ if (args.skipValidate) {
16112
+ deps.out("skipped validation (--skip-validate) \u2014 the credential is checked at the next daemon poll.");
16113
+ }
16114
+ deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
15512
16115
  }
15513
- deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
15514
16116
  return 0;
15515
16117
  }
15516
16118
  function reportPreWriteValidation(outcome, deps) {
@@ -15536,10 +16138,26 @@ function reportPreWriteValidation(outcome, deps) {
15536
16138
  return 2;
15537
16139
  }
15538
16140
  }
15539
- async function connectorList(deps, filterProject) {
16141
+ async function connectorList(deps, filterProject, json = false) {
15540
16142
  const config = await readConnectorsConfig(deps.home);
15541
16143
  let entries = config.connectors;
15542
16144
  if (filterProject) entries = entries.filter((e) => e.project === filterProject);
16145
+ if (json) {
16146
+ const rows = entries.map((e) => ({
16147
+ id: e.id,
16148
+ provider: e.provider,
16149
+ project: e.project ?? null,
16150
+ kind: isPushProvider(e.provider) ? "push" : "pull",
16151
+ credential: describeCredential(e.credential, deps.env).map((c) => ({
16152
+ ...c.field ? { field: c.field } : {},
16153
+ ref: c.display,
16154
+ ...c.status ? { status: c.status } : { kind: c.kind }
16155
+ })),
16156
+ ...e.options ? { options: e.options } : {}
16157
+ }));
16158
+ deps.out(JSON.stringify(rows, null, 2));
16159
+ return 0;
16160
+ }
15543
16161
  if (entries.length === 0) {
15544
16162
  deps.out(
15545
16163
  filterProject ? `no connectors configured for project "${filterProject}".` : "no connectors configured. run `neat connector add <provider>` to add one."
@@ -15562,6 +16180,22 @@ async function connectorRemove(deps, id) {
15562
16180
  deps.err("neat connector remove: missing <id>. run `neat connector list` to see configured ids.");
15563
16181
  return 2;
15564
16182
  }
16183
+ const config = await readConnectorsConfig(deps.home);
16184
+ const entry2 = config.connectors.find((c) => c.id === id);
16185
+ if (!entry2) {
16186
+ deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
16187
+ return 1;
16188
+ }
16189
+ if (isPushProvider(entry2.provider)) {
16190
+ const outcome = await deprovisionConnector(entry2, deps.env, deps.fetchImpl);
16191
+ if (outcome.status !== "ok") {
16192
+ deps.err(
16193
+ `neat connector remove: could not delete the ${entry2.provider} drain \u2014 ${outcome.reason}. The entry was kept so you can retry (or delete the drain in the Vercel dashboard, then re-run).`
16194
+ );
16195
+ return 1;
16196
+ }
16197
+ if (outcome.note) deps.out(`note: ${outcome.note}`);
16198
+ }
15565
16199
  const removed = await removeConnectorEntry(id, deps.home);
15566
16200
  if (!removed) {
15567
16201
  deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
@@ -15602,13 +16236,20 @@ async function connectorTest(deps, id) {
15602
16236
  }
15603
16237
  function printConnectorUsage(write) {
15604
16238
  write("usage: neat connector <add|list|remove|test> [args]");
16239
+ write(` providers: ${knownProviderNames().join(", ")}`);
16240
+ write(" pull (polled): supabase, railway, firebase, cloudflare");
16241
+ write(" push (drains): vercel \u2014 provisions a Vercel trace Drain that forwards");
16242
+ write(" traces to the daemon's OTLP receiver; no app instrumentation");
15605
16243
  write(" add <provider> add a connector; validates the credential first (--skip-validate to skip)");
15606
16244
  write(" flags: --project <name> --credential/--token <$VAR|value> --id <id>");
15607
16245
  write(" --<option> <value> (provider-specific) --plaintext --skip-validate");
16246
+ write(" vercel: neat connector add vercel --token $VERCEL_TOKEN \\");
16247
+ write(" --otel-token $NEAT_OTEL_TOKEN --team-id <teamId> \\");
16248
+ write(" --endpoint https://<public-host>/v1/traces [--project-ids <id,id>]");
15608
16249
  write(" list list configured connectors (credentials shown redacted)");
15609
16250
  write(" flags: --project <name>");
15610
- write(" remove <id> remove a connector by id");
15611
- write(" test <id> re-run the credential validation for an existing connector");
16251
+ write(" remove <id> remove a connector by id (a push provider also has its drain deleted)");
16252
+ write(" test <id> re-run validation for an existing connector (a push provider re-tests its drain)");
15612
16253
  }
15613
16254
  async function runConnectorCommand(rawArgs, deps = {}) {
15614
16255
  const resolved = resolveDeps(deps);
@@ -15618,11 +16259,15 @@ async function runConnectorCommand(rawArgs, deps = {}) {
15618
16259
  return 2;
15619
16260
  }
15620
16261
  const a = parsed.value;
16262
+ if (a.help) {
16263
+ printConnectorUsage(resolved.out);
16264
+ return 0;
16265
+ }
15621
16266
  switch (a.subcommand) {
15622
16267
  case "add":
15623
16268
  return connectorAdd(a, resolved);
15624
16269
  case "list":
15625
- return connectorList(resolved, a.project);
16270
+ return connectorList(resolved, a.project, a.json);
15626
16271
  case "remove":
15627
16272
  return connectorRemove(resolved, a.positional[0]);
15628
16273
  case "test":
@@ -15640,7 +16285,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
15640
16285
 
15641
16286
  // src/hooks-cli.ts
15642
16287
  init_cjs_shims();
15643
- var import_node_path59 = __toESM(require("path"), 1);
16288
+ var import_node_path60 = __toESM(require("path"), 1);
15644
16289
  var import_node_os5 = __toESM(require("os"), 1);
15645
16290
  var import_node_fs40 = require("fs");
15646
16291
  var import_node_url5 = require("url");
@@ -15649,14 +16294,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
15649
16294
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
15650
16295
  var HOOK_MATCHER = "Grep|Glob|Bash";
15651
16296
  function moduleDir() {
15652
- return typeof __dirname !== "undefined" ? __dirname : import_node_path59.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
16297
+ return typeof __dirname !== "undefined" ? __dirname : import_node_path60.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
15653
16298
  }
15654
16299
  async function readSkillAsset(rel) {
15655
16300
  const here = moduleDir();
15656
16301
  const candidates = [
15657
- import_node_path59.default.resolve(here, "../../claude-skill", rel),
15658
- import_node_path59.default.resolve(here, "../../../claude-skill", rel),
15659
- import_node_path59.default.resolve(here, "../claude-skill", rel)
16302
+ import_node_path60.default.resolve(here, "../../claude-skill", rel),
16303
+ import_node_path60.default.resolve(here, "../../../claude-skill", rel),
16304
+ import_node_path60.default.resolve(here, "../claude-skill", rel)
15660
16305
  ];
15661
16306
  for (const candidate of candidates) {
15662
16307
  try {
@@ -15670,17 +16315,17 @@ async function readSkillAsset(rel) {
15670
16315
  }
15671
16316
  function neatHome3() {
15672
16317
  const override = process.env.NEAT_HOME;
15673
- if (override && override.length > 0) return import_node_path59.default.resolve(override);
15674
- return import_node_path59.default.join(import_node_os5.default.homedir(), ".neat");
16318
+ if (override && override.length > 0) return import_node_path60.default.resolve(override);
16319
+ return import_node_path60.default.join(import_node_os5.default.homedir(), ".neat");
15675
16320
  }
15676
16321
  function claudeSettingsPath() {
15677
16322
  const override = process.env.NEAT_CLAUDE_SETTINGS;
15678
- if (override && override.length > 0) return import_node_path59.default.resolve(override);
16323
+ if (override && override.length > 0) return import_node_path60.default.resolve(override);
15679
16324
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
15680
- return import_node_path59.default.join(home, ".claude", "settings.json");
16325
+ return import_node_path60.default.join(home, ".claude", "settings.json");
15681
16326
  }
15682
16327
  function installedHookPath() {
15683
- return import_node_path59.default.join(neatHome3(), "hooks", HOOK_FILENAME);
16328
+ return import_node_path60.default.join(neatHome3(), "hooks", HOOK_FILENAME);
15684
16329
  }
15685
16330
  function isNeatSearchEntry(entry2) {
15686
16331
  return (entry2.hooks ?? []).some(
@@ -15713,9 +16358,9 @@ async function runHooks(opts) {
15713
16358
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
15714
16359
  const guide = await readSkillAsset(GUIDE_FILENAME);
15715
16360
  const scriptPath = installedHookPath();
15716
- await import_node_fs40.promises.mkdir(import_node_path59.default.dirname(scriptPath), { recursive: true });
16361
+ await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(scriptPath), { recursive: true });
15717
16362
  await import_node_fs40.promises.writeFile(scriptPath, hookScript, { mode: 493 });
15718
- const guidePath = import_node_path59.default.join(neatHome3(), GUIDE_INSTALL_NAME);
16363
+ const guidePath = import_node_path60.default.join(neatHome3(), GUIDE_INSTALL_NAME);
15719
16364
  await import_node_fs40.promises.writeFile(guidePath, guide, "utf8");
15720
16365
  const settingsFile = claudeSettingsPath();
15721
16366
  let settings = {};
@@ -15742,7 +16387,7 @@ async function runHooks(opts) {
15742
16387
  ...settings,
15743
16388
  hooks: { ...hooks, PreToolUse: preToolUse }
15744
16389
  };
15745
- await import_node_fs40.promises.mkdir(import_node_path59.default.dirname(settingsFile), { recursive: true });
16390
+ await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(settingsFile), { recursive: true });
15746
16391
  await import_node_fs40.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
15747
16392
  console.log(`neat hooks: installed the search-nudge hook`);
15748
16393
  console.log(` script: ${scriptPath}`);
@@ -15814,11 +16459,11 @@ async function runHooksCommand(args) {
15814
16459
 
15815
16460
  // src/cli-verbs.ts
15816
16461
  init_cjs_shims();
15817
- var import_node_path60 = __toESM(require("path"), 1);
16462
+ var import_node_path61 = __toESM(require("path"), 1);
15818
16463
 
15819
16464
  // src/cli-client.ts
15820
16465
  init_cjs_shims();
15821
- var import_types48 = require("@neat.is/types");
16466
+ var import_types49 = require("@neat.is/types");
15822
16467
  var HttpError = class extends Error {
15823
16468
  constructor(status2, message, responseBody = "") {
15824
16469
  super(message);
@@ -15843,10 +16488,10 @@ function createHttpClient(baseUrl, bearerToken) {
15843
16488
  const root = baseUrl.replace(/\/$/, "");
15844
16489
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
15845
16490
  return {
15846
- async get(path62) {
16491
+ async get(path63) {
15847
16492
  let res;
15848
16493
  try {
15849
- res = await fetch(`${root}${path62}`, {
16494
+ res = await fetch(`${root}${path63}`, {
15850
16495
  headers: { ...authHeader }
15851
16496
  });
15852
16497
  } catch (err) {
@@ -15858,16 +16503,16 @@ function createHttpClient(baseUrl, bearerToken) {
15858
16503
  const body = await res.text().catch(() => "");
15859
16504
  throw new HttpError(
15860
16505
  res.status,
15861
- `${res.status} ${res.statusText} on GET ${path62}: ${body}`,
16506
+ `${res.status} ${res.statusText} on GET ${path63}: ${body}`,
15862
16507
  body
15863
16508
  );
15864
16509
  }
15865
16510
  return await res.json();
15866
16511
  },
15867
- async post(path62, body) {
16512
+ async post(path63, body) {
15868
16513
  let res;
15869
16514
  try {
15870
- res = await fetch(`${root}${path62}`, {
16515
+ res = await fetch(`${root}${path63}`, {
15871
16516
  method: "POST",
15872
16517
  headers: { "content-type": "application/json", ...authHeader },
15873
16518
  body: JSON.stringify(body)
@@ -15881,7 +16526,7 @@ function createHttpClient(baseUrl, bearerToken) {
15881
16526
  const text = await res.text().catch(() => "");
15882
16527
  throw new HttpError(
15883
16528
  res.status,
15884
- `${res.status} ${res.statusText} on POST ${path62}: ${text}`,
16529
+ `${res.status} ${res.statusText} on POST ${path63}: ${text}`,
15885
16530
  text
15886
16531
  );
15887
16532
  }
@@ -15895,12 +16540,12 @@ function projectPath(project, suffix) {
15895
16540
  }
15896
16541
  async function runRootCause(client, input) {
15897
16542
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
15898
- const path62 = projectPath(
16543
+ const path63 = projectPath(
15899
16544
  input.project,
15900
16545
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
15901
16546
  );
15902
16547
  try {
15903
- const result = await client.get(path62);
16548
+ const result = await client.get(path63);
15904
16549
  const arrowPath = result.traversalPath.join(" \u2190 ");
15905
16550
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
15906
16551
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -15926,12 +16571,12 @@ async function runRootCause(client, input) {
15926
16571
  }
15927
16572
  async function runBlastRadius(client, input) {
15928
16573
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
15929
- const path62 = projectPath(
16574
+ const path63 = projectPath(
15930
16575
  input.project,
15931
16576
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
15932
16577
  );
15933
16578
  try {
15934
- const result = await client.get(path62);
16579
+ const result = await client.get(path63);
15935
16580
  if (result.totalAffected === 0) {
15936
16581
  return {
15937
16582
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -15960,17 +16605,17 @@ async function runBlastRadius(client, input) {
15960
16605
  }
15961
16606
  }
15962
16607
  function formatBlastEntry(n) {
15963
- const tag = n.edgeProvenance === import_types48.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
16608
+ const tag = n.edgeProvenance === import_types49.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
15964
16609
  return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
15965
16610
  }
15966
16611
  async function runDependencies(client, input) {
15967
16612
  const depth = input.depth ?? 3;
15968
- const path62 = projectPath(
16613
+ const path63 = projectPath(
15969
16614
  input.project,
15970
16615
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
15971
16616
  );
15972
16617
  try {
15973
- const result = await client.get(path62);
16618
+ const result = await client.get(path63);
15974
16619
  if (result.total === 0) {
15975
16620
  return {
15976
16621
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -16017,7 +16662,7 @@ async function runObservedDependencies(client, input) {
16017
16662
  if (result.observed) {
16018
16663
  return {
16019
16664
  summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
16020
- provenance: import_types48.Provenance.OBSERVED
16665
+ provenance: import_types49.Provenance.OBSERVED
16021
16666
  };
16022
16667
  }
16023
16668
  const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
@@ -16027,7 +16672,7 @@ async function runObservedDependencies(client, input) {
16027
16672
  return {
16028
16673
  summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
16029
16674
  block: blockLines.join("\n"),
16030
- provenance: import_types48.Provenance.OBSERVED
16675
+ provenance: import_types49.Provenance.OBSERVED
16031
16676
  };
16032
16677
  } catch (err) {
16033
16678
  if (err instanceof HttpError && err.status === 404) {
@@ -16062,9 +16707,9 @@ function formatDuration(ms) {
16062
16707
  return `${Math.round(h / 24)}d`;
16063
16708
  }
16064
16709
  async function runIncidents(client, input) {
16065
- const path62 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
16710
+ const path63 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
16066
16711
  try {
16067
- const body = await client.get(path62);
16712
+ const body = await client.get(path63);
16068
16713
  const events = body.events;
16069
16714
  if (events.length === 0) {
16070
16715
  return {
@@ -16081,7 +16726,7 @@ async function runIncidents(client, input) {
16081
16726
  return {
16082
16727
  summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
16083
16728
  block: blockLines.join("\n"),
16084
- provenance: import_types48.Provenance.OBSERVED
16729
+ provenance: import_types49.Provenance.OBSERVED
16085
16730
  };
16086
16731
  } catch (err) {
16087
16732
  if (err instanceof HttpError && err.status === 404) {
@@ -16190,7 +16835,7 @@ async function runStaleEdges(client, input) {
16190
16835
  return {
16191
16836
  summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
16192
16837
  block: blockLines.join("\n"),
16193
- provenance: import_types48.Provenance.STALE
16838
+ provenance: import_types49.Provenance.STALE
16194
16839
  };
16195
16840
  }
16196
16841
  async function runPolicies(client, input) {
@@ -16354,7 +16999,7 @@ async function resolveProjectEntry(opts) {
16354
16999
  const cwd = opts.cwd ?? process.cwd();
16355
17000
  const resolvedCwd = await normalizeProjectPath(cwd);
16356
17001
  for (const entry2 of entries) {
16357
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path60.default.sep}`)) {
17002
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path61.default.sep}`)) {
16358
17003
  return entry2;
16359
17004
  }
16360
17005
  }
@@ -16507,7 +17152,7 @@ async function runSync(opts) {
16507
17152
  }
16508
17153
 
16509
17154
  // src/cli.ts
16510
- var import_types49 = require("@neat.is/types");
17155
+ var import_types50 = require("@neat.is/types");
16511
17156
  function isNpxInvocation() {
16512
17157
  if (process.env.npm_command === "exec") return true;
16513
17158
  const execpath = process.env.npm_execpath ?? "";
@@ -16576,8 +17221,9 @@ function usage2() {
16576
17221
  console.log(" --dry-run run extraction in-memory; do not write");
16577
17222
  console.log(" --no-instrument skip the SDK install apply step");
16578
17223
  console.log(" --json emit the delta summary as JSON");
16579
- console.log(" connector Configure pull-based OBSERVED connectors (supabase, railway,");
16580
- console.log(" firebase, cloudflare). Subcommands:");
17224
+ console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
17225
+ console.log(" firebase, cloudflare) and push (vercel, via a trace Drain).");
17226
+ console.log(" Subcommands:");
16581
17227
  console.log(" add <provider> add a connector; validates the credential");
16582
17228
  console.log(" against the provider first (--skip-validate to skip)");
16583
17229
  console.log(" flags: --project <name>, --credential/--token <$VAR|value>,");
@@ -16817,12 +17463,12 @@ async function runInit(opts) {
16817
17463
  printDiscoveryReport(opts, services);
16818
17464
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
16819
17465
  const patch = renderPatch(sections);
16820
- const patchPath = import_node_path61.default.join(opts.scanPath, "neat.patch");
17466
+ const patchPath = import_node_path62.default.join(opts.scanPath, "neat.patch");
16821
17467
  if (opts.dryRun) {
16822
17468
  await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
16823
17469
  written.push(patchPath);
16824
17470
  console.log(`dry-run: patch written to ${patchPath}`);
16825
- const gitignorePath = import_node_path61.default.join(opts.scanPath, ".gitignore");
17471
+ const gitignorePath = import_node_path62.default.join(opts.scanPath, ".gitignore");
16826
17472
  const gitignoreExists = await import_node_fs41.promises.stat(gitignorePath).then(() => true).catch(() => false);
16827
17473
  const verb = gitignoreExists ? "append" : "create";
16828
17474
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
@@ -16834,9 +17480,9 @@ async function runInit(opts) {
16834
17480
  const graph = getGraph(graphKey);
16835
17481
  const projectPaths = pathsForProject(
16836
17482
  graphKey,
16837
- import_node_path61.default.join(opts.scanPath, "neat-out")
17483
+ import_node_path62.default.join(opts.scanPath, "neat-out")
16838
17484
  );
16839
- const errorsPath = import_node_path61.default.join(import_node_path61.default.dirname(opts.outPath), import_node_path61.default.basename(projectPaths.errorsPath));
17485
+ const errorsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), import_node_path62.default.basename(projectPaths.errorsPath));
16840
17486
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
16841
17487
  await saveGraphToDisk(graph, opts.outPath);
16842
17488
  written.push(opts.outPath);
@@ -16955,9 +17601,9 @@ var CLAUDE_SKILL_CONFIG = {
16955
17601
  };
16956
17602
  function claudeConfigPath() {
16957
17603
  const override = process.env.NEAT_CLAUDE_CONFIG;
16958
- if (override && override.length > 0) return import_node_path61.default.resolve(override);
17604
+ if (override && override.length > 0) return import_node_path62.default.resolve(override);
16959
17605
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
16960
- return import_node_path61.default.join(home, ".claude.json");
17606
+ return import_node_path62.default.join(home, ".claude.json");
16961
17607
  }
16962
17608
  async function runSkill(opts) {
16963
17609
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -16981,7 +17627,7 @@ async function runSkill(opts) {
16981
17627
  ...existing,
16982
17628
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
16983
17629
  };
16984
- await import_node_fs41.promises.mkdir(import_node_path61.default.dirname(target), { recursive: true });
17630
+ await import_node_fs41.promises.mkdir(import_node_path62.default.dirname(target), { recursive: true });
16985
17631
  await import_node_fs41.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
16986
17632
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
16987
17633
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -17047,12 +17693,12 @@ async function main() {
17047
17693
  console.error("neat init: --apply and --dry-run are mutually exclusive");
17048
17694
  process.exit(2);
17049
17695
  }
17050
- const scanPath = import_node_path61.default.resolve(target);
17696
+ const scanPath = import_node_path62.default.resolve(target);
17051
17697
  const projectExplicit = parsed.project !== null;
17052
- const projectName = projectExplicit ? project : import_node_path61.default.basename(scanPath);
17698
+ const projectName = projectExplicit ? project : import_node_path62.default.basename(scanPath);
17053
17699
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
17054
- const fallback = pathsForProject(projectKey, import_node_path61.default.join(scanPath, "neat-out")).snapshotPath;
17055
- const outPath = import_node_path61.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
17700
+ const fallback = pathsForProject(projectKey, import_node_path62.default.join(scanPath, "neat-out")).snapshotPath;
17701
+ const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
17056
17702
  const result = await runInit({
17057
17703
  scanPath,
17058
17704
  outPath,
@@ -17073,21 +17719,21 @@ async function main() {
17073
17719
  usage2();
17074
17720
  process.exit(2);
17075
17721
  }
17076
- const scanPath = import_node_path61.default.resolve(target);
17722
+ const scanPath = import_node_path62.default.resolve(target);
17077
17723
  const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
17078
17724
  if (!stat || !stat.isDirectory()) {
17079
17725
  console.error(`neat watch: ${scanPath} is not a directory`);
17080
17726
  process.exit(2);
17081
17727
  }
17082
- const projectPaths = pathsForProject(project, import_node_path61.default.join(scanPath, "neat-out"));
17083
- const outPath = import_node_path61.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
17084
- const errorsPath = import_node_path61.default.resolve(
17085
- process.env.NEAT_ERRORS_PATH ?? import_node_path61.default.join(import_node_path61.default.dirname(outPath), import_node_path61.default.basename(projectPaths.errorsPath))
17728
+ const projectPaths = pathsForProject(project, import_node_path62.default.join(scanPath, "neat-out"));
17729
+ const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
17730
+ const errorsPath = import_node_path62.default.resolve(
17731
+ process.env.NEAT_ERRORS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.errorsPath))
17086
17732
  );
17087
- const staleEventsPath = import_node_path61.default.resolve(
17088
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path61.default.join(import_node_path61.default.dirname(outPath), import_node_path61.default.basename(projectPaths.staleEventsPath))
17733
+ const staleEventsPath = import_node_path62.default.resolve(
17734
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.staleEventsPath))
17089
17735
  );
17090
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path61.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
17736
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path62.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
17091
17737
  const handle = await startWatch(getGraph(project), {
17092
17738
  scanPath,
17093
17739
  outPath,
@@ -17270,11 +17916,11 @@ async function main() {
17270
17916
  process.exit(1);
17271
17917
  }
17272
17918
  async function tryOrchestrator(cmd, parsed) {
17273
- const scanPath = import_node_path61.default.resolve(cmd);
17919
+ const scanPath = import_node_path62.default.resolve(cmd);
17274
17920
  const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
17275
17921
  if (!stat || !stat.isDirectory()) return null;
17276
17922
  const projectExplicit = parsed.project !== null;
17277
- const projectName = projectExplicit ? parsed.project : import_node_path61.default.basename(scanPath);
17923
+ const projectName = projectExplicit ? parsed.project : import_node_path62.default.basename(scanPath);
17278
17924
  const result = await runOrchestrator({
17279
17925
  scanPath,
17280
17926
  project: projectName,
@@ -17463,10 +18109,10 @@ async function runQueryVerb(cmd, parsed) {
17463
18109
  const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
17464
18110
  const out = [];
17465
18111
  for (const p of parts) {
17466
- const r = import_types49.DivergenceTypeSchema.safeParse(p);
18112
+ const r = import_types50.DivergenceTypeSchema.safeParse(p);
17467
18113
  if (!r.success) {
17468
18114
  console.error(
17469
- `neat divergences: unknown --type "${p}". allowed: ${import_types49.DivergenceTypeSchema.options.join(", ")}`
18115
+ `neat divergences: unknown --type "${p}". allowed: ${import_types50.DivergenceTypeSchema.options.join(", ")}`
17470
18116
  );
17471
18117
  return 2;
17472
18118
  }