@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/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();
@@ -1446,19 +1447,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1446
1447
  function longestIncomingWalk(graph, start, maxDepth) {
1447
1448
  let best = { path: [start], edges: [] };
1448
1449
  const visited = /* @__PURE__ */ new Set([start]);
1449
- function step(node, path51, edges) {
1450
- if (path51.length > best.path.length) {
1451
- best = { path: [...path51], edges: [...edges] };
1450
+ function step(node, path52, edges) {
1451
+ if (path52.length > best.path.length) {
1452
+ best = { path: [...path52], edges: [...edges] };
1452
1453
  }
1453
- if (path51.length - 1 >= maxDepth) return;
1454
+ if (path52.length - 1 >= maxDepth) return;
1454
1455
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1455
1456
  for (const [srcId, edge] of incoming) {
1456
1457
  if (visited.has(srcId)) continue;
1457
1458
  visited.add(srcId);
1458
- path51.push(srcId);
1459
+ path52.push(srcId);
1459
1460
  edges.push(edge);
1460
- step(srcId, path51, edges);
1461
- path51.pop();
1461
+ step(srcId, path52, edges);
1462
+ path52.pop();
1462
1463
  edges.pop();
1463
1464
  visited.delete(srcId);
1464
1465
  }
@@ -1652,26 +1653,26 @@ function dominantFailingCall(graph, serviceId5, visited) {
1652
1653
  return best;
1653
1654
  }
1654
1655
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1655
- const path51 = [originServiceId];
1656
+ const path52 = [originServiceId];
1656
1657
  const edges = [];
1657
1658
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1658
1659
  let current = originServiceId;
1659
1660
  for (let depth = 0; depth < maxDepth; depth++) {
1660
1661
  const hop = dominantFailingCall(graph, current, visited);
1661
1662
  if (!hop) break;
1662
- path51.push(hop.nextService);
1663
+ path52.push(hop.nextService);
1663
1664
  edges.push(hop.edge);
1664
1665
  visited.add(hop.nextService);
1665
1666
  current = hop.nextService;
1666
1667
  }
1667
1668
  if (edges.length === 0) return null;
1668
- return { path: path51, edges, culprit: current };
1669
+ return { path: path52, edges, culprit: current };
1669
1670
  }
1670
1671
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1671
1672
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1672
1673
  if (!chain) return null;
1673
1674
  const culprit = chain.culprit;
1674
- const path51 = [...chain.path];
1675
+ const path52 = [...chain.path];
1675
1676
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1676
1677
  const baseConfidence = confidenceFromMix(chain.edges);
1677
1678
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1679,14 +1680,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1679
1680
  if (loc) {
1680
1681
  let rootCauseNode = culprit;
1681
1682
  if (loc.fileNode) {
1682
- path51.push(loc.fileNode);
1683
+ path52.push(loc.fileNode);
1683
1684
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1684
1685
  rootCauseNode = loc.fileNode;
1685
1686
  }
1686
1687
  return import_types.RootCauseResultSchema.parse({
1687
1688
  rootCauseNode,
1688
1689
  rootCauseReason: loc.rootCauseReason,
1689
- traversalPath: path51,
1690
+ traversalPath: path52,
1690
1691
  edgeProvenances,
1691
1692
  confidence,
1692
1693
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1698,7 +1699,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1698
1699
  return import_types.RootCauseResultSchema.parse({
1699
1700
  rootCauseNode: culprit,
1700
1701
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1701
- traversalPath: path51,
1702
+ traversalPath: path52,
1702
1703
  edgeProvenances,
1703
1704
  confidence,
1704
1705
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -3122,6 +3123,19 @@ function ensureServiceNode(graph, serviceName, env) {
3122
3123
  graph.addNode(id, node);
3123
3124
  return id;
3124
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
+ }
3125
3139
  function ensureDatabaseNode(graph, host, engine) {
3126
3140
  const id = (0, import_types4.databaseId)(host);
3127
3141
  if (graph.hasNode(id)) return id;
@@ -3434,6 +3448,18 @@ async function handleSpan(ctx, span) {
3434
3448
  callSiteEvidence
3435
3449
  );
3436
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
+ }
3437
3463
  }
3438
3464
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
3439
3465
  const targetId = ensureMessagingDestinationNode(
@@ -6281,7 +6307,7 @@ async function addGrpcMethods(graph, services) {
6281
6307
 
6282
6308
  // src/extract/calls/index.ts
6283
6309
  init_cjs_shims();
6284
- var import_types21 = require("@neat.is/types");
6310
+ var import_types22 = require("@neat.is/types");
6285
6311
 
6286
6312
  // src/extract/calls/http.ts
6287
6313
  init_cjs_shims();
@@ -6994,15 +7020,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6994
7020
  return out;
6995
7021
  }
6996
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
+
6997
7319
  // src/extract/calls/index.ts
6998
7320
  function edgeTypeFromEndpoint(ep) {
6999
7321
  switch (ep.edgeType) {
7000
7322
  case "PUBLISHES_TO":
7001
- return import_types21.EdgeType.PUBLISHES_TO;
7323
+ return import_types22.EdgeType.PUBLISHES_TO;
7002
7324
  case "CONSUMES_FROM":
7003
- return import_types21.EdgeType.CONSUMES_FROM;
7325
+ return import_types22.EdgeType.CONSUMES_FROM;
7004
7326
  default:
7005
- return import_types21.EdgeType.CALLS;
7327
+ return import_types22.EdgeType.CALLS;
7006
7328
  }
7007
7329
  }
7008
7330
  function isAwsKind(kind) {
@@ -7014,23 +7336,27 @@ async function addExternalEndpointEdges(graph, services) {
7014
7336
  for (const service of services) {
7015
7337
  const files = await loadSourceFiles(service.dir);
7016
7338
  const endpoints = [];
7339
+ const maskedFiles = [];
7017
7340
  for (const file of files) {
7018
7341
  if (isTestPath(file.path)) continue;
7019
7342
  const masked = maskCommentsInSource(file.content);
7020
7343
  const maskedFile = { path: file.path, content: masked };
7344
+ maskedFiles.push(maskedFile);
7021
7345
  endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
7022
7346
  endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
7023
7347
  endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
7024
7348
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
7025
7349
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
7350
+ endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
7026
7351
  }
7352
+ endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
7027
7353
  if (endpoints.length === 0) continue;
7028
7354
  const seenEdges = /* @__PURE__ */ new Set();
7029
7355
  for (const ep of endpoints) {
7030
7356
  if (!graph.hasNode(ep.infraId)) {
7031
7357
  const node = {
7032
7358
  id: ep.infraId,
7033
- type: import_types21.NodeType.InfraNode,
7359
+ type: import_types22.NodeType.InfraNode,
7034
7360
  name: ep.name,
7035
7361
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
7036
7362
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -7042,7 +7368,7 @@ async function addExternalEndpointEdges(graph, services) {
7042
7368
  nodesAdded++;
7043
7369
  }
7044
7370
  const edgeType = edgeTypeFromEndpoint(ep);
7045
- const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
7371
+ const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
7046
7372
  const relFile = toPosix2(ep.evidence.file);
7047
7373
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
7048
7374
  graph,
@@ -7052,7 +7378,7 @@ async function addExternalEndpointEdges(graph, services) {
7052
7378
  );
7053
7379
  nodesAdded += n;
7054
7380
  edgesAdded += e;
7055
- if (!(0, import_types21.passesExtractedFloor)(confidence)) {
7381
+ if (!(0, import_types22.passesExtractedFloor)(confidence)) {
7056
7382
  noteExtractedDropped({
7057
7383
  source: fileNodeId,
7058
7384
  target: ep.infraId,
@@ -7072,7 +7398,7 @@ async function addExternalEndpointEdges(graph, services) {
7072
7398
  source: fileNodeId,
7073
7399
  target: ep.infraId,
7074
7400
  type: edgeType,
7075
- provenance: import_types21.Provenance.EXTRACTED,
7401
+ provenance: import_types22.Provenance.EXTRACTED,
7076
7402
  confidence,
7077
7403
  evidence: ep.evidence
7078
7404
  };
@@ -7098,16 +7424,16 @@ init_cjs_shims();
7098
7424
 
7099
7425
  // src/extract/infra/docker-compose.ts
7100
7426
  init_cjs_shims();
7101
- var import_node_path33 = __toESM(require("path"), 1);
7102
- var import_types23 = require("@neat.is/types");
7427
+ var import_node_path34 = __toESM(require("path"), 1);
7428
+ var import_types24 = require("@neat.is/types");
7103
7429
 
7104
7430
  // src/extract/infra/shared.ts
7105
7431
  init_cjs_shims();
7106
- var import_types22 = require("@neat.is/types");
7432
+ var import_types23 = require("@neat.is/types");
7107
7433
  function makeInfraNode(kind, name, provider = "self", extras) {
7108
7434
  return {
7109
- id: (0, import_types22.infraId)(kind, name),
7110
- type: import_types22.NodeType.InfraNode,
7435
+ id: (0, import_types23.infraId)(kind, name),
7436
+ type: import_types23.NodeType.InfraNode,
7111
7437
  name,
7112
7438
  provider,
7113
7439
  kind,
@@ -7151,8 +7477,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
7151
7477
  source: anchorId,
7152
7478
  target: node.id,
7153
7479
  type: edgeType,
7154
- provenance: import_types22.Provenance.EXTRACTED,
7155
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7480
+ provenance: import_types23.Provenance.EXTRACTED,
7481
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
7156
7482
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7157
7483
  };
7158
7484
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7169,7 +7495,7 @@ function dependsOnList(value) {
7169
7495
  }
7170
7496
  function serviceNameToServiceNode(name, services) {
7171
7497
  for (const s of services) {
7172
- 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;
7173
7499
  }
7174
7500
  return null;
7175
7501
  }
@@ -7178,7 +7504,7 @@ async function addComposeInfra(graph, scanPath, services) {
7178
7504
  let edgesAdded = 0;
7179
7505
  let composePath = null;
7180
7506
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
7181
- const abs = import_node_path33.default.join(scanPath, name);
7507
+ const abs = import_node_path34.default.join(scanPath, name);
7182
7508
  if (await exists2(abs)) {
7183
7509
  composePath = abs;
7184
7510
  break;
@@ -7191,13 +7517,13 @@ async function addComposeInfra(graph, scanPath, services) {
7191
7517
  } catch (err) {
7192
7518
  recordExtractionError(
7193
7519
  "infra docker-compose",
7194
- import_node_path33.default.relative(scanPath, composePath),
7520
+ import_node_path34.default.relative(scanPath, composePath),
7195
7521
  err
7196
7522
  );
7197
7523
  return { nodesAdded, edgesAdded };
7198
7524
  }
7199
7525
  if (!compose?.services) return { nodesAdded, edgesAdded };
7200
- 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("/");
7201
7527
  const composeNameToNodeId = /* @__PURE__ */ new Map();
7202
7528
  for (const [composeName, svc] of Object.entries(compose.services)) {
7203
7529
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -7219,15 +7545,15 @@ async function addComposeInfra(graph, scanPath, services) {
7219
7545
  for (const dep of dependsOnList(svc.depends_on)) {
7220
7546
  const targetId = composeNameToNodeId.get(dep);
7221
7547
  if (!targetId) continue;
7222
- 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);
7223
7549
  if (graph.hasEdge(edgeId)) continue;
7224
7550
  const edge = {
7225
7551
  id: edgeId,
7226
7552
  source: sourceId,
7227
7553
  target: targetId,
7228
- type: import_types23.EdgeType.DEPENDS_ON,
7229
- provenance: import_types23.Provenance.EXTRACTED,
7230
- 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"),
7231
7557
  evidence: { file: evidenceFile }
7232
7558
  };
7233
7559
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7239,9 +7565,9 @@ async function addComposeInfra(graph, scanPath, services) {
7239
7565
 
7240
7566
  // src/extract/infra/dockerfile.ts
7241
7567
  init_cjs_shims();
7242
- var import_node_path34 = __toESM(require("path"), 1);
7568
+ var import_node_path35 = __toESM(require("path"), 1);
7243
7569
  var import_node_fs18 = require("fs");
7244
- var import_types24 = require("@neat.is/types");
7570
+ var import_types25 = require("@neat.is/types");
7245
7571
  function readDockerfile(content) {
7246
7572
  let image = null;
7247
7573
  const ports = [];
@@ -7270,7 +7596,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7270
7596
  let nodesAdded = 0;
7271
7597
  let edgesAdded = 0;
7272
7598
  for (const service of services) {
7273
- const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
7599
+ const dockerfilePath = import_node_path35.default.join(service.dir, "Dockerfile");
7274
7600
  if (!await exists2(dockerfilePath)) continue;
7275
7601
  let content;
7276
7602
  try {
@@ -7278,7 +7604,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7278
7604
  } catch (err) {
7279
7605
  recordExtractionError(
7280
7606
  "infra dockerfile",
7281
- import_node_path34.default.relative(scanPath, dockerfilePath),
7607
+ import_node_path35.default.relative(scanPath, dockerfilePath),
7282
7608
  err
7283
7609
  );
7284
7610
  continue;
@@ -7290,8 +7616,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7290
7616
  graph.addNode(node.id, node);
7291
7617
  nodesAdded++;
7292
7618
  }
7293
- const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
7294
- 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));
7295
7621
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7296
7622
  graph,
7297
7623
  service.pkg.name,
@@ -7300,15 +7626,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7300
7626
  );
7301
7627
  nodesAdded += fn;
7302
7628
  edgesAdded += fe;
7303
- 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);
7304
7630
  if (!graph.hasEdge(edgeId)) {
7305
7631
  const edge = {
7306
7632
  id: edgeId,
7307
7633
  source: fileNodeId,
7308
7634
  target: node.id,
7309
- type: import_types24.EdgeType.RUNS_ON,
7310
- provenance: import_types24.Provenance.EXTRACTED,
7311
- 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"),
7312
7638
  evidence: {
7313
7639
  file: evidenceFile,
7314
7640
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -7323,15 +7649,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7323
7649
  graph.addNode(portNode.id, portNode);
7324
7650
  nodesAdded++;
7325
7651
  }
7326
- 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);
7327
7653
  if (graph.hasEdge(portEdgeId)) continue;
7328
7654
  const portEdge = {
7329
7655
  id: portEdgeId,
7330
7656
  source: fileNodeId,
7331
7657
  target: portNode.id,
7332
- type: import_types24.EdgeType.CONNECTS_TO,
7333
- provenance: import_types24.Provenance.EXTRACTED,
7334
- 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"),
7335
7661
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
7336
7662
  };
7337
7663
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -7344,8 +7670,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7344
7670
  // src/extract/infra/terraform.ts
7345
7671
  init_cjs_shims();
7346
7672
  var import_node_fs19 = require("fs");
7347
- var import_node_path35 = __toESM(require("path"), 1);
7348
- var import_types25 = require("@neat.is/types");
7673
+ var import_node_path36 = __toESM(require("path"), 1);
7674
+ var import_types26 = require("@neat.is/types");
7349
7675
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
7350
7676
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
7351
7677
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -7355,11 +7681,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
7355
7681
  for (const entry of entries) {
7356
7682
  if (entry.isDirectory()) {
7357
7683
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
7358
- const child = import_node_path35.default.join(start, entry.name);
7684
+ const child = import_node_path36.default.join(start, entry.name);
7359
7685
  if (await isPythonVenvDir(child)) continue;
7360
7686
  out.push(...await walkTfFiles(child, depth + 1, max));
7361
7687
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
7362
- out.push(import_node_path35.default.join(start, entry.name));
7688
+ out.push(import_node_path36.default.join(start, entry.name));
7363
7689
  }
7364
7690
  }
7365
7691
  return out;
@@ -7391,7 +7717,7 @@ async function addTerraformResources(graph, scanPath) {
7391
7717
  const files = await walkTfFiles(scanPath);
7392
7718
  for (const file of files) {
7393
7719
  const content = await import_node_fs19.promises.readFile(file, "utf8");
7394
- const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
7720
+ const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, file));
7395
7721
  const resources = [];
7396
7722
  const byKey = /* @__PURE__ */ new Map();
7397
7723
  RESOURCE_RE.lastIndex = 0;
@@ -7426,16 +7752,16 @@ async function addTerraformResources(graph, scanPath) {
7426
7752
  if (!target) continue;
7427
7753
  if (seen.has(target.nodeId)) continue;
7428
7754
  seen.add(target.nodeId);
7429
- 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);
7430
7756
  if (graph.hasEdge(edgeId)) continue;
7431
7757
  const line = lineAt2(content, resource.bodyOffset + ref.index);
7432
7758
  const edge = {
7433
7759
  id: edgeId,
7434
7760
  source: resource.nodeId,
7435
7761
  target: target.nodeId,
7436
- type: import_types25.EdgeType.DEPENDS_ON,
7437
- provenance: import_types25.Provenance.EXTRACTED,
7438
- 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"),
7439
7765
  evidence: { file: evidenceFile, line, snippet: key }
7440
7766
  };
7441
7767
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7449,7 +7775,7 @@ async function addTerraformResources(graph, scanPath) {
7449
7775
  // src/extract/infra/k8s.ts
7450
7776
  init_cjs_shims();
7451
7777
  var import_node_fs20 = require("fs");
7452
- var import_node_path36 = __toESM(require("path"), 1);
7778
+ var import_node_path37 = __toESM(require("path"), 1);
7453
7779
  var import_yaml3 = require("yaml");
7454
7780
  var K8S_KIND_TO_INFRA_KIND = {
7455
7781
  Service: "k8s-service",
@@ -7467,11 +7793,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
7467
7793
  for (const entry of entries) {
7468
7794
  if (entry.isDirectory()) {
7469
7795
  if (IGNORED_DIRS.has(entry.name)) continue;
7470
- const child = import_node_path36.default.join(start, entry.name);
7796
+ const child = import_node_path37.default.join(start, entry.name);
7471
7797
  if (await isPythonVenvDir(child)) continue;
7472
7798
  out.push(...await walkYamlFiles2(child, depth + 1, max));
7473
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry.name))) {
7474
- 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));
7475
7801
  }
7476
7802
  }
7477
7803
  return out;
@@ -7505,13 +7831,13 @@ async function addK8sResources(graph, scanPath) {
7505
7831
  // src/extract/infra/cloudflare.ts
7506
7832
  init_cjs_shims();
7507
7833
  var import_node_fs21 = require("fs");
7508
- var import_node_path37 = __toESM(require("path"), 1);
7834
+ var import_node_path38 = __toESM(require("path"), 1);
7509
7835
  var import_smol_toml2 = require("smol-toml");
7510
- var import_types26 = require("@neat.is/types");
7836
+ var import_types27 = require("@neat.is/types");
7511
7837
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7512
7838
  async function readWranglerConfig(dir) {
7513
7839
  for (const filename of WRANGLER_FILENAMES) {
7514
- const abs = import_node_path37.default.join(dir, filename);
7840
+ const abs = import_node_path38.default.join(dir, filename);
7515
7841
  if (!await exists2(abs)) continue;
7516
7842
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7517
7843
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7555,8 +7881,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7555
7881
  source: anchorId,
7556
7882
  target: node.id,
7557
7883
  type: edgeType,
7558
- provenance: import_types26.Provenance.EXTRACTED,
7559
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7884
+ provenance: import_types27.Provenance.EXTRACTED,
7885
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7560
7886
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7561
7887
  };
7562
7888
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7574,11 +7900,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7574
7900
  try {
7575
7901
  read = await readWranglerConfig(service.dir);
7576
7902
  } catch (err) {
7577
- 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);
7578
7904
  continue;
7579
7905
  }
7580
7906
  if (!read || !read.config.name) continue;
7581
- 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)));
7582
7908
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7583
7909
  }
7584
7910
  for (const worker of discovered) {
@@ -7590,7 +7916,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7590
7916
  }
7591
7917
  let anchorId = service.node.id;
7592
7918
  if (config.main) {
7593
- const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
7919
+ const entryRelPath = toPosix2(import_node_path38.default.normalize(config.main));
7594
7920
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7595
7921
  graph,
7596
7922
  service.pkg.name,
@@ -7617,15 +7943,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7617
7943
  nodesAdded++;
7618
7944
  }
7619
7945
  if (runtimeNode.id !== anchorId) {
7620
- 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);
7621
7947
  if (!graph.hasEdge(runsOnId)) {
7622
7948
  const edge = {
7623
7949
  id: runsOnId,
7624
7950
  source: anchorId,
7625
7951
  target: runtimeNode.id,
7626
- type: import_types26.EdgeType.RUNS_ON,
7627
- provenance: import_types26.Provenance.EXTRACTED,
7628
- 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"),
7629
7955
  evidence: {
7630
7956
  file: evidenceFile,
7631
7957
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7639,7 +7965,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7639
7965
  const result = addResourceEdge(
7640
7966
  graph,
7641
7967
  anchorId,
7642
- import_types26.EdgeType.CONNECTS_TO,
7968
+ import_types27.EdgeType.CONNECTS_TO,
7643
7969
  "cloudflare-route",
7644
7970
  route,
7645
7971
  evidenceFile,
@@ -7663,7 +7989,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7663
7989
  const result = addResourceEdge(
7664
7990
  graph,
7665
7991
  anchorId,
7666
- import_types26.EdgeType.DEPENDS_ON,
7992
+ import_types27.EdgeType.DEPENDS_ON,
7667
7993
  group.kind,
7668
7994
  name,
7669
7995
  evidenceFile,
@@ -7677,7 +8003,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7677
8003
  const result = addResourceEdge(
7678
8004
  graph,
7679
8005
  anchorId,
7680
- import_types26.EdgeType.DEPENDS_ON,
8006
+ import_types27.EdgeType.DEPENDS_ON,
7681
8007
  "cloudflare-cron",
7682
8008
  cron,
7683
8009
  evidenceFile,
@@ -7690,7 +8016,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7690
8016
  const result = addResourceEdge(
7691
8017
  graph,
7692
8018
  anchorId,
7693
- import_types26.EdgeType.DEPENDS_ON,
8019
+ import_types27.EdgeType.DEPENDS_ON,
7694
8020
  "cloudflare-env-var",
7695
8021
  varName,
7696
8022
  evidenceFile,
@@ -7703,15 +8029,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7703
8029
  if (!svc.service) continue;
7704
8030
  const target = workerIndex.get(svc.service);
7705
8031
  if (target && target.anchorId !== anchorId) {
7706
- 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);
7707
8033
  if (!graph.hasEdge(edgeId)) {
7708
8034
  const edge = {
7709
8035
  id: edgeId,
7710
8036
  source: anchorId,
7711
8037
  target: target.anchorId,
7712
- type: import_types26.EdgeType.CALLS,
7713
- provenance: import_types26.Provenance.EXTRACTED,
7714
- 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"),
7715
8041
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7716
8042
  };
7717
8043
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7722,7 +8048,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7722
8048
  const result = addResourceEdge(
7723
8049
  graph,
7724
8050
  anchorId,
7725
- import_types26.EdgeType.DEPENDS_ON,
8051
+ import_types27.EdgeType.DEPENDS_ON,
7726
8052
  "cloudflare-service-binding",
7727
8053
  svc.service,
7728
8054
  evidenceFile,
@@ -7738,12 +8064,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7738
8064
  // src/extract/infra/vercel.ts
7739
8065
  init_cjs_shims();
7740
8066
  var import_node_fs22 = require("fs");
7741
- var import_node_path38 = __toESM(require("path"), 1);
7742
- var import_types27 = require("@neat.is/types");
8067
+ var import_node_path39 = __toESM(require("path"), 1);
8068
+ var import_types28 = require("@neat.is/types");
7743
8069
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7744
8070
  async function readVercelConfig(dir) {
7745
8071
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7746
- const abs = import_node_path38.default.join(dir, filename);
8072
+ const abs = import_node_path39.default.join(dir, filename);
7747
8073
  if (!await exists2(abs)) continue;
7748
8074
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7749
8075
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7752,7 +8078,7 @@ async function readVercelConfig(dir) {
7752
8078
  return null;
7753
8079
  }
7754
8080
  async function readLinkedProjectName(dir) {
7755
- const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
8081
+ const abs = import_node_path39.default.join(dir, ".vercel", "project.json");
7756
8082
  if (!await exists2(abs)) return void 0;
7757
8083
  const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
7758
8084
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7770,7 +8096,7 @@ async function addVercelServices(graph, services, scanPath) {
7770
8096
  read = await readVercelConfig(service.dir);
7771
8097
  projectName = await readLinkedProjectName(service.dir);
7772
8098
  } catch (err) {
7773
- 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);
7774
8100
  continue;
7775
8101
  }
7776
8102
  if (!read && !projectName) continue;
@@ -7786,7 +8112,7 @@ async function addVercelServices(graph, services, scanPath) {
7786
8112
  const anchorId = service.node.id;
7787
8113
  if (!read) continue;
7788
8114
  const { config, relFile, raw } = read;
7789
- 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)));
7790
8116
  const add = (edgeType, kind, name) => {
7791
8117
  if (!name) return;
7792
8118
  const result = emitPlatformResourceEdge(
@@ -7802,12 +8128,12 @@ async function addVercelServices(graph, services, scanPath) {
7802
8128
  nodesAdded += result.nodesAdded;
7803
8129
  edgesAdded += result.edgesAdded;
7804
8130
  };
7805
- add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7806
- for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7807
- for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7808
- 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);
7809
8135
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7810
- add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
8136
+ add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7811
8137
  }
7812
8138
  }
7813
8139
  return { nodesAdded, edgesAdded };
@@ -7816,13 +8142,13 @@ async function addVercelServices(graph, services, scanPath) {
7816
8142
  // src/extract/infra/railway.ts
7817
8143
  init_cjs_shims();
7818
8144
  var import_node_fs23 = require("fs");
7819
- var import_node_path39 = __toESM(require("path"), 1);
8145
+ var import_node_path40 = __toESM(require("path"), 1);
7820
8146
  var import_smol_toml3 = require("smol-toml");
7821
- var import_types28 = require("@neat.is/types");
8147
+ var import_types29 = require("@neat.is/types");
7822
8148
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7823
8149
  async function readRailwayConfig(dir) {
7824
8150
  for (const filename of RAILWAY_FILENAMES) {
7825
- const abs = import_node_path39.default.join(dir, filename);
8151
+ const abs = import_node_path40.default.join(dir, filename);
7826
8152
  if (!await exists2(abs)) continue;
7827
8153
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
7828
8154
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7838,7 +8164,7 @@ async function addRailwayServices(graph, services, scanPath) {
7838
8164
  try {
7839
8165
  read = await readRailwayConfig(service.dir);
7840
8166
  } catch (err) {
7841
- 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);
7842
8168
  continue;
7843
8169
  }
7844
8170
  if (!read) continue;
@@ -7848,7 +8174,7 @@ async function addRailwayServices(graph, services, scanPath) {
7848
8174
  }
7849
8175
  const anchorId = service.node.id;
7850
8176
  const { config, relFile, raw } = read;
7851
- 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)));
7852
8178
  const add = (edgeType, kind, name) => {
7853
8179
  if (!name) return;
7854
8180
  const result = emitPlatformResourceEdge(
@@ -7864,9 +8190,9 @@ async function addRailwayServices(graph, services, scanPath) {
7864
8190
  nodesAdded += result.nodesAdded;
7865
8191
  edgesAdded += result.edgesAdded;
7866
8192
  };
7867
- add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7868
- add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7869
- 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);
7870
8196
  }
7871
8197
  return { nodesAdded, edgesAdded };
7872
8198
  }
@@ -7874,12 +8200,12 @@ async function addRailwayServices(graph, services, scanPath) {
7874
8200
  // src/extract/infra/supabase.ts
7875
8201
  init_cjs_shims();
7876
8202
  var import_node_fs24 = require("fs");
7877
- var import_node_path40 = __toESM(require("path"), 1);
8203
+ var import_node_path41 = __toESM(require("path"), 1);
7878
8204
  var import_smol_toml4 = require("smol-toml");
7879
- var import_types29 = require("@neat.is/types");
8205
+ var import_types30 = require("@neat.is/types");
7880
8206
  async function readSupabaseConfig(dir) {
7881
- const relFile = import_node_path40.default.join("supabase", "config.toml");
7882
- 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);
7883
8209
  if (!await exists2(abs)) return null;
7884
8210
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
7885
8211
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7893,7 +8219,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7893
8219
  try {
7894
8220
  read = await readSupabaseConfig(service.dir);
7895
8221
  } catch (err) {
7896
- 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);
7897
8223
  continue;
7898
8224
  }
7899
8225
  if (!read) continue;
@@ -7908,7 +8234,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7908
8234
  });
7909
8235
  }
7910
8236
  const anchorId = service.node.id;
7911
- 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)));
7912
8238
  const add = (edgeType, kind, name) => {
7913
8239
  if (!name) return;
7914
8240
  const result = emitPlatformResourceEdge(
@@ -7924,10 +8250,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7924
8250
  nodesAdded += result.nodesAdded;
7925
8251
  edgesAdded += result.edgesAdded;
7926
8252
  };
7927
- add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7928
- for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7929
- if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7930
- 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");
7931
8257
  }
7932
8258
  return { nodesAdded, edgesAdded };
7933
8259
  }
@@ -7949,17 +8275,17 @@ async function addInfra(graph, scanPath, services) {
7949
8275
  }
7950
8276
 
7951
8277
  // src/extract/index.ts
7952
- var import_node_path42 = __toESM(require("path"), 1);
8278
+ var import_node_path43 = __toESM(require("path"), 1);
7953
8279
 
7954
8280
  // src/extract/retire.ts
7955
8281
  init_cjs_shims();
7956
8282
  var import_node_fs25 = require("fs");
7957
- var import_node_path41 = __toESM(require("path"), 1);
7958
- var import_types30 = require("@neat.is/types");
8283
+ var import_node_path42 = __toESM(require("path"), 1);
8284
+ var import_types31 = require("@neat.is/types");
7959
8285
  function dropOrphanedFileNodes(graph) {
7960
8286
  const orphans = [];
7961
8287
  graph.forEachNode((id, attrs) => {
7962
- if (attrs.type !== import_types30.NodeType.FileNode) return;
8288
+ if (attrs.type !== import_types31.NodeType.FileNode) return;
7963
8289
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7964
8290
  orphans.push(id);
7965
8291
  }
@@ -7972,14 +8298,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7972
8298
  const bases = [scanPath, ...serviceDirs];
7973
8299
  graph.forEachEdge((id, attrs) => {
7974
8300
  const edge = attrs;
7975
- if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
8301
+ if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
7976
8302
  const evidenceFile = edge.evidence?.file;
7977
8303
  if (!evidenceFile) return;
7978
- if (import_node_path41.default.isAbsolute(evidenceFile)) {
8304
+ if (import_node_path42.default.isAbsolute(evidenceFile)) {
7979
8305
  if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
7980
8306
  return;
7981
8307
  }
7982
- 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)));
7983
8309
  if (!found) toDrop.push(id);
7984
8310
  });
7985
8311
  for (const id of toDrop) graph.dropEdge(id);
@@ -8021,7 +8347,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
8021
8347
  }
8022
8348
  const droppedEntries = drainDroppedExtracted();
8023
8349
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
8024
- 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");
8025
8351
  try {
8026
8352
  await writeRejectedExtracted(droppedEntries, rejectedPath);
8027
8353
  } catch (err) {
@@ -8133,8 +8459,8 @@ function canonicalJson(value) {
8133
8459
  // src/persist.ts
8134
8460
  init_cjs_shims();
8135
8461
  var import_node_fs27 = require("fs");
8136
- var import_node_path43 = __toESM(require("path"), 1);
8137
- var import_types31 = require("@neat.is/types");
8462
+ var import_node_path44 = __toESM(require("path"), 1);
8463
+ var import_types32 = require("@neat.is/types");
8138
8464
  var SCHEMA_VERSION = 4;
8139
8465
  function migrateV1ToV2(payload) {
8140
8466
  const nodes = payload.graph.nodes;
@@ -8156,12 +8482,12 @@ function migrateV2ToV3(payload) {
8156
8482
  for (const edge of edges) {
8157
8483
  const attrs = edge.attributes;
8158
8484
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
8159
- attrs.provenance = import_types31.Provenance.OBSERVED;
8485
+ attrs.provenance = import_types32.Provenance.OBSERVED;
8160
8486
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
8161
8487
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
8162
8488
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
8163
8489
  if (type && source && target) {
8164
- const newId = (0, import_types31.observedEdgeId)(source, target, type);
8490
+ const newId = (0, import_types32.observedEdgeId)(source, target, type);
8165
8491
  attrs.id = newId;
8166
8492
  if (edge.key) edge.key = newId;
8167
8493
  }
@@ -8170,7 +8496,7 @@ function migrateV2ToV3(payload) {
8170
8496
  return { ...payload, schemaVersion: 3 };
8171
8497
  }
8172
8498
  async function ensureDir(filePath) {
8173
- 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 });
8174
8500
  }
8175
8501
  async function saveGraphToDisk(graph, outPath) {
8176
8502
  await ensureDir(outPath);
@@ -8251,23 +8577,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
8251
8577
 
8252
8578
  // src/projects.ts
8253
8579
  init_cjs_shims();
8254
- var import_node_path44 = __toESM(require("path"), 1);
8580
+ var import_node_path45 = __toESM(require("path"), 1);
8255
8581
  function pathsForProject(project, baseDir) {
8256
8582
  if (project === DEFAULT_PROJECT) {
8257
8583
  return {
8258
- snapshotPath: import_node_path44.default.join(baseDir, "graph.json"),
8259
- errorsPath: import_node_path44.default.join(baseDir, "errors.ndjson"),
8260
- staleEventsPath: import_node_path44.default.join(baseDir, "stale-events.ndjson"),
8261
- embeddingsCachePath: import_node_path44.default.join(baseDir, "embeddings.json"),
8262
- 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")
8263
8589
  };
8264
8590
  }
8265
8591
  return {
8266
- snapshotPath: import_node_path44.default.join(baseDir, `${project}.json`),
8267
- errorsPath: import_node_path44.default.join(baseDir, `errors.${project}.ndjson`),
8268
- staleEventsPath: import_node_path44.default.join(baseDir, `stale-events.${project}.ndjson`),
8269
- embeddingsCachePath: import_node_path44.default.join(baseDir, `embeddings.${project}.json`),
8270
- 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`)
8271
8597
  };
8272
8598
  }
8273
8599
  var Projects = class {
@@ -8309,15 +8635,15 @@ function parseExtraProjects(raw) {
8309
8635
  init_cjs_shims();
8310
8636
  var import_node_fs28 = require("fs");
8311
8637
  var import_node_os3 = __toESM(require("os"), 1);
8312
- var import_node_path45 = __toESM(require("path"), 1);
8313
- var import_types32 = require("@neat.is/types");
8638
+ var import_node_path46 = __toESM(require("path"), 1);
8639
+ var import_types33 = require("@neat.is/types");
8314
8640
  function neatHome() {
8315
8641
  const override = process.env.NEAT_HOME;
8316
- if (override && override.length > 0) return import_node_path45.default.resolve(override);
8317
- 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");
8318
8644
  }
8319
8645
  function registryPath() {
8320
- return import_node_path45.default.join(neatHome(), "projects.json");
8646
+ return import_node_path46.default.join(neatHome(), "projects.json");
8321
8647
  }
8322
8648
  async function readRegistry() {
8323
8649
  const file = registryPath();
@@ -8331,7 +8657,7 @@ async function readRegistry() {
8331
8657
  throw err;
8332
8658
  }
8333
8659
  const parsed = JSON.parse(raw);
8334
- return import_types32.RegistryFileSchema.parse(parsed);
8660
+ return import_types33.RegistryFileSchema.parse(parsed);
8335
8661
  }
8336
8662
  async function getProject(name) {
8337
8663
  const reg = await readRegistry();
@@ -8406,16 +8732,16 @@ init_auth();
8406
8732
  // src/connectors-config.ts
8407
8733
  init_cjs_shims();
8408
8734
  var import_node_os4 = __toESM(require("os"), 1);
8409
- var import_node_path46 = __toESM(require("path"), 1);
8735
+ var import_node_path47 = __toESM(require("path"), 1);
8410
8736
  var import_node_fs29 = require("fs");
8411
8737
  var CONNECTORS_CONFIG_VERSION = 1;
8412
8738
  function neatHome2() {
8413
8739
  const override = process.env.NEAT_HOME;
8414
- if (override && override.length > 0) return import_node_path46.default.resolve(override);
8415
- 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");
8416
8742
  }
8417
8743
  function connectorsConfigPath(home = neatHome2()) {
8418
- return import_node_path46.default.join(home, "connectors.json");
8744
+ return import_node_path47.default.join(home, "connectors.json");
8419
8745
  }
8420
8746
  var MODE_MASK_LOOSER_THAN_0600 = 63;
8421
8747
  async function warnIfModeLooserThan0600(file) {
@@ -8714,11 +9040,11 @@ function registerRoutes(scope, ctx) {
8714
9040
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8715
9041
  const parsed = [];
8716
9042
  for (const c of candidates) {
8717
- const r = import_types33.DivergenceTypeSchema.safeParse(c);
9043
+ const r = import_types34.DivergenceTypeSchema.safeParse(c);
8718
9044
  if (!r.success) {
8719
9045
  return reply.code(400).send({
8720
9046
  error: `unknown divergence type "${c}"`,
8721
- allowed: import_types33.DivergenceTypeSchema.options
9047
+ allowed: import_types34.DivergenceTypeSchema.options
8722
9048
  });
8723
9049
  }
8724
9050
  parsed.push(r.data);
@@ -8989,7 +9315,7 @@ function registerRoutes(scope, ctx) {
8989
9315
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
8990
9316
  let violations = await log.readAll();
8991
9317
  if (req.query.severity) {
8992
- const sev = import_types33.PolicySeveritySchema.safeParse(req.query.severity);
9318
+ const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
8993
9319
  if (!sev.success) {
8994
9320
  return reply.code(400).send({
8995
9321
  error: "invalid severity",
@@ -9028,7 +9354,7 @@ function registerRoutes(scope, ctx) {
9028
9354
  scope.post("/policies/check", async (req, reply) => {
9029
9355
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
9030
9356
  if (!proj) return;
9031
- const parsed = import_types33.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9357
+ const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9032
9358
  if (!parsed.success) {
9033
9359
  return reply.code(400).send({
9034
9360
  error: "invalid /policies/check body",
@@ -9294,7 +9620,7 @@ init_otel_grpc();
9294
9620
  // src/search.ts
9295
9621
  init_cjs_shims();
9296
9622
  var import_node_fs30 = require("fs");
9297
- var import_node_path49 = __toESM(require("path"), 1);
9623
+ var import_node_path50 = __toESM(require("path"), 1);
9298
9624
  var import_node_crypto3 = require("crypto");
9299
9625
  var DEFAULT_LIMIT = 10;
9300
9626
  var NOMIC_DIM = 768;
@@ -9457,7 +9783,7 @@ async function readCache(cachePath) {
9457
9783
  }
9458
9784
  }
9459
9785
  async function writeCache(cachePath, cache) {
9460
- 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 });
9461
9787
  await import_node_fs30.promises.writeFile(cachePath, JSON.stringify(cache));
9462
9788
  }
9463
9789
  var VectorIndex = class {
@@ -9638,14 +9964,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
9638
9964
  async function main() {
9639
9965
  const baseDirEnv = process.env.NEAT_OUT_DIR;
9640
9966
  const legacyOutPath = process.env.NEAT_OUT_PATH;
9641
- 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");
9642
- 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");
9643
9969
  const registry = new Projects();
9644
9970
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
9645
9971
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
9646
9972
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
9647
9973
  const projectScan = process.env[envKey];
9648
- 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);
9649
9975
  }
9650
9976
  const host = process.env.HOST ?? "0.0.0.0";
9651
9977
  const port = Number(process.env.PORT ?? 8080);