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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.cjs CHANGED
@@ -59,8 +59,8 @@ function mountBearerAuth(app, opts) {
59
59
  ]);
60
60
  const publicRead = opts.publicRead === true;
61
61
  app.addHook("preHandler", (req, reply, done) => {
62
- const path51 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
- if (exactUnauthPaths.has(path51) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path51)) {
62
+ const path52 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
+ if (exactUnauthPaths.has(path52) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path52)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -192,8 +192,8 @@ function reshapeGrpcRequest(req) {
192
192
  };
193
193
  }
194
194
  function resolveProtoRoot() {
195
- const here = import_node_path47.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
- return import_node_path47.default.resolve(here, "..", "proto");
195
+ const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
+ return import_node_path48.default.resolve(here, "..", "proto");
197
197
  }
198
198
  function loadTraceService() {
199
199
  const protoRoot = resolveProtoRoot();
@@ -261,13 +261,13 @@ async function startOtelGrpcReceiver(opts) {
261
261
  })
262
262
  };
263
263
  }
264
- var import_node_url, import_node_path47, import_node_crypto2, grpc, protoLoader;
264
+ var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
265
265
  var init_otel_grpc = __esm({
266
266
  "src/otel-grpc.ts"() {
267
267
  "use strict";
268
268
  init_cjs_shims();
269
269
  import_node_url = require("url");
270
- import_node_path47 = __toESM(require("path"), 1);
270
+ import_node_path48 = __toESM(require("path"), 1);
271
271
  import_node_crypto2 = require("crypto");
272
272
  grpc = __toESM(require("@grpc/grpc-js"), 1);
273
273
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -360,8 +360,8 @@ function websocketChannelPathOf(attrs) {
360
360
  const v = attrs[key];
361
361
  if (typeof v === "string" && v.length > 0) {
362
362
  const q = v.indexOf("?");
363
- const path51 = q === -1 ? v : v.slice(0, q);
364
- if (path51.length > 0) return path51;
363
+ const path52 = q === -1 ? v : v.slice(0, q);
364
+ if (path52.length > 0) return path52;
365
365
  }
366
366
  }
367
367
  return void 0;
@@ -396,6 +396,7 @@ function parseOtlpRequest(body) {
396
396
  attributes: attrs,
397
397
  dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
398
398
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
399
+ dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
399
400
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
400
401
  messagingDestination: messagingDestinationOf(attrs),
401
402
  graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
@@ -415,10 +416,10 @@ function parseOtlpRequest(body) {
415
416
  return out;
416
417
  }
417
418
  function loadProtoRoot() {
418
- const here = import_node_path48.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
419
- const protoRoot = import_node_path48.default.resolve(here, "..", "proto");
419
+ const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
420
+ const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
420
421
  const root = new import_protobufjs.default.Root();
421
- root.resolvePath = (_origin, target) => import_node_path48.default.resolve(protoRoot, target);
422
+ root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
422
423
  root.loadSync(
423
424
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
424
425
  { keepCase: true }
@@ -630,12 +631,12 @@ async function buildOtelReceiver(opts) {
630
631
  };
631
632
  return decorated;
632
633
  }
633
- var import_node_path48, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
634
+ var import_node_path49, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
634
635
  var init_otel = __esm({
635
636
  "src/otel.ts"() {
636
637
  "use strict";
637
638
  init_cjs_shims();
638
- import_node_path48 = __toESM(require("path"), 1);
639
+ import_node_path49 = __toESM(require("path"), 1);
639
640
  import_node_url2 = require("url");
640
641
  import_fastify2 = __toESM(require("fastify"), 1);
641
642
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -651,7 +652,7 @@ var init_otel = __esm({
651
652
 
652
653
  // src/server.ts
653
654
  init_cjs_shims();
654
- var import_node_path50 = __toESM(require("path"), 1);
655
+ var import_node_path51 = __toESM(require("path"), 1);
655
656
 
656
657
  // src/graph.ts
657
658
  init_cjs_shims();
@@ -675,7 +676,7 @@ function getGraph(project = DEFAULT_PROJECT) {
675
676
  init_cjs_shims();
676
677
  var import_fastify = __toESM(require("fastify"), 1);
677
678
  var import_cors = __toESM(require("@fastify/cors"), 1);
678
- var import_types33 = require("@neat.is/types");
679
+ var import_types34 = require("@neat.is/types");
679
680
 
680
681
  // src/extend/index.ts
681
682
  init_cjs_shims();
@@ -777,11 +778,42 @@ async function readPackageJson(scanPath) {
777
778
  const raw = await import_node_fs2.promises.readFile(pkgPath, "utf8");
778
779
  return JSON.parse(raw);
779
780
  }
781
+ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
782
+ "node_modules",
783
+ "dist",
784
+ "build",
785
+ "out",
786
+ "coverage",
787
+ "neat-out"
788
+ ]);
780
789
  async function findHookFiles(scanPath) {
781
- const entries = await import_node_fs2.promises.readdir(scanPath);
782
- return entries.filter(
783
- (e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
784
- ).sort();
790
+ const found = [];
791
+ const walk3 = async (dir) => {
792
+ const entries = await import_node_fs2.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
793
+ for (const entry of entries) {
794
+ if (entry.isDirectory()) {
795
+ if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
796
+ await walk3(import_node_path2.default.join(dir, entry.name));
797
+ } else if (entry.isFile()) {
798
+ if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
799
+ const rel = import_node_path2.default.relative(scanPath, import_node_path2.default.join(dir, entry.name));
800
+ found.push(rel.split(import_node_path2.default.sep).join("/"));
801
+ }
802
+ }
803
+ }
804
+ };
805
+ await walk3(scanPath);
806
+ return found.sort();
807
+ }
808
+ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
809
+ let fallback = null;
810
+ for (const file of hookFiles) {
811
+ const content = await import_node_fs2.promises.readFile(import_node_path2.default.join(scanPath, file), "utf8");
812
+ const patched = splicedContent(content, snippet2);
813
+ if (patched !== null) return { file, content, patched };
814
+ if (fallback === null) fallback = { file, content };
815
+ }
816
+ return { file: fallback.file, content: fallback.content, patched: null };
785
817
  }
786
818
  function extendLogPath() {
787
819
  return process.env.NEAT_EXTEND_LOG ?? import_node_path2.default.join(import_node_os.default.homedir(), ".neat", "extend-log.ndjson");
@@ -874,7 +906,13 @@ async function applyExtension(ctx, args, options) {
874
906
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
875
907
  }
876
908
  }
877
- const primaryFile = hookFiles[0];
909
+ const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
910
+ if (primary.patched === null) {
911
+ throw new Error(
912
+ `Could not find instrumentation insertion point in ${hookFiles.join(", ")}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
913
+ );
914
+ }
915
+ const primaryFile = primary.file;
878
916
  const primaryPath = import_node_path2.default.join(ctx.scanPath, primaryFile);
879
917
  const filesTouched = [];
880
918
  const depsAdded = [];
@@ -886,14 +924,7 @@ async function applyExtension(ctx, args, options) {
886
924
  filesTouched.push("package.json");
887
925
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
888
926
  }
889
- const hookContent = await import_node_fs2.promises.readFile(primaryPath, "utf8");
890
- const patched = splicedContent(hookContent, args.registration_snippet);
891
- if (!patched) {
892
- throw new Error(
893
- `Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
894
- );
895
- }
896
- await import_node_fs2.promises.writeFile(primaryPath, patched, "utf8");
927
+ await import_node_fs2.promises.writeFile(primaryPath, primary.patched, "utf8");
897
928
  filesTouched.push(primaryFile);
898
929
  const cmd = await detectPackageManager(ctx.scanPath);
899
930
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -935,7 +966,7 @@ async function dryRunExtension(ctx, args) {
935
966
  };
936
967
  }
937
968
  }
938
- const primaryFile = hookFiles[0];
969
+ const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
939
970
  const filesTouched = [];
940
971
  const depsToAdd = [];
941
972
  let packageJsonPatch = {};
@@ -946,10 +977,8 @@ async function dryRunExtension(ctx, args) {
946
977
  depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
947
978
  filesTouched.push("package.json");
948
979
  }
949
- const hookContent = await import_node_fs2.promises.readFile(import_node_path2.default.join(ctx.scanPath, primaryFile), "utf8");
950
- const patched = splicedContent(hookContent, args.registration_snippet);
951
- if (patched) {
952
- filesTouched.push(primaryFile);
980
+ if (primary.patched !== null) {
981
+ filesTouched.push(primary.file);
953
982
  templatePatch = `+ ${args.registration_snippet}`;
954
983
  } else {
955
984
  templatePatch = "Could not find insertion point in hook file.";
@@ -1418,19 +1447,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1418
1447
  function longestIncomingWalk(graph, start, maxDepth) {
1419
1448
  let best = { path: [start], edges: [] };
1420
1449
  const visited = /* @__PURE__ */ new Set([start]);
1421
- function step(node, path51, edges) {
1422
- if (path51.length > best.path.length) {
1423
- best = { path: [...path51], edges: [...edges] };
1450
+ function step(node, path52, edges) {
1451
+ if (path52.length > best.path.length) {
1452
+ best = { path: [...path52], edges: [...edges] };
1424
1453
  }
1425
- if (path51.length - 1 >= maxDepth) return;
1454
+ if (path52.length - 1 >= maxDepth) return;
1426
1455
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1427
1456
  for (const [srcId, edge] of incoming) {
1428
1457
  if (visited.has(srcId)) continue;
1429
1458
  visited.add(srcId);
1430
- path51.push(srcId);
1459
+ path52.push(srcId);
1431
1460
  edges.push(edge);
1432
- step(srcId, path51, edges);
1433
- path51.pop();
1461
+ step(srcId, path52, edges);
1462
+ path52.pop();
1434
1463
  edges.pop();
1435
1464
  visited.delete(srcId);
1436
1465
  }
@@ -1624,26 +1653,26 @@ function dominantFailingCall(graph, serviceId5, visited) {
1624
1653
  return best;
1625
1654
  }
1626
1655
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1627
- const path51 = [originServiceId];
1656
+ const path52 = [originServiceId];
1628
1657
  const edges = [];
1629
1658
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1630
1659
  let current = originServiceId;
1631
1660
  for (let depth = 0; depth < maxDepth; depth++) {
1632
1661
  const hop = dominantFailingCall(graph, current, visited);
1633
1662
  if (!hop) break;
1634
- path51.push(hop.nextService);
1663
+ path52.push(hop.nextService);
1635
1664
  edges.push(hop.edge);
1636
1665
  visited.add(hop.nextService);
1637
1666
  current = hop.nextService;
1638
1667
  }
1639
1668
  if (edges.length === 0) return null;
1640
- return { path: path51, edges, culprit: current };
1669
+ return { path: path52, edges, culprit: current };
1641
1670
  }
1642
1671
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1643
1672
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1644
1673
  if (!chain) return null;
1645
1674
  const culprit = chain.culprit;
1646
- const path51 = [...chain.path];
1675
+ const path52 = [...chain.path];
1647
1676
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1648
1677
  const baseConfidence = confidenceFromMix(chain.edges);
1649
1678
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1651,14 +1680,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1651
1680
  if (loc) {
1652
1681
  let rootCauseNode = culprit;
1653
1682
  if (loc.fileNode) {
1654
- path51.push(loc.fileNode);
1683
+ path52.push(loc.fileNode);
1655
1684
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1656
1685
  rootCauseNode = loc.fileNode;
1657
1686
  }
1658
1687
  return import_types.RootCauseResultSchema.parse({
1659
1688
  rootCauseNode,
1660
1689
  rootCauseReason: loc.rootCauseReason,
1661
- traversalPath: path51,
1690
+ traversalPath: path52,
1662
1691
  edgeProvenances,
1663
1692
  confidence,
1664
1693
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1670,7 +1699,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1670
1699
  return import_types.RootCauseResultSchema.parse({
1671
1700
  rootCauseNode: culprit,
1672
1701
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1673
- traversalPath: path51,
1702
+ traversalPath: path52,
1674
1703
  edgeProvenances,
1675
1704
  confidence,
1676
1705
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -3094,6 +3123,19 @@ function ensureServiceNode(graph, serviceName, env) {
3094
3123
  graph.addNode(id, node);
3095
3124
  return id;
3096
3125
  }
3126
+ function ensureInfraNode(graph, kind, name, provider) {
3127
+ const id = (0, import_types4.infraId)(kind, name);
3128
+ if (graph.hasNode(id)) return id;
3129
+ const node = {
3130
+ id,
3131
+ type: import_types4.NodeType.InfraNode,
3132
+ name,
3133
+ provider,
3134
+ kind
3135
+ };
3136
+ graph.addNode(id, node);
3137
+ return id;
3138
+ }
3097
3139
  function ensureDatabaseNode(graph, host, engine) {
3098
3140
  const id = (0, import_types4.databaseId)(host);
3099
3141
  if (graph.hasNode(id)) return id;
@@ -3406,6 +3448,18 @@ async function handleSpan(ctx, span) {
3406
3448
  callSiteEvidence
3407
3449
  );
3408
3450
  if (result) affectedNode = targetId;
3451
+ if (span.dbSystem === "mongodb" && span.dbCollection) {
3452
+ const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
3453
+ upsertObservedEdge(
3454
+ ctx.graph,
3455
+ import_types4.EdgeType.CALLS,
3456
+ observedSource(),
3457
+ collectionId,
3458
+ ts,
3459
+ isError,
3460
+ callSiteEvidence
3461
+ );
3462
+ }
3409
3463
  }
3410
3464
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
3411
3465
  const targetId = ensureMessagingDestinationNode(
@@ -5150,14 +5204,21 @@ function engineFromImage(image) {
5150
5204
  // src/extract/databases/dotenv.ts
5151
5205
  var CONNECTION_KEYS = /* @__PURE__ */ new Set([
5152
5206
  "DATABASE_URL",
5207
+ "DATABASE_URI",
5153
5208
  "DB_URL",
5209
+ "DB_URI",
5154
5210
  "POSTGRES_URL",
5211
+ "POSTGRES_URI",
5155
5212
  "POSTGRESQL_URL",
5213
+ "POSTGRESQL_URI",
5156
5214
  "MYSQL_URL",
5215
+ "MYSQL_URI",
5216
+ "MONGODB_URL",
5157
5217
  "MONGODB_URI",
5158
5218
  "MONGO_URL",
5159
5219
  "MONGO_URI",
5160
- "REDIS_URL"
5220
+ "REDIS_URL",
5221
+ "REDIS_URI"
5161
5222
  ]);
5162
5223
  function parseDotenvLine(line) {
5163
5224
  const trimmed = line.trim();
@@ -6246,7 +6307,7 @@ async function addGrpcMethods(graph, services) {
6246
6307
 
6247
6308
  // src/extract/calls/index.ts
6248
6309
  init_cjs_shims();
6249
- var import_types21 = require("@neat.is/types");
6310
+ var import_types22 = require("@neat.is/types");
6250
6311
 
6251
6312
  // src/extract/calls/http.ts
6252
6313
  init_cjs_shims();
@@ -6959,15 +7020,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6959
7020
  return out;
6960
7021
  }
6961
7022
 
7023
+ // src/extract/calls/mongoose.ts
7024
+ init_cjs_shims();
7025
+ var import_node_path33 = __toESM(require("path"), 1);
7026
+ var import_types21 = require("@neat.is/types");
7027
+ var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
7028
+ var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
7029
+ var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
7030
+ var UNCOUNTABLES = /* @__PURE__ */ new Set([
7031
+ "advice",
7032
+ "energy",
7033
+ "excretion",
7034
+ "digestion",
7035
+ "cooperation",
7036
+ "health",
7037
+ "justice",
7038
+ "labour",
7039
+ "machinery",
7040
+ "equipment",
7041
+ "information",
7042
+ "pollution",
7043
+ "sewage",
7044
+ "paper",
7045
+ "money",
7046
+ "species",
7047
+ "series",
7048
+ "rain",
7049
+ "rice",
7050
+ "fish",
7051
+ "sheep",
7052
+ "moose",
7053
+ "deer",
7054
+ "news",
7055
+ "expertise",
7056
+ "status",
7057
+ "media"
7058
+ ]);
7059
+ var PLURALIZE_RULES = [
7060
+ [/human$/gi, "humans"],
7061
+ [/(m)an$/gi, "$1en"],
7062
+ [/(pe)rson$/gi, "$1ople"],
7063
+ [/(child)$/gi, "$1ren"],
7064
+ [/^(ox)$/gi, "$1en"],
7065
+ [/(ax|test)is$/gi, "$1es"],
7066
+ [/(octop|vir)us$/gi, "$1i"],
7067
+ [/(alias|status)$/gi, "$1es"],
7068
+ [/(bu)s$/gi, "$1ses"],
7069
+ [/(buffal|tomat|potat)o$/gi, "$1oes"],
7070
+ [/([ti])um$/gi, "$1a"],
7071
+ [/sis$/gi, "ses"],
7072
+ [/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
7073
+ [/(hive)$/gi, "$1s"],
7074
+ [/([^aeiouy]|qu)y$/gi, "$1ies"],
7075
+ [/(x|ch|ss|sh)$/gi, "$1es"],
7076
+ [/(matr|vert|ind)ix|ex$/gi, "$1ices"],
7077
+ [/([m|l])ouse$/gi, "$1ice"],
7078
+ [/(kn|w|l)ife$/gi, "$1ives"],
7079
+ [/(quiz)$/gi, "$1zes"],
7080
+ [/s$/gi, "s"],
7081
+ [/([^a-z])$/, "$1"],
7082
+ [/$/gi, "s"]
7083
+ ];
7084
+ function pluralizeCollection(name) {
7085
+ const str = name.toLowerCase();
7086
+ if (UNCOUNTABLES.has(str)) return str;
7087
+ for (const [re, repl] of PLURALIZE_RULES) {
7088
+ if (str.match(re)) return str.replace(re, repl);
7089
+ }
7090
+ return str;
7091
+ }
7092
+ var MODEL_METHODS = [
7093
+ "find",
7094
+ "findOne",
7095
+ "findById",
7096
+ "findByIdAndUpdate",
7097
+ "findByIdAndDelete",
7098
+ "findOneAndUpdate",
7099
+ "findOneAndDelete",
7100
+ "findOneAndReplace",
7101
+ "countDocuments",
7102
+ "estimatedDocumentCount",
7103
+ "distinct",
7104
+ "aggregate",
7105
+ "exists",
7106
+ "where",
7107
+ "populate",
7108
+ "watch",
7109
+ "hydrate",
7110
+ "create",
7111
+ "insertMany",
7112
+ "insertOne",
7113
+ "updateOne",
7114
+ "updateMany",
7115
+ "replaceOne",
7116
+ "deleteOne",
7117
+ "deleteMany",
7118
+ "bulkWrite",
7119
+ "bulkSave",
7120
+ "save",
7121
+ // pre-v7, removed in current mongoose but present in older codebases:
7122
+ "count",
7123
+ "update",
7124
+ "remove",
7125
+ "findOneAndRemove",
7126
+ "findByIdAndRemove"
7127
+ ];
7128
+ var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
7129
+ function schemaCollectionVars(content) {
7130
+ const out = /* @__PURE__ */ new Map();
7131
+ const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
7132
+ let m;
7133
+ while ((m = re.exec(content)) !== null) {
7134
+ const windowText = content.slice(m.index, m.index + 2e3);
7135
+ const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
7136
+ if (opt) out.set(m[1], opt[1]);
7137
+ }
7138
+ return out;
7139
+ }
7140
+ function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
7141
+ const trimmed = rest.trim();
7142
+ const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
7143
+ if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
7144
+ const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
7145
+ if (firstIdent && schemaColl.has(firstIdent)) {
7146
+ return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
7147
+ }
7148
+ if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
7149
+ return { name: modelName, kind: "mongodb-model" };
7150
+ }
7151
+ return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
7152
+ }
7153
+ function collectModelDefs(content, pluralizeOn) {
7154
+ const schemaColl = schemaCollectionVars(content);
7155
+ const out = [];
7156
+ 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;
7157
+ let m;
7158
+ while ((m = re.exec(content)) !== null) {
7159
+ out.push({
7160
+ modelName: m[2],
7161
+ varName: m[1] ?? null,
7162
+ resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
7163
+ matchText: m[0]
7164
+ });
7165
+ }
7166
+ return out;
7167
+ }
7168
+ function endpoint(r, file, serviceDir, matchText) {
7169
+ const line = lineOf(file.content, matchText);
7170
+ return {
7171
+ infraId: (0, import_types21.infraId)(r.kind, r.name),
7172
+ name: r.name,
7173
+ kind: r.kind,
7174
+ edgeType: "CALLS",
7175
+ confidenceKind: "verified-call-site",
7176
+ evidence: { file: import_node_path33.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
7177
+ };
7178
+ }
7179
+ function mongooseEndpointsFromFile(file, serviceDir) {
7180
+ const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
7181
+ const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
7182
+ if (!hasMongoose && !hasMongodb) return [];
7183
+ const content = file.content;
7184
+ const out = [];
7185
+ const seen = /* @__PURE__ */ new Set();
7186
+ const push = (r, matchText) => {
7187
+ const key = `${r.kind}/${r.name}`;
7188
+ if (seen.has(key)) return;
7189
+ seen.add(key);
7190
+ out.push(endpoint(r, file, serviceDir, matchText));
7191
+ };
7192
+ if (hasMongoose) {
7193
+ const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
7194
+ for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
7195
+ }
7196
+ const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
7197
+ let c;
7198
+ while ((c = collRe.exec(content)) !== null) {
7199
+ push({ name: c[1], kind: "mongodb-collection" }, c[0]);
7200
+ }
7201
+ return out;
7202
+ }
7203
+ function parseDestructure(inner, specifier, out) {
7204
+ for (const raw of inner.split(",")) {
7205
+ const part = raw.trim();
7206
+ if (!part) continue;
7207
+ const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
7208
+ const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
7209
+ if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
7210
+ else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
7211
+ else {
7212
+ const id = /^(\w+)$/.exec(part);
7213
+ if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
7214
+ }
7215
+ }
7216
+ }
7217
+ function parseImportBindings(content) {
7218
+ const out = [];
7219
+ const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
7220
+ let m;
7221
+ while ((m = esm.exec(content)) !== null) {
7222
+ const clause = m[1].trim();
7223
+ const spec = m[2];
7224
+ const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
7225
+ if (ns) {
7226
+ out.push({ local: ns[1], kind: "namespace", specifier: spec });
7227
+ continue;
7228
+ }
7229
+ const named = /\{([^}]*)\}/.exec(clause);
7230
+ if (named) parseDestructure(named[1], spec, out);
7231
+ const def = /^(\w+)\s*(?:,|$)/.exec(clause);
7232
+ if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
7233
+ }
7234
+ const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
7235
+ while ((m = cjs.exec(content)) !== null) {
7236
+ const lhs = m[1];
7237
+ const spec = m[2];
7238
+ if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
7239
+ else out.push({ local: lhs, kind: "whole", specifier: spec });
7240
+ }
7241
+ return out;
7242
+ }
7243
+ function fileExportsOf(content, pluralizeOn) {
7244
+ const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
7245
+ if (defs.length === 0) return null;
7246
+ const byVar = /* @__PURE__ */ new Map();
7247
+ for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
7248
+ const byName = new Map(byVar);
7249
+ let def;
7250
+ const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
7251
+ if (named && byVar.has(named[1])) def = byVar.get(named[1]);
7252
+ if (!def) {
7253
+ const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
7254
+ if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
7255
+ }
7256
+ if (!def && byVar.size === 1) def = [...byVar.values()][0];
7257
+ return { byName, ...def ? { default: def } : {} };
7258
+ }
7259
+ async function mongooseCrossFileEndpoints(files, serviceDir) {
7260
+ const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
7261
+ if (mongooseFiles.length === 0) return [];
7262
+ const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
7263
+ const registry = /* @__PURE__ */ new Map();
7264
+ for (const f of mongooseFiles) {
7265
+ const fx = fileExportsOf(f.content, pluralizeOn);
7266
+ if (fx) registry.set(toPosix2(import_node_path33.default.relative(serviceDir, f.path)), fx);
7267
+ }
7268
+ if (registry.size === 0) return [];
7269
+ const out = [];
7270
+ const seen = /* @__PURE__ */ new Set();
7271
+ for (const f of files) {
7272
+ const bindings = parseImportBindings(f.content);
7273
+ if (bindings.length === 0) continue;
7274
+ const directColl = /* @__PURE__ */ new Map();
7275
+ const nsExports = /* @__PURE__ */ new Map();
7276
+ for (const b of bindings) {
7277
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path33.default.dirname(f.path), serviceDir, null);
7278
+ if (!resolvedRel) continue;
7279
+ const fx = registry.get(resolvedRel);
7280
+ if (!fx) continue;
7281
+ if (b.kind === "named" && b.exportName) {
7282
+ const coll = fx.byName.get(b.exportName);
7283
+ if (coll) directColl.set(b.local, coll);
7284
+ } else if (b.kind === "default") {
7285
+ if (fx.default) directColl.set(b.local, fx.default);
7286
+ } else {
7287
+ if (fx.default) directColl.set(b.local, fx.default);
7288
+ nsExports.set(b.local, fx);
7289
+ }
7290
+ }
7291
+ if (directColl.size === 0 && nsExports.size === 0) continue;
7292
+ const localDefs = new Set(
7293
+ collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
7294
+ );
7295
+ const emit = (collection, matchText) => {
7296
+ const key = `${f.path}::${collection}`;
7297
+ if (seen.has(key)) return;
7298
+ seen.add(key);
7299
+ out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
7300
+ };
7301
+ for (const [local, collection] of directColl) {
7302
+ if (localDefs.has(local)) continue;
7303
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
7304
+ let qm;
7305
+ while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
7306
+ }
7307
+ for (const [local, fx] of nsExports) {
7308
+ const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
7309
+ let qm;
7310
+ while ((qm = qre.exec(f.content)) !== null) {
7311
+ const coll = fx.byName.get(qm[1]);
7312
+ if (coll) emit(coll, qm[0]);
7313
+ }
7314
+ }
7315
+ }
7316
+ return out;
7317
+ }
7318
+
6962
7319
  // src/extract/calls/index.ts
6963
7320
  function edgeTypeFromEndpoint(ep) {
6964
7321
  switch (ep.edgeType) {
6965
7322
  case "PUBLISHES_TO":
6966
- return import_types21.EdgeType.PUBLISHES_TO;
7323
+ return import_types22.EdgeType.PUBLISHES_TO;
6967
7324
  case "CONSUMES_FROM":
6968
- return import_types21.EdgeType.CONSUMES_FROM;
7325
+ return import_types22.EdgeType.CONSUMES_FROM;
6969
7326
  default:
6970
- return import_types21.EdgeType.CALLS;
7327
+ return import_types22.EdgeType.CALLS;
6971
7328
  }
6972
7329
  }
6973
7330
  function isAwsKind(kind) {
@@ -6979,23 +7336,27 @@ async function addExternalEndpointEdges(graph, services) {
6979
7336
  for (const service of services) {
6980
7337
  const files = await loadSourceFiles(service.dir);
6981
7338
  const endpoints = [];
7339
+ const maskedFiles = [];
6982
7340
  for (const file of files) {
6983
7341
  if (isTestPath(file.path)) continue;
6984
7342
  const masked = maskCommentsInSource(file.content);
6985
7343
  const maskedFile = { path: file.path, content: masked };
7344
+ maskedFiles.push(maskedFile);
6986
7345
  endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
6987
7346
  endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
6988
7347
  endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
6989
7348
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
6990
7349
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
7350
+ endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
6991
7351
  }
7352
+ endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
6992
7353
  if (endpoints.length === 0) continue;
6993
7354
  const seenEdges = /* @__PURE__ */ new Set();
6994
7355
  for (const ep of endpoints) {
6995
7356
  if (!graph.hasNode(ep.infraId)) {
6996
7357
  const node = {
6997
7358
  id: ep.infraId,
6998
- type: import_types21.NodeType.InfraNode,
7359
+ type: import_types22.NodeType.InfraNode,
6999
7360
  name: ep.name,
7000
7361
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
7001
7362
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -7007,7 +7368,7 @@ async function addExternalEndpointEdges(graph, services) {
7007
7368
  nodesAdded++;
7008
7369
  }
7009
7370
  const edgeType = edgeTypeFromEndpoint(ep);
7010
- const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
7371
+ const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
7011
7372
  const relFile = toPosix2(ep.evidence.file);
7012
7373
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
7013
7374
  graph,
@@ -7017,7 +7378,7 @@ async function addExternalEndpointEdges(graph, services) {
7017
7378
  );
7018
7379
  nodesAdded += n;
7019
7380
  edgesAdded += e;
7020
- if (!(0, import_types21.passesExtractedFloor)(confidence)) {
7381
+ if (!(0, import_types22.passesExtractedFloor)(confidence)) {
7021
7382
  noteExtractedDropped({
7022
7383
  source: fileNodeId,
7023
7384
  target: ep.infraId,
@@ -7037,7 +7398,7 @@ async function addExternalEndpointEdges(graph, services) {
7037
7398
  source: fileNodeId,
7038
7399
  target: ep.infraId,
7039
7400
  type: edgeType,
7040
- provenance: import_types21.Provenance.EXTRACTED,
7401
+ provenance: import_types22.Provenance.EXTRACTED,
7041
7402
  confidence,
7042
7403
  evidence: ep.evidence
7043
7404
  };
@@ -7063,16 +7424,16 @@ init_cjs_shims();
7063
7424
 
7064
7425
  // src/extract/infra/docker-compose.ts
7065
7426
  init_cjs_shims();
7066
- var import_node_path33 = __toESM(require("path"), 1);
7067
- var import_types23 = require("@neat.is/types");
7427
+ var import_node_path34 = __toESM(require("path"), 1);
7428
+ var import_types24 = require("@neat.is/types");
7068
7429
 
7069
7430
  // src/extract/infra/shared.ts
7070
7431
  init_cjs_shims();
7071
- var import_types22 = require("@neat.is/types");
7432
+ var import_types23 = require("@neat.is/types");
7072
7433
  function makeInfraNode(kind, name, provider = "self", extras) {
7073
7434
  return {
7074
- id: (0, import_types22.infraId)(kind, name),
7075
- type: import_types22.NodeType.InfraNode,
7435
+ id: (0, import_types23.infraId)(kind, name),
7436
+ type: import_types23.NodeType.InfraNode,
7076
7437
  name,
7077
7438
  provider,
7078
7439
  kind,
@@ -7116,8 +7477,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
7116
7477
  source: anchorId,
7117
7478
  target: node.id,
7118
7479
  type: edgeType,
7119
- provenance: import_types22.Provenance.EXTRACTED,
7120
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7480
+ provenance: import_types23.Provenance.EXTRACTED,
7481
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
7121
7482
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7122
7483
  };
7123
7484
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7134,7 +7495,7 @@ function dependsOnList(value) {
7134
7495
  }
7135
7496
  function serviceNameToServiceNode(name, services) {
7136
7497
  for (const s of services) {
7137
- if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
7498
+ if (s.node.name === name || import_node_path34.default.basename(s.dir) === name) return s.node.id;
7138
7499
  }
7139
7500
  return null;
7140
7501
  }
@@ -7143,7 +7504,7 @@ async function addComposeInfra(graph, scanPath, services) {
7143
7504
  let edgesAdded = 0;
7144
7505
  let composePath = null;
7145
7506
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
7146
- const abs = import_node_path33.default.join(scanPath, name);
7507
+ const abs = import_node_path34.default.join(scanPath, name);
7147
7508
  if (await exists2(abs)) {
7148
7509
  composePath = abs;
7149
7510
  break;
@@ -7156,13 +7517,13 @@ async function addComposeInfra(graph, scanPath, services) {
7156
7517
  } catch (err) {
7157
7518
  recordExtractionError(
7158
7519
  "infra docker-compose",
7159
- import_node_path33.default.relative(scanPath, composePath),
7520
+ import_node_path34.default.relative(scanPath, composePath),
7160
7521
  err
7161
7522
  );
7162
7523
  return { nodesAdded, edgesAdded };
7163
7524
  }
7164
7525
  if (!compose?.services) return { nodesAdded, edgesAdded };
7165
- const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
7526
+ const evidenceFile = import_node_path34.default.relative(scanPath, composePath).split(import_node_path34.default.sep).join("/");
7166
7527
  const composeNameToNodeId = /* @__PURE__ */ new Map();
7167
7528
  for (const [composeName, svc] of Object.entries(compose.services)) {
7168
7529
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -7184,15 +7545,15 @@ async function addComposeInfra(graph, scanPath, services) {
7184
7545
  for (const dep of dependsOnList(svc.depends_on)) {
7185
7546
  const targetId = composeNameToNodeId.get(dep);
7186
7547
  if (!targetId) continue;
7187
- const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
7548
+ const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
7188
7549
  if (graph.hasEdge(edgeId)) continue;
7189
7550
  const edge = {
7190
7551
  id: edgeId,
7191
7552
  source: sourceId,
7192
7553
  target: targetId,
7193
- type: import_types23.EdgeType.DEPENDS_ON,
7194
- provenance: import_types23.Provenance.EXTRACTED,
7195
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7554
+ type: import_types24.EdgeType.DEPENDS_ON,
7555
+ provenance: import_types24.Provenance.EXTRACTED,
7556
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
7196
7557
  evidence: { file: evidenceFile }
7197
7558
  };
7198
7559
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7204,9 +7565,9 @@ async function addComposeInfra(graph, scanPath, services) {
7204
7565
 
7205
7566
  // src/extract/infra/dockerfile.ts
7206
7567
  init_cjs_shims();
7207
- var import_node_path34 = __toESM(require("path"), 1);
7568
+ var import_node_path35 = __toESM(require("path"), 1);
7208
7569
  var import_node_fs18 = require("fs");
7209
- var import_types24 = require("@neat.is/types");
7570
+ var import_types25 = require("@neat.is/types");
7210
7571
  function readDockerfile(content) {
7211
7572
  let image = null;
7212
7573
  const ports = [];
@@ -7235,7 +7596,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7235
7596
  let nodesAdded = 0;
7236
7597
  let edgesAdded = 0;
7237
7598
  for (const service of services) {
7238
- const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
7599
+ const dockerfilePath = import_node_path35.default.join(service.dir, "Dockerfile");
7239
7600
  if (!await exists2(dockerfilePath)) continue;
7240
7601
  let content;
7241
7602
  try {
@@ -7243,7 +7604,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7243
7604
  } catch (err) {
7244
7605
  recordExtractionError(
7245
7606
  "infra dockerfile",
7246
- import_node_path34.default.relative(scanPath, dockerfilePath),
7607
+ import_node_path35.default.relative(scanPath, dockerfilePath),
7247
7608
  err
7248
7609
  );
7249
7610
  continue;
@@ -7255,8 +7616,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7255
7616
  graph.addNode(node.id, node);
7256
7617
  nodesAdded++;
7257
7618
  }
7258
- const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
7259
- const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
7619
+ const relDockerfile = toPosix2(import_node_path35.default.relative(service.dir, dockerfilePath));
7620
+ const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, dockerfilePath));
7260
7621
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7261
7622
  graph,
7262
7623
  service.pkg.name,
@@ -7265,15 +7626,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7265
7626
  );
7266
7627
  nodesAdded += fn;
7267
7628
  edgesAdded += fe;
7268
- const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
7629
+ const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
7269
7630
  if (!graph.hasEdge(edgeId)) {
7270
7631
  const edge = {
7271
7632
  id: edgeId,
7272
7633
  source: fileNodeId,
7273
7634
  target: node.id,
7274
- type: import_types24.EdgeType.RUNS_ON,
7275
- provenance: import_types24.Provenance.EXTRACTED,
7276
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7635
+ type: import_types25.EdgeType.RUNS_ON,
7636
+ provenance: import_types25.Provenance.EXTRACTED,
7637
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7277
7638
  evidence: {
7278
7639
  file: evidenceFile,
7279
7640
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -7288,15 +7649,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7288
7649
  graph.addNode(portNode.id, portNode);
7289
7650
  nodesAdded++;
7290
7651
  }
7291
- const portEdgeId = (0, import_types5.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
7652
+ const portEdgeId = (0, import_types5.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
7292
7653
  if (graph.hasEdge(portEdgeId)) continue;
7293
7654
  const portEdge = {
7294
7655
  id: portEdgeId,
7295
7656
  source: fileNodeId,
7296
7657
  target: portNode.id,
7297
- type: import_types24.EdgeType.CONNECTS_TO,
7298
- provenance: import_types24.Provenance.EXTRACTED,
7299
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7658
+ type: import_types25.EdgeType.CONNECTS_TO,
7659
+ provenance: import_types25.Provenance.EXTRACTED,
7660
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7300
7661
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
7301
7662
  };
7302
7663
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -7309,8 +7670,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7309
7670
  // src/extract/infra/terraform.ts
7310
7671
  init_cjs_shims();
7311
7672
  var import_node_fs19 = require("fs");
7312
- var import_node_path35 = __toESM(require("path"), 1);
7313
- var import_types25 = require("@neat.is/types");
7673
+ var import_node_path36 = __toESM(require("path"), 1);
7674
+ var import_types26 = require("@neat.is/types");
7314
7675
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
7315
7676
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
7316
7677
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -7320,11 +7681,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
7320
7681
  for (const entry of entries) {
7321
7682
  if (entry.isDirectory()) {
7322
7683
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
7323
- const child = import_node_path35.default.join(start, entry.name);
7684
+ const child = import_node_path36.default.join(start, entry.name);
7324
7685
  if (await isPythonVenvDir(child)) continue;
7325
7686
  out.push(...await walkTfFiles(child, depth + 1, max));
7326
7687
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
7327
- out.push(import_node_path35.default.join(start, entry.name));
7688
+ out.push(import_node_path36.default.join(start, entry.name));
7328
7689
  }
7329
7690
  }
7330
7691
  return out;
@@ -7356,7 +7717,7 @@ async function addTerraformResources(graph, scanPath) {
7356
7717
  const files = await walkTfFiles(scanPath);
7357
7718
  for (const file of files) {
7358
7719
  const content = await import_node_fs19.promises.readFile(file, "utf8");
7359
- const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
7720
+ const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, file));
7360
7721
  const resources = [];
7361
7722
  const byKey = /* @__PURE__ */ new Map();
7362
7723
  RESOURCE_RE.lastIndex = 0;
@@ -7391,16 +7752,16 @@ async function addTerraformResources(graph, scanPath) {
7391
7752
  if (!target) continue;
7392
7753
  if (seen.has(target.nodeId)) continue;
7393
7754
  seen.add(target.nodeId);
7394
- const edgeId = (0, import_types5.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
7755
+ const edgeId = (0, import_types5.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
7395
7756
  if (graph.hasEdge(edgeId)) continue;
7396
7757
  const line = lineAt2(content, resource.bodyOffset + ref.index);
7397
7758
  const edge = {
7398
7759
  id: edgeId,
7399
7760
  source: resource.nodeId,
7400
7761
  target: target.nodeId,
7401
- type: import_types25.EdgeType.DEPENDS_ON,
7402
- provenance: import_types25.Provenance.EXTRACTED,
7403
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7762
+ type: import_types26.EdgeType.DEPENDS_ON,
7763
+ provenance: import_types26.Provenance.EXTRACTED,
7764
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7404
7765
  evidence: { file: evidenceFile, line, snippet: key }
7405
7766
  };
7406
7767
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7414,7 +7775,7 @@ async function addTerraformResources(graph, scanPath) {
7414
7775
  // src/extract/infra/k8s.ts
7415
7776
  init_cjs_shims();
7416
7777
  var import_node_fs20 = require("fs");
7417
- var import_node_path36 = __toESM(require("path"), 1);
7778
+ var import_node_path37 = __toESM(require("path"), 1);
7418
7779
  var import_yaml3 = require("yaml");
7419
7780
  var K8S_KIND_TO_INFRA_KIND = {
7420
7781
  Service: "k8s-service",
@@ -7432,11 +7793,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
7432
7793
  for (const entry of entries) {
7433
7794
  if (entry.isDirectory()) {
7434
7795
  if (IGNORED_DIRS.has(entry.name)) continue;
7435
- const child = import_node_path36.default.join(start, entry.name);
7796
+ const child = import_node_path37.default.join(start, entry.name);
7436
7797
  if (await isPythonVenvDir(child)) continue;
7437
7798
  out.push(...await walkYamlFiles2(child, depth + 1, max));
7438
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry.name))) {
7439
- out.push(import_node_path36.default.join(start, entry.name));
7799
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path37.default.extname(entry.name))) {
7800
+ out.push(import_node_path37.default.join(start, entry.name));
7440
7801
  }
7441
7802
  }
7442
7803
  return out;
@@ -7470,13 +7831,13 @@ async function addK8sResources(graph, scanPath) {
7470
7831
  // src/extract/infra/cloudflare.ts
7471
7832
  init_cjs_shims();
7472
7833
  var import_node_fs21 = require("fs");
7473
- var import_node_path37 = __toESM(require("path"), 1);
7834
+ var import_node_path38 = __toESM(require("path"), 1);
7474
7835
  var import_smol_toml2 = require("smol-toml");
7475
- var import_types26 = require("@neat.is/types");
7836
+ var import_types27 = require("@neat.is/types");
7476
7837
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7477
7838
  async function readWranglerConfig(dir) {
7478
7839
  for (const filename of WRANGLER_FILENAMES) {
7479
- const abs = import_node_path37.default.join(dir, filename);
7840
+ const abs = import_node_path38.default.join(dir, filename);
7480
7841
  if (!await exists2(abs)) continue;
7481
7842
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7482
7843
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7520,8 +7881,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7520
7881
  source: anchorId,
7521
7882
  target: node.id,
7522
7883
  type: edgeType,
7523
- provenance: import_types26.Provenance.EXTRACTED,
7524
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7884
+ provenance: import_types27.Provenance.EXTRACTED,
7885
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7525
7886
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7526
7887
  };
7527
7888
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7539,11 +7900,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7539
7900
  try {
7540
7901
  read = await readWranglerConfig(service.dir);
7541
7902
  } catch (err) {
7542
- recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
7903
+ recordExtractionError("infra cloudflare", import_node_path38.default.relative(scanPath, service.dir), err);
7543
7904
  continue;
7544
7905
  }
7545
7906
  if (!read || !read.config.name) continue;
7546
- const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
7907
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, read.relFile)));
7547
7908
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7548
7909
  }
7549
7910
  for (const worker of discovered) {
@@ -7555,7 +7916,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7555
7916
  }
7556
7917
  let anchorId = service.node.id;
7557
7918
  if (config.main) {
7558
- const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
7919
+ const entryRelPath = toPosix2(import_node_path38.default.normalize(config.main));
7559
7920
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7560
7921
  graph,
7561
7922
  service.pkg.name,
@@ -7582,15 +7943,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7582
7943
  nodesAdded++;
7583
7944
  }
7584
7945
  if (runtimeNode.id !== anchorId) {
7585
- const runsOnId = (0, import_types5.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
7946
+ const runsOnId = (0, import_types5.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
7586
7947
  if (!graph.hasEdge(runsOnId)) {
7587
7948
  const edge = {
7588
7949
  id: runsOnId,
7589
7950
  source: anchorId,
7590
7951
  target: runtimeNode.id,
7591
- type: import_types26.EdgeType.RUNS_ON,
7592
- provenance: import_types26.Provenance.EXTRACTED,
7593
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7952
+ type: import_types27.EdgeType.RUNS_ON,
7953
+ provenance: import_types27.Provenance.EXTRACTED,
7954
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7594
7955
  evidence: {
7595
7956
  file: evidenceFile,
7596
7957
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7604,7 +7965,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7604
7965
  const result = addResourceEdge(
7605
7966
  graph,
7606
7967
  anchorId,
7607
- import_types26.EdgeType.CONNECTS_TO,
7968
+ import_types27.EdgeType.CONNECTS_TO,
7608
7969
  "cloudflare-route",
7609
7970
  route,
7610
7971
  evidenceFile,
@@ -7628,7 +7989,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7628
7989
  const result = addResourceEdge(
7629
7990
  graph,
7630
7991
  anchorId,
7631
- import_types26.EdgeType.DEPENDS_ON,
7992
+ import_types27.EdgeType.DEPENDS_ON,
7632
7993
  group.kind,
7633
7994
  name,
7634
7995
  evidenceFile,
@@ -7642,7 +8003,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7642
8003
  const result = addResourceEdge(
7643
8004
  graph,
7644
8005
  anchorId,
7645
- import_types26.EdgeType.DEPENDS_ON,
8006
+ import_types27.EdgeType.DEPENDS_ON,
7646
8007
  "cloudflare-cron",
7647
8008
  cron,
7648
8009
  evidenceFile,
@@ -7655,7 +8016,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7655
8016
  const result = addResourceEdge(
7656
8017
  graph,
7657
8018
  anchorId,
7658
- import_types26.EdgeType.DEPENDS_ON,
8019
+ import_types27.EdgeType.DEPENDS_ON,
7659
8020
  "cloudflare-env-var",
7660
8021
  varName,
7661
8022
  evidenceFile,
@@ -7668,15 +8029,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7668
8029
  if (!svc.service) continue;
7669
8030
  const target = workerIndex.get(svc.service);
7670
8031
  if (target && target.anchorId !== anchorId) {
7671
- const edgeId = (0, import_types5.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
8032
+ const edgeId = (0, import_types5.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
7672
8033
  if (!graph.hasEdge(edgeId)) {
7673
8034
  const edge = {
7674
8035
  id: edgeId,
7675
8036
  source: anchorId,
7676
8037
  target: target.anchorId,
7677
- type: import_types26.EdgeType.CALLS,
7678
- provenance: import_types26.Provenance.EXTRACTED,
7679
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
8038
+ type: import_types27.EdgeType.CALLS,
8039
+ provenance: import_types27.Provenance.EXTRACTED,
8040
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7680
8041
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7681
8042
  };
7682
8043
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7687,7 +8048,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7687
8048
  const result = addResourceEdge(
7688
8049
  graph,
7689
8050
  anchorId,
7690
- import_types26.EdgeType.DEPENDS_ON,
8051
+ import_types27.EdgeType.DEPENDS_ON,
7691
8052
  "cloudflare-service-binding",
7692
8053
  svc.service,
7693
8054
  evidenceFile,
@@ -7703,12 +8064,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7703
8064
  // src/extract/infra/vercel.ts
7704
8065
  init_cjs_shims();
7705
8066
  var import_node_fs22 = require("fs");
7706
- var import_node_path38 = __toESM(require("path"), 1);
7707
- var import_types27 = require("@neat.is/types");
8067
+ var import_node_path39 = __toESM(require("path"), 1);
8068
+ var import_types28 = require("@neat.is/types");
7708
8069
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7709
8070
  async function readVercelConfig(dir) {
7710
8071
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7711
- const abs = import_node_path38.default.join(dir, filename);
8072
+ const abs = import_node_path39.default.join(dir, filename);
7712
8073
  if (!await exists2(abs)) continue;
7713
8074
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7714
8075
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7717,7 +8078,7 @@ async function readVercelConfig(dir) {
7717
8078
  return null;
7718
8079
  }
7719
8080
  async function readLinkedProjectName(dir) {
7720
- const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
8081
+ const abs = import_node_path39.default.join(dir, ".vercel", "project.json");
7721
8082
  if (!await exists2(abs)) return void 0;
7722
8083
  const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
7723
8084
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7735,7 +8096,7 @@ async function addVercelServices(graph, services, scanPath) {
7735
8096
  read = await readVercelConfig(service.dir);
7736
8097
  projectName = await readLinkedProjectName(service.dir);
7737
8098
  } catch (err) {
7738
- recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
8099
+ recordExtractionError("infra vercel", import_node_path39.default.relative(scanPath, service.dir), err);
7739
8100
  continue;
7740
8101
  }
7741
8102
  if (!read && !projectName) continue;
@@ -7751,7 +8112,7 @@ async function addVercelServices(graph, services, scanPath) {
7751
8112
  const anchorId = service.node.id;
7752
8113
  if (!read) continue;
7753
8114
  const { config, relFile, raw } = read;
7754
- const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
8115
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7755
8116
  const add = (edgeType, kind, name) => {
7756
8117
  if (!name) return;
7757
8118
  const result = emitPlatformResourceEdge(
@@ -7767,12 +8128,12 @@ async function addVercelServices(graph, services, scanPath) {
7767
8128
  nodesAdded += result.nodesAdded;
7768
8129
  edgesAdded += result.edgesAdded;
7769
8130
  };
7770
- add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7771
- for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7772
- for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7773
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
8131
+ add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
8132
+ for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
8133
+ for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
8134
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7774
8135
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7775
- add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
8136
+ add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7776
8137
  }
7777
8138
  }
7778
8139
  return { nodesAdded, edgesAdded };
@@ -7781,13 +8142,13 @@ async function addVercelServices(graph, services, scanPath) {
7781
8142
  // src/extract/infra/railway.ts
7782
8143
  init_cjs_shims();
7783
8144
  var import_node_fs23 = require("fs");
7784
- var import_node_path39 = __toESM(require("path"), 1);
8145
+ var import_node_path40 = __toESM(require("path"), 1);
7785
8146
  var import_smol_toml3 = require("smol-toml");
7786
- var import_types28 = require("@neat.is/types");
8147
+ var import_types29 = require("@neat.is/types");
7787
8148
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7788
8149
  async function readRailwayConfig(dir) {
7789
8150
  for (const filename of RAILWAY_FILENAMES) {
7790
- const abs = import_node_path39.default.join(dir, filename);
8151
+ const abs = import_node_path40.default.join(dir, filename);
7791
8152
  if (!await exists2(abs)) continue;
7792
8153
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
7793
8154
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7803,7 +8164,7 @@ async function addRailwayServices(graph, services, scanPath) {
7803
8164
  try {
7804
8165
  read = await readRailwayConfig(service.dir);
7805
8166
  } catch (err) {
7806
- recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
8167
+ recordExtractionError("infra railway", import_node_path40.default.relative(scanPath, service.dir), err);
7807
8168
  continue;
7808
8169
  }
7809
8170
  if (!read) continue;
@@ -7813,7 +8174,7 @@ async function addRailwayServices(graph, services, scanPath) {
7813
8174
  }
7814
8175
  const anchorId = service.node.id;
7815
8176
  const { config, relFile, raw } = read;
7816
- const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
8177
+ const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
7817
8178
  const add = (edgeType, kind, name) => {
7818
8179
  if (!name) return;
7819
8180
  const result = emitPlatformResourceEdge(
@@ -7829,9 +8190,9 @@ async function addRailwayServices(graph, services, scanPath) {
7829
8190
  nodesAdded += result.nodesAdded;
7830
8191
  edgesAdded += result.edgesAdded;
7831
8192
  };
7832
- add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7833
- add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7834
- add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
8193
+ add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
8194
+ add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
8195
+ add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7835
8196
  }
7836
8197
  return { nodesAdded, edgesAdded };
7837
8198
  }
@@ -7839,12 +8200,12 @@ async function addRailwayServices(graph, services, scanPath) {
7839
8200
  // src/extract/infra/supabase.ts
7840
8201
  init_cjs_shims();
7841
8202
  var import_node_fs24 = require("fs");
7842
- var import_node_path40 = __toESM(require("path"), 1);
8203
+ var import_node_path41 = __toESM(require("path"), 1);
7843
8204
  var import_smol_toml4 = require("smol-toml");
7844
- var import_types29 = require("@neat.is/types");
8205
+ var import_types30 = require("@neat.is/types");
7845
8206
  async function readSupabaseConfig(dir) {
7846
- const relFile = import_node_path40.default.join("supabase", "config.toml");
7847
- const abs = import_node_path40.default.join(dir, relFile);
8207
+ const relFile = import_node_path41.default.join("supabase", "config.toml");
8208
+ const abs = import_node_path41.default.join(dir, relFile);
7848
8209
  if (!await exists2(abs)) return null;
7849
8210
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
7850
8211
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7858,7 +8219,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7858
8219
  try {
7859
8220
  read = await readSupabaseConfig(service.dir);
7860
8221
  } catch (err) {
7861
- recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
8222
+ recordExtractionError("infra supabase", import_node_path41.default.relative(scanPath, service.dir), err);
7862
8223
  continue;
7863
8224
  }
7864
8225
  if (!read) continue;
@@ -7873,7 +8234,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7873
8234
  });
7874
8235
  }
7875
8236
  const anchorId = service.node.id;
7876
- const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
8237
+ const evidenceFile = toPosix2(import_node_path41.default.relative(scanPath, import_node_path41.default.join(service.dir, relFile)));
7877
8238
  const add = (edgeType, kind, name) => {
7878
8239
  if (!name) return;
7879
8240
  const result = emitPlatformResourceEdge(
@@ -7889,10 +8250,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7889
8250
  nodesAdded += result.nodesAdded;
7890
8251
  edgesAdded += result.edgesAdded;
7891
8252
  };
7892
- add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7893
- for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7894
- if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7895
- if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
8253
+ add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
8254
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
8255
+ if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
8256
+ if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7896
8257
  }
7897
8258
  return { nodesAdded, edgesAdded };
7898
8259
  }
@@ -7914,17 +8275,17 @@ async function addInfra(graph, scanPath, services) {
7914
8275
  }
7915
8276
 
7916
8277
  // src/extract/index.ts
7917
- var import_node_path42 = __toESM(require("path"), 1);
8278
+ var import_node_path43 = __toESM(require("path"), 1);
7918
8279
 
7919
8280
  // src/extract/retire.ts
7920
8281
  init_cjs_shims();
7921
8282
  var import_node_fs25 = require("fs");
7922
- var import_node_path41 = __toESM(require("path"), 1);
7923
- var import_types30 = require("@neat.is/types");
8283
+ var import_node_path42 = __toESM(require("path"), 1);
8284
+ var import_types31 = require("@neat.is/types");
7924
8285
  function dropOrphanedFileNodes(graph) {
7925
8286
  const orphans = [];
7926
8287
  graph.forEachNode((id, attrs) => {
7927
- if (attrs.type !== import_types30.NodeType.FileNode) return;
8288
+ if (attrs.type !== import_types31.NodeType.FileNode) return;
7928
8289
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7929
8290
  orphans.push(id);
7930
8291
  }
@@ -7937,14 +8298,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7937
8298
  const bases = [scanPath, ...serviceDirs];
7938
8299
  graph.forEachEdge((id, attrs) => {
7939
8300
  const edge = attrs;
7940
- if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
8301
+ if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
7941
8302
  const evidenceFile = edge.evidence?.file;
7942
8303
  if (!evidenceFile) return;
7943
- if (import_node_path41.default.isAbsolute(evidenceFile)) {
8304
+ if (import_node_path42.default.isAbsolute(evidenceFile)) {
7944
8305
  if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
7945
8306
  return;
7946
8307
  }
7947
- const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path41.default.join(base, evidenceFile)));
8308
+ const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path42.default.join(base, evidenceFile)));
7948
8309
  if (!found) toDrop.push(id);
7949
8310
  });
7950
8311
  for (const id of toDrop) graph.dropEdge(id);
@@ -7986,7 +8347,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7986
8347
  }
7987
8348
  const droppedEntries = drainDroppedExtracted();
7988
8349
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7989
- const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
8350
+ const rejectedPath = import_node_path43.default.join(import_node_path43.default.dirname(opts.errorsPath), "rejected.ndjson");
7990
8351
  try {
7991
8352
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7992
8353
  } catch (err) {
@@ -8098,8 +8459,8 @@ function canonicalJson(value) {
8098
8459
  // src/persist.ts
8099
8460
  init_cjs_shims();
8100
8461
  var import_node_fs27 = require("fs");
8101
- var import_node_path43 = __toESM(require("path"), 1);
8102
- var import_types31 = require("@neat.is/types");
8462
+ var import_node_path44 = __toESM(require("path"), 1);
8463
+ var import_types32 = require("@neat.is/types");
8103
8464
  var SCHEMA_VERSION = 4;
8104
8465
  function migrateV1ToV2(payload) {
8105
8466
  const nodes = payload.graph.nodes;
@@ -8121,12 +8482,12 @@ function migrateV2ToV3(payload) {
8121
8482
  for (const edge of edges) {
8122
8483
  const attrs = edge.attributes;
8123
8484
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
8124
- attrs.provenance = import_types31.Provenance.OBSERVED;
8485
+ attrs.provenance = import_types32.Provenance.OBSERVED;
8125
8486
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
8126
8487
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
8127
8488
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
8128
8489
  if (type && source && target) {
8129
- const newId = (0, import_types31.observedEdgeId)(source, target, type);
8490
+ const newId = (0, import_types32.observedEdgeId)(source, target, type);
8130
8491
  attrs.id = newId;
8131
8492
  if (edge.key) edge.key = newId;
8132
8493
  }
@@ -8135,7 +8496,7 @@ function migrateV2ToV3(payload) {
8135
8496
  return { ...payload, schemaVersion: 3 };
8136
8497
  }
8137
8498
  async function ensureDir(filePath) {
8138
- await import_node_fs27.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
8499
+ await import_node_fs27.promises.mkdir(import_node_path44.default.dirname(filePath), { recursive: true });
8139
8500
  }
8140
8501
  async function saveGraphToDisk(graph, outPath) {
8141
8502
  await ensureDir(outPath);
@@ -8216,23 +8577,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
8216
8577
 
8217
8578
  // src/projects.ts
8218
8579
  init_cjs_shims();
8219
- var import_node_path44 = __toESM(require("path"), 1);
8580
+ var import_node_path45 = __toESM(require("path"), 1);
8220
8581
  function pathsForProject(project, baseDir) {
8221
8582
  if (project === DEFAULT_PROJECT) {
8222
8583
  return {
8223
- snapshotPath: import_node_path44.default.join(baseDir, "graph.json"),
8224
- errorsPath: import_node_path44.default.join(baseDir, "errors.ndjson"),
8225
- staleEventsPath: import_node_path44.default.join(baseDir, "stale-events.ndjson"),
8226
- embeddingsCachePath: import_node_path44.default.join(baseDir, "embeddings.json"),
8227
- policyViolationsPath: import_node_path44.default.join(baseDir, "policy-violations.ndjson")
8584
+ snapshotPath: import_node_path45.default.join(baseDir, "graph.json"),
8585
+ errorsPath: import_node_path45.default.join(baseDir, "errors.ndjson"),
8586
+ staleEventsPath: import_node_path45.default.join(baseDir, "stale-events.ndjson"),
8587
+ embeddingsCachePath: import_node_path45.default.join(baseDir, "embeddings.json"),
8588
+ policyViolationsPath: import_node_path45.default.join(baseDir, "policy-violations.ndjson")
8228
8589
  };
8229
8590
  }
8230
8591
  return {
8231
- snapshotPath: import_node_path44.default.join(baseDir, `${project}.json`),
8232
- errorsPath: import_node_path44.default.join(baseDir, `errors.${project}.ndjson`),
8233
- staleEventsPath: import_node_path44.default.join(baseDir, `stale-events.${project}.ndjson`),
8234
- embeddingsCachePath: import_node_path44.default.join(baseDir, `embeddings.${project}.json`),
8235
- policyViolationsPath: import_node_path44.default.join(baseDir, `policy-violations.${project}.ndjson`)
8592
+ snapshotPath: import_node_path45.default.join(baseDir, `${project}.json`),
8593
+ errorsPath: import_node_path45.default.join(baseDir, `errors.${project}.ndjson`),
8594
+ staleEventsPath: import_node_path45.default.join(baseDir, `stale-events.${project}.ndjson`),
8595
+ embeddingsCachePath: import_node_path45.default.join(baseDir, `embeddings.${project}.json`),
8596
+ policyViolationsPath: import_node_path45.default.join(baseDir, `policy-violations.${project}.ndjson`)
8236
8597
  };
8237
8598
  }
8238
8599
  var Projects = class {
@@ -8274,15 +8635,15 @@ function parseExtraProjects(raw) {
8274
8635
  init_cjs_shims();
8275
8636
  var import_node_fs28 = require("fs");
8276
8637
  var import_node_os3 = __toESM(require("os"), 1);
8277
- var import_node_path45 = __toESM(require("path"), 1);
8278
- var import_types32 = require("@neat.is/types");
8638
+ var import_node_path46 = __toESM(require("path"), 1);
8639
+ var import_types33 = require("@neat.is/types");
8279
8640
  function neatHome() {
8280
8641
  const override = process.env.NEAT_HOME;
8281
- if (override && override.length > 0) return import_node_path45.default.resolve(override);
8282
- return import_node_path45.default.join(import_node_os3.default.homedir(), ".neat");
8642
+ if (override && override.length > 0) return import_node_path46.default.resolve(override);
8643
+ return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
8283
8644
  }
8284
8645
  function registryPath() {
8285
- return import_node_path45.default.join(neatHome(), "projects.json");
8646
+ return import_node_path46.default.join(neatHome(), "projects.json");
8286
8647
  }
8287
8648
  async function readRegistry() {
8288
8649
  const file = registryPath();
@@ -8296,7 +8657,7 @@ async function readRegistry() {
8296
8657
  throw err;
8297
8658
  }
8298
8659
  const parsed = JSON.parse(raw);
8299
- return import_types32.RegistryFileSchema.parse(parsed);
8660
+ return import_types33.RegistryFileSchema.parse(parsed);
8300
8661
  }
8301
8662
  async function getProject(name) {
8302
8663
  const reg = await readRegistry();
@@ -8371,16 +8732,16 @@ init_auth();
8371
8732
  // src/connectors-config.ts
8372
8733
  init_cjs_shims();
8373
8734
  var import_node_os4 = __toESM(require("os"), 1);
8374
- var import_node_path46 = __toESM(require("path"), 1);
8735
+ var import_node_path47 = __toESM(require("path"), 1);
8375
8736
  var import_node_fs29 = require("fs");
8376
8737
  var CONNECTORS_CONFIG_VERSION = 1;
8377
8738
  function neatHome2() {
8378
8739
  const override = process.env.NEAT_HOME;
8379
- if (override && override.length > 0) return import_node_path46.default.resolve(override);
8380
- return import_node_path46.default.join(import_node_os4.default.homedir(), ".neat");
8740
+ if (override && override.length > 0) return import_node_path47.default.resolve(override);
8741
+ return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
8381
8742
  }
8382
8743
  function connectorsConfigPath(home = neatHome2()) {
8383
- return import_node_path46.default.join(home, "connectors.json");
8744
+ return import_node_path47.default.join(home, "connectors.json");
8384
8745
  }
8385
8746
  var MODE_MASK_LOOSER_THAN_0600 = 63;
8386
8747
  async function warnIfModeLooserThan0600(file) {
@@ -8679,11 +9040,11 @@ function registerRoutes(scope, ctx) {
8679
9040
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8680
9041
  const parsed = [];
8681
9042
  for (const c of candidates) {
8682
- const r = import_types33.DivergenceTypeSchema.safeParse(c);
9043
+ const r = import_types34.DivergenceTypeSchema.safeParse(c);
8683
9044
  if (!r.success) {
8684
9045
  return reply.code(400).send({
8685
9046
  error: `unknown divergence type "${c}"`,
8686
- allowed: import_types33.DivergenceTypeSchema.options
9047
+ allowed: import_types34.DivergenceTypeSchema.options
8687
9048
  });
8688
9049
  }
8689
9050
  parsed.push(r.data);
@@ -8954,7 +9315,7 @@ function registerRoutes(scope, ctx) {
8954
9315
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
8955
9316
  let violations = await log.readAll();
8956
9317
  if (req.query.severity) {
8957
- const sev = import_types33.PolicySeveritySchema.safeParse(req.query.severity);
9318
+ const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
8958
9319
  if (!sev.success) {
8959
9320
  return reply.code(400).send({
8960
9321
  error: "invalid severity",
@@ -8993,7 +9354,7 @@ function registerRoutes(scope, ctx) {
8993
9354
  scope.post("/policies/check", async (req, reply) => {
8994
9355
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
8995
9356
  if (!proj) return;
8996
- const parsed = import_types33.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9357
+ const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
8997
9358
  if (!parsed.success) {
8998
9359
  return reply.code(400).send({
8999
9360
  error: "invalid /policies/check body",
@@ -9127,7 +9488,7 @@ function registerRoutes(scope, ctx) {
9127
9488
  });
9128
9489
  }
9129
9490
  async function buildApi(opts) {
9130
- const app = (0, import_fastify.default)({ logger: false });
9491
+ const app = (0, import_fastify.default)({ logger: false, routerOptions: { maxParamLength: 1024 } });
9131
9492
  await app.register(import_cors.default, { origin: true });
9132
9493
  const env = readAuthEnv();
9133
9494
  const authToken = opts.authToken ?? env.authToken;
@@ -9259,7 +9620,7 @@ init_otel_grpc();
9259
9620
  // src/search.ts
9260
9621
  init_cjs_shims();
9261
9622
  var import_node_fs30 = require("fs");
9262
- var import_node_path49 = __toESM(require("path"), 1);
9623
+ var import_node_path50 = __toESM(require("path"), 1);
9263
9624
  var import_node_crypto3 = require("crypto");
9264
9625
  var DEFAULT_LIMIT = 10;
9265
9626
  var NOMIC_DIM = 768;
@@ -9422,7 +9783,7 @@ async function readCache(cachePath) {
9422
9783
  }
9423
9784
  }
9424
9785
  async function writeCache(cachePath, cache) {
9425
- await import_node_fs30.promises.mkdir(import_node_path49.default.dirname(cachePath), { recursive: true });
9786
+ await import_node_fs30.promises.mkdir(import_node_path50.default.dirname(cachePath), { recursive: true });
9426
9787
  await import_node_fs30.promises.writeFile(cachePath, JSON.stringify(cache));
9427
9788
  }
9428
9789
  var VectorIndex = class {
@@ -9603,14 +9964,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
9603
9964
  async function main() {
9604
9965
  const baseDirEnv = process.env.NEAT_OUT_DIR;
9605
9966
  const legacyOutPath = process.env.NEAT_OUT_PATH;
9606
- const baseDir = baseDirEnv ? import_node_path50.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path50.default.resolve(import_node_path50.default.dirname(legacyOutPath)) : import_node_path50.default.resolve("./neat-out");
9607
- const defaultScanPath = import_node_path50.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
9967
+ const baseDir = baseDirEnv ? import_node_path51.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path51.default.resolve(import_node_path51.default.dirname(legacyOutPath)) : import_node_path51.default.resolve("./neat-out");
9968
+ const defaultScanPath = import_node_path51.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
9608
9969
  const registry = new Projects();
9609
9970
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
9610
9971
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
9611
9972
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
9612
9973
  const projectScan = process.env[envKey];
9613
- await bootProject(registry, name, projectScan ? import_node_path50.default.resolve(projectScan) : void 0, baseDir);
9974
+ await bootProject(registry, name, projectScan ? import_node_path51.default.resolve(projectScan) : void 0, baseDir);
9614
9975
  }
9615
9976
  const host = process.env.HOST ?? "0.0.0.0";
9616
9977
  const port = Number(process.env.PORT ?? 8080);