@neat.is/core 0.6.1 → 0.6.2-dev.20260723

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/neatd.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req2, reply, done) => {
64
- const path54 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path54) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path54)) {
64
+ const path55 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path55) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path55)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
- return import_node_path48.default.resolve(here, "..", "proto");
197
+ const here = import_node_path49.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
+ return import_node_path49.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url, import_node_path49, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url = require("url");
272
- import_node_path48 = __toESM(require("path"), 1);
272
+ import_node_path49 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -347,6 +347,14 @@ function normalizeDbSystem(attrs) {
347
347
  if (typeof raw !== "string") return void 0;
348
348
  return raw === "mongoose" ? "mongodb" : raw;
349
349
  }
350
+ function tableFromSqlStatement(sql) {
351
+ if (typeof sql !== "string" || sql.length === 0) return null;
352
+ if (/\bjoin\b/i.test(sql)) return null;
353
+ const froms = sql.match(/\bfrom\b/gi);
354
+ if (froms && froms.length > 1) return null;
355
+ const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
356
+ return m ? m[1] : null;
357
+ }
350
358
  function messagingDestinationOf(attrs) {
351
359
  for (const key of ["messaging.destination.name", "messaging.destination"]) {
352
360
  const v = attrs[key];
@@ -367,8 +375,8 @@ function websocketChannelPathOf(attrs) {
367
375
  const v = attrs[key];
368
376
  if (typeof v === "string" && v.length > 0) {
369
377
  const q = v.indexOf("?");
370
- const path54 = q === -1 ? v : v.slice(0, q);
371
- if (path54.length > 0) return path54;
378
+ const path55 = q === -1 ? v : v.slice(0, q);
379
+ if (path55.length > 0) return path55;
372
380
  }
373
381
  }
374
382
  return void 0;
@@ -404,6 +412,7 @@ function parseOtlpRequest(body) {
404
412
  dbSystem: normalizeDbSystem(attrs),
405
413
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
406
414
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
415
+ dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
407
416
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
408
417
  messagingDestination: messagingDestinationOf(attrs),
409
418
  graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
@@ -423,10 +432,10 @@ function parseOtlpRequest(body) {
423
432
  return out;
424
433
  }
425
434
  function loadProtoRoot() {
426
- const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
427
- const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
435
+ const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
436
+ const protoRoot = import_node_path50.default.resolve(here, "..", "proto");
428
437
  const root = new import_protobufjs.default.Root();
429
- root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
438
+ root.resolvePath = (_origin, target) => import_node_path50.default.resolve(protoRoot, target);
430
439
  root.loadSync(
431
440
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
432
441
  { keepCase: true }
@@ -654,12 +663,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
654
663
  }
655
664
  }
656
665
  }
657
- var import_node_path49, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
666
+ var import_node_path50, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
658
667
  var init_otel = __esm({
659
668
  "src/otel.ts"() {
660
669
  "use strict";
661
670
  init_cjs_shims();
662
- import_node_path49 = __toESM(require("path"), 1);
671
+ import_node_path50 = __toESM(require("path"), 1);
663
672
  import_node_url2 = require("url");
664
673
  import_fastify2 = __toESM(require("fastify"), 1);
665
674
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -683,13 +692,13 @@ __export(neatd_exports, {
683
692
  module.exports = __toCommonJS(neatd_exports);
684
693
  init_cjs_shims();
685
694
  var import_node_fs33 = require("fs");
686
- var import_node_path53 = __toESM(require("path"), 1);
695
+ var import_node_path54 = __toESM(require("path"), 1);
687
696
  var import_node_module2 = require("module");
688
697
 
689
698
  // src/daemon.ts
690
699
  init_cjs_shims();
691
700
  var import_node_fs31 = require("fs");
692
- var import_node_path51 = __toESM(require("path"), 1);
701
+ var import_node_path52 = __toESM(require("path"), 1);
693
702
  var import_node_module = require("module");
694
703
 
695
704
  // src/graph.ts
@@ -1219,19 +1228,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1219
1228
  function longestIncomingWalk(graph, start, maxDepth) {
1220
1229
  let best = { path: [start], edges: [] };
1221
1230
  const visited = /* @__PURE__ */ new Set([start]);
1222
- function step(node, path54, edges) {
1223
- if (path54.length > best.path.length) {
1224
- best = { path: [...path54], edges: [...edges] };
1231
+ function step(node, path55, edges) {
1232
+ if (path55.length > best.path.length) {
1233
+ best = { path: [...path55], edges: [...edges] };
1225
1234
  }
1226
- if (path54.length - 1 >= maxDepth) return;
1235
+ if (path55.length - 1 >= maxDepth) return;
1227
1236
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1228
1237
  for (const [srcId, edge] of incoming) {
1229
1238
  if (visited.has(srcId)) continue;
1230
1239
  visited.add(srcId);
1231
- path54.push(srcId);
1240
+ path55.push(srcId);
1232
1241
  edges.push(edge);
1233
- step(srcId, path54, edges);
1234
- path54.pop();
1242
+ step(srcId, path55, edges);
1243
+ path55.pop();
1235
1244
  edges.pop();
1236
1245
  visited.delete(srcId);
1237
1246
  }
@@ -1239,11 +1248,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1239
1248
  step(start, [start], []);
1240
1249
  return best;
1241
1250
  }
1242
- function databaseRootCauseShape(graph, origin, walk3) {
1251
+ function databaseRootCauseShape(graph, origin, walk4) {
1243
1252
  const targetDb = origin;
1244
1253
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1245
1254
  if (candidatePairs.length === 0) return null;
1246
- for (const id of walk3.path) {
1255
+ for (const id of walk4.path) {
1247
1256
  const owner = resolveOwningService(graph, id);
1248
1257
  if (!owner) continue;
1249
1258
  const { id: serviceId6, svc } = owner;
@@ -1270,8 +1279,8 @@ function databaseRootCauseShape(graph, origin, walk3) {
1270
1279
  }
1271
1280
  return null;
1272
1281
  }
1273
- function serviceRootCauseShape(graph, _origin, walk3) {
1274
- for (const id of walk3.path) {
1282
+ function serviceRootCauseShape(graph, _origin, walk4) {
1283
+ for (const id of walk4.path) {
1275
1284
  const owner = resolveOwningService(graph, id);
1276
1285
  if (!owner) continue;
1277
1286
  const { id: serviceId6, svc } = owner;
@@ -1307,10 +1316,10 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1307
1316
  }
1308
1317
  return null;
1309
1318
  }
1310
- function fileRootCauseShape(graph, origin, walk3) {
1319
+ function fileRootCauseShape(graph, origin, walk4) {
1311
1320
  const owner = resolveOwningService(graph, origin.id);
1312
1321
  if (!owner) return null;
1313
- return serviceRootCauseShape(graph, owner.svc, walk3);
1322
+ return serviceRootCauseShape(graph, owner.svc, walk4);
1314
1323
  }
1315
1324
  var rootCauseShapes = {
1316
1325
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1322,16 +1331,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1322
1331
  const origin = graph.getNodeAttributes(errorNodeId);
1323
1332
  const shape = rootCauseShapes[origin.type];
1324
1333
  if (shape) {
1325
- const walk3 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1326
- const match = shape(graph, origin, walk3);
1334
+ const walk4 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1335
+ const match = shape(graph, origin, walk4);
1327
1336
  if (match) {
1328
1337
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1329
1338
  return import_types.RootCauseResultSchema.parse({
1330
1339
  rootCauseNode: match.rootCauseNode,
1331
1340
  rootCauseReason: reason,
1332
- traversalPath: walk3.path,
1333
- edgeProvenances: walk3.edges.map((e) => e.provenance),
1334
- confidence: confidenceFromMix(walk3.edges),
1341
+ traversalPath: walk4.path,
1342
+ edgeProvenances: walk4.edges.map((e) => e.provenance),
1343
+ confidence: confidenceFromMix(walk4.edges),
1335
1344
  fixRecommendation: match.fixRecommendation
1336
1345
  });
1337
1346
  }
@@ -1352,8 +1361,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
1352
1361
  if (relevant.length === 0) return null;
1353
1362
  const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
1354
1363
  const attrs = latest.attributes ?? {};
1355
- const filepath = typeof attrs["code.filepath"] === "string" ? attrs["code.filepath"] : void 0;
1356
- const lineno = typeof attrs["code.lineno"] === "number" ? attrs["code.lineno"] : void 0;
1364
+ const filepath = codeFilepathOf(attrs);
1365
+ const lineno = codeLinenoOf(attrs);
1357
1366
  const route = typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0;
1358
1367
  const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
1359
1368
  const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage);
@@ -1425,26 +1434,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
1425
1434
  return best;
1426
1435
  }
1427
1436
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1428
- const path54 = [originServiceId];
1437
+ const path55 = [originServiceId];
1429
1438
  const edges = [];
1430
1439
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1431
1440
  let current = originServiceId;
1432
1441
  for (let depth = 0; depth < maxDepth; depth++) {
1433
1442
  const hop = dominantFailingCall(graph, current, visited);
1434
1443
  if (!hop) break;
1435
- path54.push(hop.nextService);
1444
+ path55.push(hop.nextService);
1436
1445
  edges.push(hop.edge);
1437
1446
  visited.add(hop.nextService);
1438
1447
  current = hop.nextService;
1439
1448
  }
1440
1449
  if (edges.length === 0) return null;
1441
- return { path: path54, edges, culprit: current };
1450
+ return { path: path55, edges, culprit: current };
1442
1451
  }
1443
1452
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1444
1453
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1445
1454
  if (!chain) return null;
1446
1455
  const culprit = chain.culprit;
1447
- const path54 = [...chain.path];
1456
+ const path55 = [...chain.path];
1448
1457
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1449
1458
  const baseConfidence = confidenceFromMix(chain.edges);
1450
1459
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1452,14 +1461,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1452
1461
  if (loc) {
1453
1462
  let rootCauseNode = culprit;
1454
1463
  if (loc.fileNode) {
1455
- path54.push(loc.fileNode);
1464
+ path55.push(loc.fileNode);
1456
1465
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1457
1466
  rootCauseNode = loc.fileNode;
1458
1467
  }
1459
1468
  return import_types.RootCauseResultSchema.parse({
1460
1469
  rootCauseNode,
1461
1470
  rootCauseReason: loc.rootCauseReason,
1462
- traversalPath: path54,
1471
+ traversalPath: path55,
1463
1472
  edgeProvenances,
1464
1473
  confidence,
1465
1474
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1471,7 +1480,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1471
1480
  return import_types.RootCauseResultSchema.parse({
1472
1481
  rootCauseNode: culprit,
1473
1482
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1474
- traversalPath: path54,
1483
+ traversalPath: path55,
1475
1484
  edgeProvenances,
1476
1485
  confidence,
1477
1486
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2229,9 +2238,30 @@ function isLoopbackHost(host) {
2229
2238
  const h = host.toLowerCase();
2230
2239
  return h === "localhost" || h === "ip6-localhost" || h === "::1" || h === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(h);
2231
2240
  }
2241
+ var CODE_FILE_PATH_ATTR = "code.file.path";
2232
2242
  var CODE_FILEPATH_ATTR = "code.filepath";
2243
+ var CODE_LINE_NUMBER_ATTR = "code.line.number";
2233
2244
  var CODE_LINENO_ATTR = "code.lineno";
2245
+ var CODE_FUNCTION_NAME_ATTR = "code.function.name";
2234
2246
  var CODE_FUNCTION_ATTR = "code.function";
2247
+ function codeFilepathOf(attrs) {
2248
+ const stable = attrs[CODE_FILE_PATH_ATTR];
2249
+ if (typeof stable === "string" && stable.length > 0) return stable;
2250
+ const prior = attrs[CODE_FILEPATH_ATTR];
2251
+ return typeof prior === "string" && prior.length > 0 ? prior : void 0;
2252
+ }
2253
+ function codeLinenoOf(attrs) {
2254
+ const stable = attrs[CODE_LINE_NUMBER_ATTR];
2255
+ if (typeof stable === "number" && Number.isFinite(stable)) return stable;
2256
+ const prior = attrs[CODE_LINENO_ATTR];
2257
+ return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
2258
+ }
2259
+ function codeFunctionOf(attrs) {
2260
+ const stable = attrs[CODE_FUNCTION_NAME_ATTR];
2261
+ if (typeof stable === "string" && stable.length > 0) return stable;
2262
+ const prior = attrs[CODE_FUNCTION_ATTR];
2263
+ return typeof prior === "string" && prior.length > 0 ? prior : void 0;
2264
+ }
2235
2265
  function toPosix(p) {
2236
2266
  return p.split("\\").join("/");
2237
2267
  }
@@ -2309,10 +2339,9 @@ function resolveDistToSrc(absFilepath, line) {
2309
2339
  }
2310
2340
  }
2311
2341
  function callSiteFromSpan(span, serviceNode, scanPath) {
2312
- const filepath = span.attributes[CODE_FILEPATH_ATTR];
2313
- if (typeof filepath !== "string" || filepath.length === 0) return null;
2314
- const linenoRaw = span.attributes[CODE_LINENO_ATTR];
2315
- let line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
2342
+ const filepath = codeFilepathOf(span.attributes);
2343
+ if (filepath === void 0) return null;
2344
+ let line = codeLinenoOf(span.attributes);
2316
2345
  const abs = toPosix(filepath).replace(/^file:\/\//, "");
2317
2346
  const resolved = resolveDistToSrc(abs, line);
2318
2347
  let effectivePath = filepath;
@@ -2327,8 +2356,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
2327
2356
  if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
2328
2357
  warnNoSourceMaps(serviceNode.name);
2329
2358
  }
2330
- const fnRaw = span.attributes[CODE_FUNCTION_ATTR];
2331
- const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
2359
+ const fn = codeFunctionOf(span.attributes);
2332
2360
  return {
2333
2361
  relPath,
2334
2362
  ...line !== void 0 ? { line } : {},
@@ -2910,6 +2938,18 @@ async function handleSpan(ctx, span) {
2910
2938
  callSiteEvidence
2911
2939
  );
2912
2940
  }
2941
+ if (span.dbTable) {
2942
+ const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
2943
+ upsertObservedEdge(
2944
+ ctx.graph,
2945
+ import_types3.EdgeType.CALLS,
2946
+ observedSource(),
2947
+ tableId,
2948
+ ts,
2949
+ isError,
2950
+ callSiteEvidence
2951
+ );
2952
+ }
2913
2953
  }
2914
2954
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
2915
2955
  const targetId = ensureMessagingDestinationNode(
@@ -4074,14 +4114,14 @@ function buildServiceHostIndex(services) {
4074
4114
  }
4075
4115
  async function walkSourceFiles(dir) {
4076
4116
  const out = [];
4077
- async function walk3(current) {
4117
+ async function walk4(current) {
4078
4118
  const entries = await import_node_fs10.promises.readdir(current, { withFileTypes: true }).catch(() => []);
4079
4119
  for (const entry2 of entries) {
4080
4120
  const full = import_node_path10.default.join(current, entry2.name);
4081
4121
  if (entry2.isDirectory()) {
4082
4122
  if (IGNORED_DIRS.has(entry2.name)) continue;
4083
4123
  if (await isPythonVenvDir(full)) continue;
4084
- await walk3(full);
4124
+ await walk4(full);
4085
4125
  } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path10.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
4086
4126
  // would attribute our instrumentation imports to the user's service.
4087
4127
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -4089,7 +4129,7 @@ async function walkSourceFiles(dir) {
4089
4129
  }
4090
4130
  }
4091
4131
  }
4092
- await walk3(dir);
4132
+ await walk4(dir);
4093
4133
  return out;
4094
4134
  }
4095
4135
  async function loadSourceFiles(dir) {
@@ -5239,20 +5279,20 @@ var import_node_path21 = __toESM(require("path"), 1);
5239
5279
  var import_types10 = require("@neat.is/types");
5240
5280
  async function walkConfigFiles(dir) {
5241
5281
  const out = [];
5242
- async function walk3(current) {
5282
+ async function walk4(current) {
5243
5283
  const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
5244
5284
  for (const entry2 of entries) {
5245
5285
  const full = import_node_path21.default.join(current, entry2.name);
5246
5286
  if (entry2.isDirectory()) {
5247
5287
  if (IGNORED_DIRS.has(entry2.name)) continue;
5248
5288
  if (await isPythonVenvDir(full)) continue;
5249
- await walk3(full);
5289
+ await walk4(full);
5250
5290
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
5251
5291
  out.push(full);
5252
5292
  }
5253
5293
  }
5254
5294
  }
5255
- await walk3(dir);
5295
+ await walk4(dir);
5256
5296
  return out;
5257
5297
  }
5258
5298
  async function addConfigNodes(graph, services, scanPath) {
@@ -5779,20 +5819,20 @@ function grpcMethodsFromProto(content, fqPackage) {
5779
5819
  }
5780
5820
  async function walkProtoFiles(dir) {
5781
5821
  const out = [];
5782
- async function walk3(current) {
5822
+ async function walk4(current) {
5783
5823
  const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true }).catch(() => []);
5784
5824
  for (const entry2 of entries) {
5785
5825
  const full = import_node_path23.default.join(current, entry2.name);
5786
5826
  if (entry2.isDirectory()) {
5787
5827
  if (IGNORED_DIRS.has(entry2.name)) continue;
5788
5828
  if (await isPythonVenvDir(full)) continue;
5789
- await walk3(full);
5829
+ await walk4(full);
5790
5830
  } else if (entry2.isFile() && import_node_path23.default.extname(entry2.name) === PROTO_EXTENSION) {
5791
5831
  out.push(full);
5792
5832
  }
5793
5833
  }
5794
5834
  }
5795
- await walk3(dir);
5835
+ await walk4(dir);
5796
5836
  return out;
5797
5837
  }
5798
5838
  async function addGrpcMethods(graph, services) {
@@ -5860,7 +5900,7 @@ async function addGrpcMethods(graph, services) {
5860
5900
 
5861
5901
  // src/extract/calls/index.ts
5862
5902
  init_cjs_shims();
5863
- var import_types21 = require("@neat.is/types");
5903
+ var import_types22 = require("@neat.is/types");
5864
5904
 
5865
5905
  // src/extract/calls/http.ts
5866
5906
  init_cjs_shims();
@@ -6869,15 +6909,130 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
6869
6909
  return out;
6870
6910
  }
6871
6911
 
6912
+ // src/extract/calls/sqlalchemy.ts
6913
+ init_cjs_shims();
6914
+ var import_node_path32 = __toESM(require("path"), 1);
6915
+ var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
6916
+ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
6917
+ var import_types21 = require("@neat.is/types");
6918
+ var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
6919
+ var PARSE_CHUNK5 = 16384;
6920
+ function makePyParser4() {
6921
+ const p = new import_tree_sitter5.default();
6922
+ p.setLanguage(import_tree_sitter_python4.default);
6923
+ return p;
6924
+ }
6925
+ function parseSource5(parser, source) {
6926
+ return parser.parse(
6927
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
6928
+ );
6929
+ }
6930
+ function flaskSqlalchemyTableName(className) {
6931
+ return className.replace(/((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g, "_$1").toLowerCase().replace(/^_+/, "");
6932
+ }
6933
+ function namedChildren(node) {
6934
+ const out = [];
6935
+ for (let i = 0; i < node.namedChildCount; i++) {
6936
+ const c = node.namedChild(i);
6937
+ if (c) out.push(c);
6938
+ }
6939
+ return out;
6940
+ }
6941
+ function pyStaticStringText2(node) {
6942
+ if (node.type !== "string") return null;
6943
+ for (const child of namedChildren(node)) {
6944
+ if (child.type === "interpolation") return null;
6945
+ if (child.type === "string_content") return child.text;
6946
+ }
6947
+ return "";
6948
+ }
6949
+ function explicitTablename(body) {
6950
+ for (const stmt of namedChildren(body)) {
6951
+ if (stmt.type !== "expression_statement") continue;
6952
+ const assign = stmt.namedChild(0);
6953
+ if (assign?.type !== "assignment") continue;
6954
+ if (assign.childForFieldName("left")?.text !== "__tablename__") continue;
6955
+ const right = assign.childForFieldName("right");
6956
+ if (right?.type === "string") {
6957
+ const s = pyStaticStringText2(right);
6958
+ return s ? { name: s } : "computed";
6959
+ }
6960
+ return "computed";
6961
+ }
6962
+ return null;
6963
+ }
6964
+ function extendsFlaskModel(cls) {
6965
+ const supers = cls.childForFieldName("superclasses");
6966
+ if (!supers) return false;
6967
+ for (const a of namedChildren(supers)) {
6968
+ const t = a.text;
6969
+ if (t === "db.Model" || t === "Model" || t.endsWith(".Model")) return true;
6970
+ }
6971
+ return false;
6972
+ }
6973
+ function walk3(node, visit) {
6974
+ visit(node);
6975
+ for (const c of namedChildren(node)) walk3(c, visit);
6976
+ }
6977
+ function sqlalchemyEndpointsFromFile(file, serviceDir) {
6978
+ if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
6979
+ const tree = parseSource5(makePyParser4(), file.content);
6980
+ const out = [];
6981
+ const seen = /* @__PURE__ */ new Set();
6982
+ const push = (name, line) => {
6983
+ if (seen.has(name)) return;
6984
+ seen.add(name);
6985
+ out.push({
6986
+ infraId: (0, import_types21.infraId)("sql-table", name),
6987
+ name,
6988
+ kind: "sql-table",
6989
+ edgeType: "CALLS",
6990
+ confidenceKind: "verified-call-site",
6991
+ evidence: {
6992
+ file: import_node_path32.default.relative(serviceDir, file.path),
6993
+ line,
6994
+ snippet: snippet(file.content, line)
6995
+ }
6996
+ });
6997
+ };
6998
+ walk3(tree.rootNode, (node) => {
6999
+ if (node.type === "class_definition") {
7000
+ const body = node.childForFieldName("body");
7001
+ const nameNode = node.childForFieldName("name");
7002
+ if (!body || !nameNode) return;
7003
+ const line = node.startPosition.row + 1;
7004
+ const explicit = explicitTablename(body);
7005
+ if (explicit === "computed") return;
7006
+ if (explicit) {
7007
+ push(explicit.name, line);
7008
+ return;
7009
+ }
7010
+ if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
7011
+ return;
7012
+ }
7013
+ if (node.type === "call") {
7014
+ const fn = node.childForFieldName("function");
7015
+ const fnText = fn?.text;
7016
+ if (fnText !== "Table" && fnText !== "sa.Table" && fnText !== "sqlalchemy.Table") return;
7017
+ const first = node.childForFieldName("arguments")?.namedChild(0);
7018
+ if (first?.type === "string") {
7019
+ const s = pyStaticStringText2(first);
7020
+ if (s) push(s, node.startPosition.row + 1);
7021
+ }
7022
+ }
7023
+ });
7024
+ return out;
7025
+ }
7026
+
6872
7027
  // src/extract/calls/index.ts
6873
7028
  function edgeTypeFromEndpoint(ep) {
6874
7029
  switch (ep.edgeType) {
6875
7030
  case "PUBLISHES_TO":
6876
- return import_types21.EdgeType.PUBLISHES_TO;
7031
+ return import_types22.EdgeType.PUBLISHES_TO;
6877
7032
  case "CONSUMES_FROM":
6878
- return import_types21.EdgeType.CONSUMES_FROM;
7033
+ return import_types22.EdgeType.CONSUMES_FROM;
6879
7034
  default:
6880
- return import_types21.EdgeType.CALLS;
7035
+ return import_types22.EdgeType.CALLS;
6881
7036
  }
6882
7037
  }
6883
7038
  function isAwsKind(kind) {
@@ -6901,6 +7056,7 @@ async function addExternalEndpointEdges(graph, services) {
6901
7056
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
6902
7057
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
6903
7058
  endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
7059
+ endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
6904
7060
  }
6905
7061
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
6906
7062
  if (endpoints.length === 0) continue;
@@ -6909,7 +7065,7 @@ async function addExternalEndpointEdges(graph, services) {
6909
7065
  if (!graph.hasNode(ep.infraId)) {
6910
7066
  const node = {
6911
7067
  id: ep.infraId,
6912
- type: import_types21.NodeType.InfraNode,
7068
+ type: import_types22.NodeType.InfraNode,
6913
7069
  name: ep.name,
6914
7070
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
6915
7071
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -6921,7 +7077,7 @@ async function addExternalEndpointEdges(graph, services) {
6921
7077
  nodesAdded++;
6922
7078
  }
6923
7079
  const edgeType = edgeTypeFromEndpoint(ep);
6924
- const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
7080
+ const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
6925
7081
  const relFile = toPosix2(ep.evidence.file);
6926
7082
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
6927
7083
  graph,
@@ -6931,7 +7087,7 @@ async function addExternalEndpointEdges(graph, services) {
6931
7087
  );
6932
7088
  nodesAdded += n;
6933
7089
  edgesAdded += e;
6934
- if (!(0, import_types21.passesExtractedFloor)(confidence)) {
7090
+ if (!(0, import_types22.passesExtractedFloor)(confidence)) {
6935
7091
  noteExtractedDropped({
6936
7092
  source: fileNodeId,
6937
7093
  target: ep.infraId,
@@ -6951,7 +7107,7 @@ async function addExternalEndpointEdges(graph, services) {
6951
7107
  source: fileNodeId,
6952
7108
  target: ep.infraId,
6953
7109
  type: edgeType,
6954
- provenance: import_types21.Provenance.EXTRACTED,
7110
+ provenance: import_types22.Provenance.EXTRACTED,
6955
7111
  confidence,
6956
7112
  evidence: ep.evidence
6957
7113
  };
@@ -6977,16 +7133,16 @@ init_cjs_shims();
6977
7133
 
6978
7134
  // src/extract/infra/docker-compose.ts
6979
7135
  init_cjs_shims();
6980
- var import_node_path32 = __toESM(require("path"), 1);
6981
- var import_types23 = require("@neat.is/types");
7136
+ var import_node_path33 = __toESM(require("path"), 1);
7137
+ var import_types24 = require("@neat.is/types");
6982
7138
 
6983
7139
  // src/extract/infra/shared.ts
6984
7140
  init_cjs_shims();
6985
- var import_types22 = require("@neat.is/types");
7141
+ var import_types23 = require("@neat.is/types");
6986
7142
  function makeInfraNode(kind, name, provider = "self", extras) {
6987
7143
  return {
6988
- id: (0, import_types22.infraId)(kind, name),
6989
- type: import_types22.NodeType.InfraNode,
7144
+ id: (0, import_types23.infraId)(kind, name),
7145
+ type: import_types23.NodeType.InfraNode,
6990
7146
  name,
6991
7147
  provider,
6992
7148
  kind,
@@ -7030,8 +7186,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
7030
7186
  source: anchorId,
7031
7187
  target: node.id,
7032
7188
  type: edgeType,
7033
- provenance: import_types22.Provenance.EXTRACTED,
7034
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7189
+ provenance: import_types23.Provenance.EXTRACTED,
7190
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
7035
7191
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7036
7192
  };
7037
7193
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7048,7 +7204,7 @@ function dependsOnList(value) {
7048
7204
  }
7049
7205
  function serviceNameToServiceNode(name, services) {
7050
7206
  for (const s of services) {
7051
- if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
7207
+ if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
7052
7208
  }
7053
7209
  return null;
7054
7210
  }
@@ -7057,7 +7213,7 @@ async function addComposeInfra(graph, scanPath, services) {
7057
7213
  let edgesAdded = 0;
7058
7214
  let composePath = null;
7059
7215
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
7060
- const abs = import_node_path32.default.join(scanPath, name);
7216
+ const abs = import_node_path33.default.join(scanPath, name);
7061
7217
  if (await exists(abs)) {
7062
7218
  composePath = abs;
7063
7219
  break;
@@ -7070,13 +7226,13 @@ async function addComposeInfra(graph, scanPath, services) {
7070
7226
  } catch (err) {
7071
7227
  recordExtractionError(
7072
7228
  "infra docker-compose",
7073
- import_node_path32.default.relative(scanPath, composePath),
7229
+ import_node_path33.default.relative(scanPath, composePath),
7074
7230
  err
7075
7231
  );
7076
7232
  return { nodesAdded, edgesAdded };
7077
7233
  }
7078
7234
  if (!compose?.services) return { nodesAdded, edgesAdded };
7079
- const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
7235
+ const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
7080
7236
  const composeNameToNodeId = /* @__PURE__ */ new Map();
7081
7237
  for (const [composeName, svc] of Object.entries(compose.services)) {
7082
7238
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -7098,15 +7254,15 @@ async function addComposeInfra(graph, scanPath, services) {
7098
7254
  for (const dep of dependsOnList(svc.depends_on)) {
7099
7255
  const targetId = composeNameToNodeId.get(dep);
7100
7256
  if (!targetId) continue;
7101
- const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
7257
+ const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
7102
7258
  if (graph.hasEdge(edgeId)) continue;
7103
7259
  const edge = {
7104
7260
  id: edgeId,
7105
7261
  source: sourceId,
7106
7262
  target: targetId,
7107
- type: import_types23.EdgeType.DEPENDS_ON,
7108
- provenance: import_types23.Provenance.EXTRACTED,
7109
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7263
+ type: import_types24.EdgeType.DEPENDS_ON,
7264
+ provenance: import_types24.Provenance.EXTRACTED,
7265
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
7110
7266
  evidence: { file: evidenceFile }
7111
7267
  };
7112
7268
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7118,9 +7274,9 @@ async function addComposeInfra(graph, scanPath, services) {
7118
7274
 
7119
7275
  // src/extract/infra/dockerfile.ts
7120
7276
  init_cjs_shims();
7121
- var import_node_path33 = __toESM(require("path"), 1);
7277
+ var import_node_path34 = __toESM(require("path"), 1);
7122
7278
  var import_node_fs16 = require("fs");
7123
- var import_types24 = require("@neat.is/types");
7279
+ var import_types25 = require("@neat.is/types");
7124
7280
  function readDockerfile(content) {
7125
7281
  let image = null;
7126
7282
  const ports = [];
@@ -7149,7 +7305,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7149
7305
  let nodesAdded = 0;
7150
7306
  let edgesAdded = 0;
7151
7307
  for (const service of services) {
7152
- const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
7308
+ const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
7153
7309
  if (!await exists(dockerfilePath)) continue;
7154
7310
  let content;
7155
7311
  try {
@@ -7157,7 +7313,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7157
7313
  } catch (err) {
7158
7314
  recordExtractionError(
7159
7315
  "infra dockerfile",
7160
- import_node_path33.default.relative(scanPath, dockerfilePath),
7316
+ import_node_path34.default.relative(scanPath, dockerfilePath),
7161
7317
  err
7162
7318
  );
7163
7319
  continue;
@@ -7169,8 +7325,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7169
7325
  graph.addNode(node.id, node);
7170
7326
  nodesAdded++;
7171
7327
  }
7172
- const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
7173
- const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
7328
+ const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
7329
+ const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
7174
7330
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7175
7331
  graph,
7176
7332
  service.pkg.name,
@@ -7179,15 +7335,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7179
7335
  );
7180
7336
  nodesAdded += fn;
7181
7337
  edgesAdded += fe;
7182
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
7338
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
7183
7339
  if (!graph.hasEdge(edgeId)) {
7184
7340
  const edge = {
7185
7341
  id: edgeId,
7186
7342
  source: fileNodeId,
7187
7343
  target: node.id,
7188
- type: import_types24.EdgeType.RUNS_ON,
7189
- provenance: import_types24.Provenance.EXTRACTED,
7190
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7344
+ type: import_types25.EdgeType.RUNS_ON,
7345
+ provenance: import_types25.Provenance.EXTRACTED,
7346
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7191
7347
  evidence: {
7192
7348
  file: evidenceFile,
7193
7349
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -7202,15 +7358,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7202
7358
  graph.addNode(portNode.id, portNode);
7203
7359
  nodesAdded++;
7204
7360
  }
7205
- const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
7361
+ const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
7206
7362
  if (graph.hasEdge(portEdgeId)) continue;
7207
7363
  const portEdge = {
7208
7364
  id: portEdgeId,
7209
7365
  source: fileNodeId,
7210
7366
  target: portNode.id,
7211
- type: import_types24.EdgeType.CONNECTS_TO,
7212
- provenance: import_types24.Provenance.EXTRACTED,
7213
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7367
+ type: import_types25.EdgeType.CONNECTS_TO,
7368
+ provenance: import_types25.Provenance.EXTRACTED,
7369
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7214
7370
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
7215
7371
  };
7216
7372
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -7223,8 +7379,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7223
7379
  // src/extract/infra/terraform.ts
7224
7380
  init_cjs_shims();
7225
7381
  var import_node_fs17 = require("fs");
7226
- var import_node_path34 = __toESM(require("path"), 1);
7227
- var import_types25 = require("@neat.is/types");
7382
+ var import_node_path35 = __toESM(require("path"), 1);
7383
+ var import_types26 = require("@neat.is/types");
7228
7384
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
7229
7385
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
7230
7386
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -7234,11 +7390,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
7234
7390
  for (const entry2 of entries) {
7235
7391
  if (entry2.isDirectory()) {
7236
7392
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
7237
- const child = import_node_path34.default.join(start, entry2.name);
7393
+ const child = import_node_path35.default.join(start, entry2.name);
7238
7394
  if (await isPythonVenvDir(child)) continue;
7239
7395
  out.push(...await walkTfFiles(child, depth + 1, max));
7240
7396
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
7241
- out.push(import_node_path34.default.join(start, entry2.name));
7397
+ out.push(import_node_path35.default.join(start, entry2.name));
7242
7398
  }
7243
7399
  }
7244
7400
  return out;
@@ -7270,7 +7426,7 @@ async function addTerraformResources(graph, scanPath) {
7270
7426
  const files = await walkTfFiles(scanPath);
7271
7427
  for (const file of files) {
7272
7428
  const content = await import_node_fs17.promises.readFile(file, "utf8");
7273
- const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
7429
+ const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
7274
7430
  const resources = [];
7275
7431
  const byKey = /* @__PURE__ */ new Map();
7276
7432
  RESOURCE_RE.lastIndex = 0;
@@ -7305,16 +7461,16 @@ async function addTerraformResources(graph, scanPath) {
7305
7461
  if (!target) continue;
7306
7462
  if (seen.has(target.nodeId)) continue;
7307
7463
  seen.add(target.nodeId);
7308
- const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
7464
+ const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
7309
7465
  if (graph.hasEdge(edgeId)) continue;
7310
7466
  const line = lineAt2(content, resource.bodyOffset + ref.index);
7311
7467
  const edge = {
7312
7468
  id: edgeId,
7313
7469
  source: resource.nodeId,
7314
7470
  target: target.nodeId,
7315
- type: import_types25.EdgeType.DEPENDS_ON,
7316
- provenance: import_types25.Provenance.EXTRACTED,
7317
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7471
+ type: import_types26.EdgeType.DEPENDS_ON,
7472
+ provenance: import_types26.Provenance.EXTRACTED,
7473
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7318
7474
  evidence: { file: evidenceFile, line, snippet: key }
7319
7475
  };
7320
7476
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7328,7 +7484,7 @@ async function addTerraformResources(graph, scanPath) {
7328
7484
  // src/extract/infra/k8s.ts
7329
7485
  init_cjs_shims();
7330
7486
  var import_node_fs18 = require("fs");
7331
- var import_node_path35 = __toESM(require("path"), 1);
7487
+ var import_node_path36 = __toESM(require("path"), 1);
7332
7488
  var import_yaml3 = require("yaml");
7333
7489
  var K8S_KIND_TO_INFRA_KIND = {
7334
7490
  Service: "k8s-service",
@@ -7346,11 +7502,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
7346
7502
  for (const entry2 of entries) {
7347
7503
  if (entry2.isDirectory()) {
7348
7504
  if (IGNORED_DIRS.has(entry2.name)) continue;
7349
- const child = import_node_path35.default.join(start, entry2.name);
7505
+ const child = import_node_path36.default.join(start, entry2.name);
7350
7506
  if (await isPythonVenvDir(child)) continue;
7351
7507
  out.push(...await walkYamlFiles2(child, depth + 1, max));
7352
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry2.name))) {
7353
- out.push(import_node_path35.default.join(start, entry2.name));
7508
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
7509
+ out.push(import_node_path36.default.join(start, entry2.name));
7354
7510
  }
7355
7511
  }
7356
7512
  return out;
@@ -7384,13 +7540,13 @@ async function addK8sResources(graph, scanPath) {
7384
7540
  // src/extract/infra/cloudflare.ts
7385
7541
  init_cjs_shims();
7386
7542
  var import_node_fs19 = require("fs");
7387
- var import_node_path36 = __toESM(require("path"), 1);
7543
+ var import_node_path37 = __toESM(require("path"), 1);
7388
7544
  var import_smol_toml2 = require("smol-toml");
7389
- var import_types26 = require("@neat.is/types");
7545
+ var import_types27 = require("@neat.is/types");
7390
7546
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7391
7547
  async function readWranglerConfig(dir) {
7392
7548
  for (const filename of WRANGLER_FILENAMES) {
7393
- const abs = import_node_path36.default.join(dir, filename);
7549
+ const abs = import_node_path37.default.join(dir, filename);
7394
7550
  if (!await exists(abs)) continue;
7395
7551
  const raw = await import_node_fs19.promises.readFile(abs, "utf8");
7396
7552
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7434,8 +7590,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7434
7590
  source: anchorId,
7435
7591
  target: node.id,
7436
7592
  type: edgeType,
7437
- provenance: import_types26.Provenance.EXTRACTED,
7438
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7593
+ provenance: import_types27.Provenance.EXTRACTED,
7594
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7439
7595
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7440
7596
  };
7441
7597
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7453,11 +7609,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7453
7609
  try {
7454
7610
  read = await readWranglerConfig(service.dir);
7455
7611
  } catch (err) {
7456
- recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
7612
+ recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
7457
7613
  continue;
7458
7614
  }
7459
7615
  if (!read || !read.config.name) continue;
7460
- const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
7616
+ const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
7461
7617
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7462
7618
  }
7463
7619
  for (const worker of discovered) {
@@ -7469,7 +7625,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7469
7625
  }
7470
7626
  let anchorId = service.node.id;
7471
7627
  if (config.main) {
7472
- const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
7628
+ const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
7473
7629
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7474
7630
  graph,
7475
7631
  service.pkg.name,
@@ -7496,15 +7652,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7496
7652
  nodesAdded++;
7497
7653
  }
7498
7654
  if (runtimeNode.id !== anchorId) {
7499
- const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
7655
+ const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
7500
7656
  if (!graph.hasEdge(runsOnId)) {
7501
7657
  const edge = {
7502
7658
  id: runsOnId,
7503
7659
  source: anchorId,
7504
7660
  target: runtimeNode.id,
7505
- type: import_types26.EdgeType.RUNS_ON,
7506
- provenance: import_types26.Provenance.EXTRACTED,
7507
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7661
+ type: import_types27.EdgeType.RUNS_ON,
7662
+ provenance: import_types27.Provenance.EXTRACTED,
7663
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7508
7664
  evidence: {
7509
7665
  file: evidenceFile,
7510
7666
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7518,7 +7674,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7518
7674
  const result = addResourceEdge(
7519
7675
  graph,
7520
7676
  anchorId,
7521
- import_types26.EdgeType.CONNECTS_TO,
7677
+ import_types27.EdgeType.CONNECTS_TO,
7522
7678
  "cloudflare-route",
7523
7679
  route,
7524
7680
  evidenceFile,
@@ -7542,7 +7698,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7542
7698
  const result = addResourceEdge(
7543
7699
  graph,
7544
7700
  anchorId,
7545
- import_types26.EdgeType.DEPENDS_ON,
7701
+ import_types27.EdgeType.DEPENDS_ON,
7546
7702
  group.kind,
7547
7703
  name,
7548
7704
  evidenceFile,
@@ -7556,7 +7712,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7556
7712
  const result = addResourceEdge(
7557
7713
  graph,
7558
7714
  anchorId,
7559
- import_types26.EdgeType.DEPENDS_ON,
7715
+ import_types27.EdgeType.DEPENDS_ON,
7560
7716
  "cloudflare-cron",
7561
7717
  cron,
7562
7718
  evidenceFile,
@@ -7569,7 +7725,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7569
7725
  const result = addResourceEdge(
7570
7726
  graph,
7571
7727
  anchorId,
7572
- import_types26.EdgeType.DEPENDS_ON,
7728
+ import_types27.EdgeType.DEPENDS_ON,
7573
7729
  "cloudflare-env-var",
7574
7730
  varName,
7575
7731
  evidenceFile,
@@ -7582,15 +7738,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7582
7738
  if (!svc.service) continue;
7583
7739
  const target = workerIndex.get(svc.service);
7584
7740
  if (target && target.anchorId !== anchorId) {
7585
- const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
7741
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
7586
7742
  if (!graph.hasEdge(edgeId)) {
7587
7743
  const edge = {
7588
7744
  id: edgeId,
7589
7745
  source: anchorId,
7590
7746
  target: target.anchorId,
7591
- type: import_types26.EdgeType.CALLS,
7592
- provenance: import_types26.Provenance.EXTRACTED,
7593
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7747
+ type: import_types27.EdgeType.CALLS,
7748
+ provenance: import_types27.Provenance.EXTRACTED,
7749
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7594
7750
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7595
7751
  };
7596
7752
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7601,7 +7757,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7601
7757
  const result = addResourceEdge(
7602
7758
  graph,
7603
7759
  anchorId,
7604
- import_types26.EdgeType.DEPENDS_ON,
7760
+ import_types27.EdgeType.DEPENDS_ON,
7605
7761
  "cloudflare-service-binding",
7606
7762
  svc.service,
7607
7763
  evidenceFile,
@@ -7617,12 +7773,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7617
7773
  // src/extract/infra/vercel.ts
7618
7774
  init_cjs_shims();
7619
7775
  var import_node_fs20 = require("fs");
7620
- var import_node_path37 = __toESM(require("path"), 1);
7621
- var import_types27 = require("@neat.is/types");
7776
+ var import_node_path38 = __toESM(require("path"), 1);
7777
+ var import_types28 = require("@neat.is/types");
7622
7778
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7623
7779
  async function readVercelConfig(dir) {
7624
7780
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7625
- const abs = import_node_path37.default.join(dir, filename);
7781
+ const abs = import_node_path38.default.join(dir, filename);
7626
7782
  if (!await exists(abs)) continue;
7627
7783
  const raw = await import_node_fs20.promises.readFile(abs, "utf8");
7628
7784
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7631,7 +7787,7 @@ async function readVercelConfig(dir) {
7631
7787
  return null;
7632
7788
  }
7633
7789
  async function readLinkedProjectName(dir) {
7634
- const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
7790
+ const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
7635
7791
  if (!await exists(abs)) return void 0;
7636
7792
  const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
7637
7793
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7649,7 +7805,7 @@ async function addVercelServices(graph, services, scanPath) {
7649
7805
  read = await readVercelConfig(service.dir);
7650
7806
  projectName = await readLinkedProjectName(service.dir);
7651
7807
  } catch (err) {
7652
- recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
7808
+ recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
7653
7809
  continue;
7654
7810
  }
7655
7811
  if (!read && !projectName) continue;
@@ -7665,7 +7821,7 @@ async function addVercelServices(graph, services, scanPath) {
7665
7821
  const anchorId = service.node.id;
7666
7822
  if (!read) continue;
7667
7823
  const { config, relFile, raw } = read;
7668
- const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
7824
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7669
7825
  const add = (edgeType, kind, name) => {
7670
7826
  if (!name) return;
7671
7827
  const result = emitPlatformResourceEdge(
@@ -7681,12 +7837,12 @@ async function addVercelServices(graph, services, scanPath) {
7681
7837
  nodesAdded += result.nodesAdded;
7682
7838
  edgesAdded += result.edgesAdded;
7683
7839
  };
7684
- add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7685
- for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7686
- for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7687
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7840
+ add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
7841
+ for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7842
+ for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7843
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7688
7844
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7689
- add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7845
+ add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7690
7846
  }
7691
7847
  }
7692
7848
  return { nodesAdded, edgesAdded };
@@ -7695,13 +7851,13 @@ async function addVercelServices(graph, services, scanPath) {
7695
7851
  // src/extract/infra/railway.ts
7696
7852
  init_cjs_shims();
7697
7853
  var import_node_fs21 = require("fs");
7698
- var import_node_path38 = __toESM(require("path"), 1);
7854
+ var import_node_path39 = __toESM(require("path"), 1);
7699
7855
  var import_smol_toml3 = require("smol-toml");
7700
- var import_types28 = require("@neat.is/types");
7856
+ var import_types29 = require("@neat.is/types");
7701
7857
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7702
7858
  async function readRailwayConfig(dir) {
7703
7859
  for (const filename of RAILWAY_FILENAMES) {
7704
- const abs = import_node_path38.default.join(dir, filename);
7860
+ const abs = import_node_path39.default.join(dir, filename);
7705
7861
  if (!await exists(abs)) continue;
7706
7862
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7707
7863
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7717,7 +7873,7 @@ async function addRailwayServices(graph, services, scanPath) {
7717
7873
  try {
7718
7874
  read = await readRailwayConfig(service.dir);
7719
7875
  } catch (err) {
7720
- recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
7876
+ recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
7721
7877
  continue;
7722
7878
  }
7723
7879
  if (!read) continue;
@@ -7727,7 +7883,7 @@ async function addRailwayServices(graph, services, scanPath) {
7727
7883
  }
7728
7884
  const anchorId = service.node.id;
7729
7885
  const { config, relFile, raw } = read;
7730
- const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7886
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7731
7887
  const add = (edgeType, kind, name) => {
7732
7888
  if (!name) return;
7733
7889
  const result = emitPlatformResourceEdge(
@@ -7743,9 +7899,9 @@ async function addRailwayServices(graph, services, scanPath) {
7743
7899
  nodesAdded += result.nodesAdded;
7744
7900
  edgesAdded += result.edgesAdded;
7745
7901
  };
7746
- add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7747
- add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7748
- add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7902
+ add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
7903
+ add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7904
+ add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7749
7905
  }
7750
7906
  return { nodesAdded, edgesAdded };
7751
7907
  }
@@ -7753,12 +7909,12 @@ async function addRailwayServices(graph, services, scanPath) {
7753
7909
  // src/extract/infra/supabase.ts
7754
7910
  init_cjs_shims();
7755
7911
  var import_node_fs22 = require("fs");
7756
- var import_node_path39 = __toESM(require("path"), 1);
7912
+ var import_node_path40 = __toESM(require("path"), 1);
7757
7913
  var import_smol_toml4 = require("smol-toml");
7758
- var import_types29 = require("@neat.is/types");
7914
+ var import_types30 = require("@neat.is/types");
7759
7915
  async function readSupabaseConfig(dir) {
7760
- const relFile = import_node_path39.default.join("supabase", "config.toml");
7761
- const abs = import_node_path39.default.join(dir, relFile);
7916
+ const relFile = import_node_path40.default.join("supabase", "config.toml");
7917
+ const abs = import_node_path40.default.join(dir, relFile);
7762
7918
  if (!await exists(abs)) return null;
7763
7919
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7764
7920
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7772,7 +7928,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7772
7928
  try {
7773
7929
  read = await readSupabaseConfig(service.dir);
7774
7930
  } catch (err) {
7775
- recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
7931
+ recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
7776
7932
  continue;
7777
7933
  }
7778
7934
  if (!read) continue;
@@ -7787,7 +7943,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7787
7943
  });
7788
7944
  }
7789
7945
  const anchorId = service.node.id;
7790
- const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7946
+ const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
7791
7947
  const add = (edgeType, kind, name) => {
7792
7948
  if (!name) return;
7793
7949
  const result = emitPlatformResourceEdge(
@@ -7803,10 +7959,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7803
7959
  nodesAdded += result.nodesAdded;
7804
7960
  edgesAdded += result.edgesAdded;
7805
7961
  };
7806
- add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7807
- for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7808
- if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7809
- if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7962
+ add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
7963
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
7964
+ if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7965
+ if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7810
7966
  }
7811
7967
  return { nodesAdded, edgesAdded };
7812
7968
  }
@@ -7828,17 +7984,17 @@ async function addInfra(graph, scanPath, services) {
7828
7984
  }
7829
7985
 
7830
7986
  // src/extract/index.ts
7831
- var import_node_path41 = __toESM(require("path"), 1);
7987
+ var import_node_path42 = __toESM(require("path"), 1);
7832
7988
 
7833
7989
  // src/extract/retire.ts
7834
7990
  init_cjs_shims();
7835
7991
  var import_node_fs23 = require("fs");
7836
- var import_node_path40 = __toESM(require("path"), 1);
7837
- var import_types30 = require("@neat.is/types");
7992
+ var import_node_path41 = __toESM(require("path"), 1);
7993
+ var import_types31 = require("@neat.is/types");
7838
7994
  function dropOrphanedFileNodes(graph) {
7839
7995
  const orphans = [];
7840
7996
  graph.forEachNode((id, attrs) => {
7841
- if (attrs.type !== import_types30.NodeType.FileNode) return;
7997
+ if (attrs.type !== import_types31.NodeType.FileNode) return;
7842
7998
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7843
7999
  orphans.push(id);
7844
8000
  }
@@ -7851,14 +8007,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7851
8007
  const bases = [scanPath, ...serviceDirs];
7852
8008
  graph.forEachEdge((id, attrs) => {
7853
8009
  const edge = attrs;
7854
- if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
8010
+ if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
7855
8011
  const evidenceFile = edge.evidence?.file;
7856
8012
  if (!evidenceFile) return;
7857
- if (import_node_path40.default.isAbsolute(evidenceFile)) {
8013
+ if (import_node_path41.default.isAbsolute(evidenceFile)) {
7858
8014
  if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
7859
8015
  return;
7860
8016
  }
7861
- const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path40.default.join(base, evidenceFile)));
8017
+ const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path41.default.join(base, evidenceFile)));
7862
8018
  if (!found) toDrop.push(id);
7863
8019
  });
7864
8020
  for (const id of toDrop) graph.dropEdge(id);
@@ -7900,7 +8056,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7900
8056
  }
7901
8057
  const droppedEntries = drainDroppedExtracted();
7902
8058
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7903
- const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
8059
+ const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
7904
8060
  try {
7905
8061
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7906
8062
  } catch (err) {
@@ -7935,8 +8091,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7935
8091
  // src/persist.ts
7936
8092
  init_cjs_shims();
7937
8093
  var import_node_fs24 = require("fs");
7938
- var import_node_path42 = __toESM(require("path"), 1);
7939
- var import_types31 = require("@neat.is/types");
8094
+ var import_node_path43 = __toESM(require("path"), 1);
8095
+ var import_types32 = require("@neat.is/types");
7940
8096
  var SCHEMA_VERSION = 4;
7941
8097
  function migrateV1ToV2(payload) {
7942
8098
  const nodes = payload.graph.nodes;
@@ -7958,12 +8114,12 @@ function migrateV2ToV3(payload) {
7958
8114
  for (const edge of edges) {
7959
8115
  const attrs = edge.attributes;
7960
8116
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
7961
- attrs.provenance = import_types31.Provenance.OBSERVED;
8117
+ attrs.provenance = import_types32.Provenance.OBSERVED;
7962
8118
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
7963
8119
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
7964
8120
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
7965
8121
  if (type && source && target) {
7966
- const newId = (0, import_types31.observedEdgeId)(source, target, type);
8122
+ const newId = (0, import_types32.observedEdgeId)(source, target, type);
7967
8123
  attrs.id = newId;
7968
8124
  if (edge.key) edge.key = newId;
7969
8125
  }
@@ -7972,7 +8128,7 @@ function migrateV2ToV3(payload) {
7972
8128
  return { ...payload, schemaVersion: 3 };
7973
8129
  }
7974
8130
  async function ensureDir(filePath) {
7975
- await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
8131
+ await import_node_fs24.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
7976
8132
  }
7977
8133
  async function saveGraphToDisk(graph, outPath) {
7978
8134
  await ensureDir(outPath);
@@ -8053,23 +8209,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
8053
8209
 
8054
8210
  // src/projects.ts
8055
8211
  init_cjs_shims();
8056
- var import_node_path43 = __toESM(require("path"), 1);
8212
+ var import_node_path44 = __toESM(require("path"), 1);
8057
8213
  function pathsForProject(project, baseDir) {
8058
8214
  if (project === DEFAULT_PROJECT) {
8059
8215
  return {
8060
- snapshotPath: import_node_path43.default.join(baseDir, "graph.json"),
8061
- errorsPath: import_node_path43.default.join(baseDir, "errors.ndjson"),
8062
- staleEventsPath: import_node_path43.default.join(baseDir, "stale-events.ndjson"),
8063
- embeddingsCachePath: import_node_path43.default.join(baseDir, "embeddings.json"),
8064
- policyViolationsPath: import_node_path43.default.join(baseDir, "policy-violations.ndjson")
8216
+ snapshotPath: import_node_path44.default.join(baseDir, "graph.json"),
8217
+ errorsPath: import_node_path44.default.join(baseDir, "errors.ndjson"),
8218
+ staleEventsPath: import_node_path44.default.join(baseDir, "stale-events.ndjson"),
8219
+ embeddingsCachePath: import_node_path44.default.join(baseDir, "embeddings.json"),
8220
+ policyViolationsPath: import_node_path44.default.join(baseDir, "policy-violations.ndjson")
8065
8221
  };
8066
8222
  }
8067
8223
  return {
8068
- snapshotPath: import_node_path43.default.join(baseDir, `${project}.json`),
8069
- errorsPath: import_node_path43.default.join(baseDir, `errors.${project}.ndjson`),
8070
- staleEventsPath: import_node_path43.default.join(baseDir, `stale-events.${project}.ndjson`),
8071
- embeddingsCachePath: import_node_path43.default.join(baseDir, `embeddings.${project}.json`),
8072
- policyViolationsPath: import_node_path43.default.join(baseDir, `policy-violations.${project}.ndjson`)
8224
+ snapshotPath: import_node_path44.default.join(baseDir, `${project}.json`),
8225
+ errorsPath: import_node_path44.default.join(baseDir, `errors.${project}.ndjson`),
8226
+ staleEventsPath: import_node_path44.default.join(baseDir, `stale-events.${project}.ndjson`),
8227
+ embeddingsCachePath: import_node_path44.default.join(baseDir, `embeddings.${project}.json`),
8228
+ policyViolationsPath: import_node_path44.default.join(baseDir, `policy-violations.${project}.ndjson`)
8073
8229
  };
8074
8230
  }
8075
8231
  var Projects = class {
@@ -8107,19 +8263,19 @@ var Projects = class {
8107
8263
  init_cjs_shims();
8108
8264
  var import_fastify = __toESM(require("fastify"), 1);
8109
8265
  var import_cors = __toESM(require("@fastify/cors"), 1);
8110
- var import_types34 = require("@neat.is/types");
8266
+ var import_types35 = require("@neat.is/types");
8111
8267
 
8112
8268
  // src/extend/index.ts
8113
8269
  init_cjs_shims();
8114
8270
  var import_node_fs26 = require("fs");
8115
- var import_node_path45 = __toESM(require("path"), 1);
8271
+ var import_node_path46 = __toESM(require("path"), 1);
8116
8272
  var import_node_os2 = __toESM(require("os"), 1);
8117
8273
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
8118
8274
 
8119
8275
  // src/installers/package-manager.ts
8120
8276
  init_cjs_shims();
8121
8277
  var import_node_fs25 = require("fs");
8122
- var import_node_path44 = __toESM(require("path"), 1);
8278
+ var import_node_path45 = __toESM(require("path"), 1);
8123
8279
  var import_node_child_process = require("child_process");
8124
8280
  var LOCKFILE_PRIORITY = [
8125
8281
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -8141,22 +8297,22 @@ async function exists2(p) {
8141
8297
  }
8142
8298
  }
8143
8299
  async function detectPackageManager(serviceDir) {
8144
- let dir = import_node_path44.default.resolve(serviceDir);
8300
+ let dir = import_node_path45.default.resolve(serviceDir);
8145
8301
  const stops = /* @__PURE__ */ new Set();
8146
8302
  for (let i = 0; i < 64; i++) {
8147
8303
  if (stops.has(dir)) break;
8148
8304
  stops.add(dir);
8149
8305
  for (const candidate of LOCKFILE_PRIORITY) {
8150
- const lockPath = import_node_path44.default.join(dir, candidate.lockfile);
8306
+ const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
8151
8307
  if (await exists2(lockPath)) {
8152
8308
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
8153
8309
  }
8154
8310
  }
8155
- const parent = import_node_path44.default.dirname(dir);
8311
+ const parent = import_node_path45.default.dirname(dir);
8156
8312
  if (parent === dir) break;
8157
8313
  dir = parent;
8158
8314
  }
8159
- return { pm: "npm", cwd: import_node_path44.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8315
+ return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8160
8316
  }
8161
8317
  async function runPackageManagerInstall(cmd) {
8162
8318
  return new Promise((resolve) => {
@@ -8205,7 +8361,7 @@ async function fileExists2(p) {
8205
8361
  }
8206
8362
  }
8207
8363
  async function readPackageJson(scanPath) {
8208
- const pkgPath = import_node_path45.default.join(scanPath, "package.json");
8364
+ const pkgPath = import_node_path46.default.join(scanPath, "package.json");
8209
8365
  const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
8210
8366
  return JSON.parse(raw);
8211
8367
  }
@@ -8219,27 +8375,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
8219
8375
  ]);
8220
8376
  async function findHookFiles(scanPath) {
8221
8377
  const found = [];
8222
- const walk3 = async (dir) => {
8378
+ const walk4 = async (dir) => {
8223
8379
  const entries = await import_node_fs26.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
8224
8380
  for (const entry2 of entries) {
8225
8381
  if (entry2.isDirectory()) {
8226
8382
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
8227
- await walk3(import_node_path45.default.join(dir, entry2.name));
8383
+ await walk4(import_node_path46.default.join(dir, entry2.name));
8228
8384
  } else if (entry2.isFile()) {
8229
8385
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
8230
- const rel = import_node_path45.default.relative(scanPath, import_node_path45.default.join(dir, entry2.name));
8231
- found.push(rel.split(import_node_path45.default.sep).join("/"));
8386
+ const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
8387
+ found.push(rel.split(import_node_path46.default.sep).join("/"));
8232
8388
  }
8233
8389
  }
8234
8390
  }
8235
8391
  };
8236
- await walk3(scanPath);
8392
+ await walk4(scanPath);
8237
8393
  return found.sort();
8238
8394
  }
8239
8395
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8240
8396
  let fallback = null;
8241
8397
  for (const file of hookFiles) {
8242
- const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(scanPath, file), "utf8");
8398
+ const content = await import_node_fs26.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
8243
8399
  const patched = splicedContent(content, snippet2);
8244
8400
  if (patched !== null) return { file, content, patched };
8245
8401
  if (fallback === null) fallback = { file, content };
@@ -8247,11 +8403,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8247
8403
  return { file: fallback.file, content: fallback.content, patched: null };
8248
8404
  }
8249
8405
  function extendLogPath() {
8250
- return process.env.NEAT_EXTEND_LOG ?? import_node_path45.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8406
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8251
8407
  }
8252
8408
  async function appendExtendLog(entry2) {
8253
8409
  const logPath = extendLogPath();
8254
- await import_node_fs26.promises.mkdir(import_node_path45.default.dirname(logPath), { recursive: true });
8410
+ await import_node_fs26.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
8255
8411
  await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
8256
8412
  }
8257
8413
  function splicedContent(fileContent, snippet2) {
@@ -8310,7 +8466,7 @@ function lookupInstrumentation(library, installedVersion) {
8310
8466
  }
8311
8467
  async function describeProjectInstrumentation(ctx) {
8312
8468
  const hookFiles = await findHookFiles(ctx.scanPath);
8313
- const envNeat = await fileExists2(import_node_path45.default.join(ctx.scanPath, ".env.neat"));
8469
+ const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
8314
8470
  const registryInstrPackages = new Set(
8315
8471
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
8316
8472
  );
@@ -8332,7 +8488,7 @@ async function applyExtension(ctx, args, options) {
8332
8488
  );
8333
8489
  }
8334
8490
  for (const file of hookFiles) {
8335
- const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
8491
+ const content = await import_node_fs26.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
8336
8492
  if (content.includes(args.registration_snippet)) {
8337
8493
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
8338
8494
  }
@@ -8344,10 +8500,10 @@ async function applyExtension(ctx, args, options) {
8344
8500
  );
8345
8501
  }
8346
8502
  const primaryFile = primary.file;
8347
- const primaryPath = import_node_path45.default.join(ctx.scanPath, primaryFile);
8503
+ const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
8348
8504
  const filesTouched = [];
8349
8505
  const depsAdded = [];
8350
- const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
8506
+ const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
8351
8507
  const pkg = await readPackageJson(ctx.scanPath);
8352
8508
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
8353
8509
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -8386,7 +8542,7 @@ async function dryRunExtension(ctx, args) {
8386
8542
  };
8387
8543
  }
8388
8544
  for (const file of hookFiles) {
8389
- const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
8545
+ const content = await import_node_fs26.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
8390
8546
  if (content.includes(args.registration_snippet)) {
8391
8547
  return {
8392
8548
  library: args.library,
@@ -8427,7 +8583,7 @@ async function rollbackExtension(ctx, args) {
8427
8583
  if (!match) {
8428
8584
  return { undone: false, message: "no apply found for library" };
8429
8585
  }
8430
- const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
8586
+ const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
8431
8587
  if (await fileExists2(pkgPath)) {
8432
8588
  const pkg = await readPackageJson(ctx.scanPath);
8433
8589
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -8438,7 +8594,7 @@ async function rollbackExtension(ctx, args) {
8438
8594
  }
8439
8595
  const hookFiles = await findHookFiles(ctx.scanPath);
8440
8596
  for (const file of hookFiles) {
8441
- const filePath = import_node_path45.default.join(ctx.scanPath, file);
8597
+ const filePath = import_node_path46.default.join(ctx.scanPath, file);
8442
8598
  const content = await import_node_fs26.promises.readFile(filePath, "utf8");
8443
8599
  if (content.includes(match.registration_snippet)) {
8444
8600
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -8454,39 +8610,39 @@ async function rollbackExtension(ctx, args) {
8454
8610
 
8455
8611
  // src/divergences.ts
8456
8612
  init_cjs_shims();
8457
- var import_types32 = require("@neat.is/types");
8613
+ var import_types33 = require("@neat.is/types");
8458
8614
  function bucketKey(source, target, type) {
8459
8615
  return `${type}|${source}|${target}`;
8460
8616
  }
8461
8617
  function bucketSourceFor(graph, edge) {
8462
- if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
8463
- const parsed = (0, import_types32.parseFileId)(edge.source);
8618
+ if (edge.type !== import_types33.EdgeType.CONNECTS_TO) return edge.source;
8619
+ const parsed = (0, import_types33.parseFileId)(edge.source);
8464
8620
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
8465
8621
  const target = graph.getNodeAttributes(edge.target);
8466
- if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
8467
- return (0, import_types32.serviceId)(parsed.service);
8622
+ if (target.type !== import_types33.NodeType.DatabaseNode) return edge.source;
8623
+ return (0, import_types33.serviceId)(parsed.service);
8468
8624
  }
8469
8625
  function bucketEdges(graph) {
8470
8626
  const buckets2 = /* @__PURE__ */ new Map();
8471
8627
  graph.forEachEdge((id, attrs) => {
8472
8628
  const e = attrs;
8473
- const parsed = (0, import_types32.parseEdgeId)(id);
8629
+ const parsed = (0, import_types33.parseEdgeId)(id);
8474
8630
  const provenance = parsed?.provenance ?? e.provenance;
8475
8631
  const source = bucketSourceFor(graph, e);
8476
8632
  const key = bucketKey(source, e.target, e.type);
8477
8633
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
8478
8634
  switch (provenance) {
8479
- case import_types32.Provenance.EXTRACTED:
8635
+ case import_types33.Provenance.EXTRACTED:
8480
8636
  cur.extracted = e;
8481
8637
  break;
8482
- case import_types32.Provenance.OBSERVED:
8638
+ case import_types33.Provenance.OBSERVED:
8483
8639
  cur.observed = e;
8484
8640
  break;
8485
- case import_types32.Provenance.INFERRED:
8641
+ case import_types33.Provenance.INFERRED:
8486
8642
  cur.inferred = e;
8487
8643
  break;
8488
8644
  default:
8489
- if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
8645
+ if (e.provenance === import_types33.Provenance.STALE) cur.stale = e;
8490
8646
  }
8491
8647
  buckets2.set(key, cur);
8492
8648
  });
@@ -8495,12 +8651,12 @@ function bucketEdges(graph) {
8495
8651
  function nodeIsFrontier(graph, nodeId) {
8496
8652
  if (!graph.hasNode(nodeId)) return false;
8497
8653
  const attrs = graph.getNodeAttributes(nodeId);
8498
- return attrs.type === import_types32.NodeType.FrontierNode;
8654
+ return attrs.type === import_types33.NodeType.FrontierNode;
8499
8655
  }
8500
8656
  function nodeIsWebsocketChannel(graph, nodeId) {
8501
8657
  if (!graph.hasNode(nodeId)) return false;
8502
8658
  const attrs = graph.getNodeAttributes(nodeId);
8503
- return attrs.type === import_types32.NodeType.WebSocketChannelNode;
8659
+ return attrs.type === import_types33.NodeType.WebSocketChannelNode;
8504
8660
  }
8505
8661
  function clampConfidence(n) {
8506
8662
  if (!Number.isFinite(n)) return 0;
@@ -8520,14 +8676,14 @@ function gradedConfidence(edge) {
8520
8676
  return clampConfidence(confidenceForEdge(edge));
8521
8677
  }
8522
8678
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
8523
- import_types32.EdgeType.CALLS,
8524
- import_types32.EdgeType.CONNECTS_TO,
8525
- import_types32.EdgeType.PUBLISHES_TO,
8526
- import_types32.EdgeType.CONSUMES_FROM
8679
+ import_types33.EdgeType.CALLS,
8680
+ import_types33.EdgeType.CONNECTS_TO,
8681
+ import_types33.EdgeType.PUBLISHES_TO,
8682
+ import_types33.EdgeType.CONSUMES_FROM
8527
8683
  ]);
8528
8684
  function detectMissingDivergences(graph, bucket) {
8529
8685
  const out = [];
8530
- if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
8686
+ if (bucket.type === import_types33.EdgeType.CONTAINS) return out;
8531
8687
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
8532
8688
  if (!nodeIsFrontier(graph, bucket.target)) {
8533
8689
  out.push({
@@ -8568,7 +8724,7 @@ function declaredHostFor(svc) {
8568
8724
  function hasExtractedConfiguredBy(graph, svcId) {
8569
8725
  for (const edgeId of graph.outboundEdges(svcId)) {
8570
8726
  const e = graph.getEdgeAttributes(edgeId);
8571
- if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
8727
+ if (e.type === import_types33.EdgeType.CONFIGURED_BY && e.provenance === import_types33.Provenance.EXTRACTED) {
8572
8728
  return true;
8573
8729
  }
8574
8730
  }
@@ -8581,10 +8737,10 @@ function detectHostMismatch(graph, svcId, svc) {
8581
8737
  const out = [];
8582
8738
  for (const edgeId of graph.outboundEdges(svcId)) {
8583
8739
  const edge = graph.getEdgeAttributes(edgeId);
8584
- if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8585
- if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8740
+ if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
8741
+ if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
8586
8742
  const target = graph.getNodeAttributes(edge.target);
8587
- if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8743
+ if (target.type !== import_types33.NodeType.DatabaseNode) continue;
8588
8744
  const observedHost = target.host?.trim();
8589
8745
  if (!observedHost) continue;
8590
8746
  if (observedHost === declaredHost) continue;
@@ -8606,10 +8762,10 @@ function detectCompatDivergences(graph, svcId, svc) {
8606
8762
  const deps = svc.dependencies ?? {};
8607
8763
  for (const edgeId of graph.outboundEdges(svcId)) {
8608
8764
  const edge = graph.getEdgeAttributes(edgeId);
8609
- if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8610
- if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8765
+ if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
8766
+ if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
8611
8767
  const target = graph.getNodeAttributes(edge.target);
8612
- if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8768
+ if (target.type !== import_types33.NodeType.DatabaseNode) continue;
8613
8769
  for (const pair of compatPairs()) {
8614
8770
  if (pair.engine !== target.engine) continue;
8615
8771
  const declared = deps[pair.driver];
@@ -8668,7 +8824,7 @@ function suppressHostMismatchHalves(all) {
8668
8824
  for (const d of all) {
8669
8825
  if (d.type !== "host-mismatch") continue;
8670
8826
  observedHalf.add(`${d.source}->${d.target}`);
8671
- declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
8827
+ declaredHalf.add((0, import_types33.databaseId)(d.extractedHost));
8672
8828
  }
8673
8829
  if (observedHalf.size === 0) return all;
8674
8830
  return all.filter((d) => {
@@ -8687,7 +8843,7 @@ function computeDivergences(graph, opts = {}) {
8687
8843
  }
8688
8844
  graph.forEachNode((nodeId, attrs) => {
8689
8845
  const n = attrs;
8690
- if (n.type !== import_types32.NodeType.ServiceNode) return;
8846
+ if (n.type !== import_types33.NodeType.ServiceNode) return;
8691
8847
  const svc = n;
8692
8848
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
8693
8849
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -8721,7 +8877,7 @@ function computeDivergences(graph, opts = {}) {
8721
8877
  if (a.source !== b.source) return a.source.localeCompare(b.source);
8722
8878
  return a.target.localeCompare(b.target);
8723
8879
  });
8724
- return import_types32.DivergenceResultSchema.parse({
8880
+ return import_types33.DivergenceResultSchema.parse({
8725
8881
  divergences: filtered,
8726
8882
  totalAffected: filtered.length,
8727
8883
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -8854,23 +9010,23 @@ function canonicalJson(value) {
8854
9010
  init_cjs_shims();
8855
9011
  var import_node_fs28 = require("fs");
8856
9012
  var import_node_os3 = __toESM(require("os"), 1);
8857
- var import_node_path46 = __toESM(require("path"), 1);
8858
- var import_types33 = require("@neat.is/types");
9013
+ var import_node_path47 = __toESM(require("path"), 1);
9014
+ var import_types34 = require("@neat.is/types");
8859
9015
  var LOCK_TIMEOUT_MS = 5e3;
8860
9016
  var LOCK_RETRY_MS = 50;
8861
9017
  function neatHome() {
8862
9018
  const override = process.env.NEAT_HOME;
8863
- if (override && override.length > 0) return import_node_path46.default.resolve(override);
8864
- return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
9019
+ if (override && override.length > 0) return import_node_path47.default.resolve(override);
9020
+ return import_node_path47.default.join(import_node_os3.default.homedir(), ".neat");
8865
9021
  }
8866
9022
  function registryPath() {
8867
- return import_node_path46.default.join(neatHome(), "projects.json");
9023
+ return import_node_path47.default.join(neatHome(), "projects.json");
8868
9024
  }
8869
9025
  function registryLockPath() {
8870
- return import_node_path46.default.join(neatHome(), "projects.json.lock");
9026
+ return import_node_path47.default.join(neatHome(), "projects.json.lock");
8871
9027
  }
8872
9028
  function daemonPidPath() {
8873
- return import_node_path46.default.join(neatHome(), "neatd.pid");
9029
+ return import_node_path47.default.join(neatHome(), "neatd.pid");
8874
9030
  }
8875
9031
  function isPidAliveDefault(pid) {
8876
9032
  try {
@@ -8930,7 +9086,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
8930
9086
  }
8931
9087
  }
8932
9088
  async function writeAtomically(target, contents) {
8933
- await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
9089
+ await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(target), { recursive: true });
8934
9090
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
8935
9091
  const fd = await import_node_fs28.promises.open(tmp, "w");
8936
9092
  try {
@@ -8943,7 +9099,7 @@ async function writeAtomically(target, contents) {
8943
9099
  }
8944
9100
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
8945
9101
  const deadline = Date.now() + timeoutMs;
8946
- await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
9102
+ await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(lockPath), { recursive: true });
8947
9103
  let probedHolder = false;
8948
9104
  while (true) {
8949
9105
  try {
@@ -8996,10 +9152,10 @@ async function readRegistry() {
8996
9152
  throw err;
8997
9153
  }
8998
9154
  const parsed = JSON.parse(raw);
8999
- return import_types33.RegistryFileSchema.parse(parsed);
9155
+ return import_types34.RegistryFileSchema.parse(parsed);
9000
9156
  }
9001
9157
  async function writeRegistry(reg) {
9002
- const validated = import_types33.RegistryFileSchema.parse(reg);
9158
+ const validated = import_types34.RegistryFileSchema.parse(reg);
9003
9159
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
9004
9160
  }
9005
9161
  async function getProject(name) {
@@ -9144,7 +9300,7 @@ init_auth();
9144
9300
  // src/connectors-config.ts
9145
9301
  init_cjs_shims();
9146
9302
  var import_node_os4 = __toESM(require("os"), 1);
9147
- var import_node_path47 = __toESM(require("path"), 1);
9303
+ var import_node_path48 = __toESM(require("path"), 1);
9148
9304
  var import_node_fs29 = require("fs");
9149
9305
  var CONNECTORS_CONFIG_VERSION = 1;
9150
9306
  var EnvRefUnsetError = class extends Error {
@@ -9159,11 +9315,11 @@ var EnvRefUnsetError = class extends Error {
9159
9315
  };
9160
9316
  function neatHome2() {
9161
9317
  const override = process.env.NEAT_HOME;
9162
- if (override && override.length > 0) return import_node_path47.default.resolve(override);
9163
- return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
9318
+ if (override && override.length > 0) return import_node_path48.default.resolve(override);
9319
+ return import_node_path48.default.join(import_node_os4.default.homedir(), ".neat");
9164
9320
  }
9165
9321
  function connectorsConfigPath(home = neatHome2()) {
9166
- return import_node_path47.default.join(home, "connectors.json");
9322
+ return import_node_path48.default.join(home, "connectors.json");
9167
9323
  }
9168
9324
  var MODE_MASK_LOOSER_THAN_0600 = 63;
9169
9325
  async function warnIfModeLooserThan0600(file) {
@@ -9499,11 +9655,11 @@ function registerRoutes(scope, ctx) {
9499
9655
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
9500
9656
  const parsed = [];
9501
9657
  for (const c of candidates) {
9502
- const r = import_types34.DivergenceTypeSchema.safeParse(c);
9658
+ const r = import_types35.DivergenceTypeSchema.safeParse(c);
9503
9659
  if (!r.success) {
9504
9660
  return reply.code(400).send({
9505
9661
  error: `unknown divergence type "${c}"`,
9506
- allowed: import_types34.DivergenceTypeSchema.options
9662
+ allowed: import_types35.DivergenceTypeSchema.options
9507
9663
  });
9508
9664
  }
9509
9665
  parsed.push(r.data);
@@ -9774,7 +9930,7 @@ function registerRoutes(scope, ctx) {
9774
9930
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
9775
9931
  let violations = await log.readAll();
9776
9932
  if (req2.query.severity) {
9777
- const sev = import_types34.PolicySeveritySchema.safeParse(req2.query.severity);
9933
+ const sev = import_types35.PolicySeveritySchema.safeParse(req2.query.severity);
9778
9934
  if (!sev.success) {
9779
9935
  return reply.code(400).send({
9780
9936
  error: "invalid severity",
@@ -9813,7 +9969,7 @@ function registerRoutes(scope, ctx) {
9813
9969
  scope.post("/policies/check", async (req2, reply) => {
9814
9970
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
9815
9971
  if (!proj) return;
9816
- const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
9972
+ const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
9817
9973
  if (!parsed.success) {
9818
9974
  return reply.code(400).send({
9819
9975
  error: "invalid /policies/check body",
@@ -10113,15 +10269,15 @@ init_otel();
10113
10269
 
10114
10270
  // src/connectors/index.ts
10115
10271
  init_cjs_shims();
10116
- var import_types35 = require("@neat.is/types");
10272
+ var import_types36 = require("@neat.is/types");
10117
10273
  var NO_ENV = "unknown";
10118
10274
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
10119
10275
  if (!graph.hasNode(targetNodeId)) return void 0;
10120
10276
  const sites = [];
10121
10277
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
10122
10278
  const edge = graph.getEdgeAttributes(edgeId);
10123
- if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
10124
- const parsed = (0, import_types35.parseFileId)(edge.source);
10279
+ if (edge.provenance !== import_types36.Provenance.EXTRACTED) continue;
10280
+ const parsed = (0, import_types36.parseFileId)(edge.source);
10125
10281
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
10126
10282
  const site = { relPath: edge.evidence.file };
10127
10283
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -10132,7 +10288,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
10132
10288
  function routeCallSiteFor(graph, targetNodeId) {
10133
10289
  if (!graph.hasNode(targetNodeId)) return void 0;
10134
10290
  const attrs = graph.getNodeAttributes(targetNodeId);
10135
- if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
10291
+ if (attrs.type !== import_types36.NodeType.RouteNode || !attrs.path) return void 0;
10136
10292
  const site = { relPath: attrs.path };
10137
10293
  if (attrs.line !== void 0) site.line = attrs.line;
10138
10294
  return site;
@@ -10603,10 +10759,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
10603
10759
  // src/connectors/supabase/map.ts
10604
10760
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
10605
10761
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
10606
- function targetFromRestPath(path54) {
10607
- const rpcMatch = REST_RPC_PATH_RE.exec(path54);
10762
+ function targetFromRestPath(path55) {
10763
+ const rpcMatch = REST_RPC_PATH_RE.exec(path55);
10608
10764
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
10609
- const tableMatch = REST_TABLE_PATH_RE.exec(path54);
10765
+ const tableMatch = REST_TABLE_PATH_RE.exec(path55);
10610
10766
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
10611
10767
  return null;
10612
10768
  }
@@ -10715,23 +10871,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10715
10871
 
10716
10872
  // src/connectors/supabase/resolve.ts
10717
10873
  init_cjs_shims();
10718
- var import_types37 = require("@neat.is/types");
10874
+ var import_types38 = require("@neat.is/types");
10719
10875
  function createSupabaseResolveTarget(graph, config) {
10720
10876
  return (signal, _ctx) => {
10721
10877
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10722
10878
  return null;
10723
10879
  }
10724
- const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10880
+ const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10725
10881
  if (graph.hasNode(subResourceId)) {
10726
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10882
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10727
10883
  }
10728
- const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
10884
+ const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
10729
10885
  if (graph.hasNode(bareResourceId)) {
10730
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10886
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10731
10887
  }
10732
- const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
10888
+ const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
10733
10889
  if (graph.hasNode(projectLevelId)) {
10734
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10890
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10735
10891
  }
10736
10892
  return null;
10737
10893
  };
@@ -10824,7 +10980,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10824
10980
 
10825
10981
  // src/connectors/railway/index.ts
10826
10982
  init_cjs_shims();
10827
- var import_types41 = require("@neat.is/types");
10983
+ var import_types42 = require("@neat.is/types");
10828
10984
 
10829
10985
  // src/connectors/railway/client.ts
10830
10986
  init_cjs_shims();
@@ -10950,7 +11106,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
10950
11106
  const out = [];
10951
11107
  graph.forEachNode((_id, attrs) => {
10952
11108
  const node = attrs;
10953
- if (node.type !== import_types41.NodeType.RouteNode) return;
11109
+ if (node.type !== import_types42.NodeType.RouteNode) return;
10954
11110
  const route = attrs;
10955
11111
  if (route.service !== serviceName) return;
10956
11112
  out.push({
@@ -11054,12 +11210,12 @@ function createRailwayResolveTarget(config) {
11054
11210
  const serviceName = config.serviceNameById[config.serviceId];
11055
11211
  if (!serviceName) return null;
11056
11212
  if (signal.targetKind === ROUTE_TARGET_KIND) {
11057
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
11213
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
11058
11214
  }
11059
11215
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
11060
11216
  const peerName = config.serviceNameById[signal.targetName];
11061
11217
  if (!peerName) return null;
11062
- return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
11218
+ return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
11063
11219
  }
11064
11220
  return null;
11065
11221
  };
@@ -11183,9 +11339,9 @@ function parseFirebaseTargetName(targetName) {
11183
11339
  const secondSep = rest.indexOf(FIELD_SEP);
11184
11340
  if (secondSep === -1) return null;
11185
11341
  const method = rest.slice(0, secondSep);
11186
- const path54 = rest.slice(secondSep + 1);
11187
- if (!resourceName || !method || !path54) return null;
11188
- return { resourceName, method, path: path54 };
11342
+ const path55 = rest.slice(secondSep + 1);
11343
+ if (!resourceName || !method || !path55) return null;
11344
+ return { resourceName, method, path: path55 };
11189
11345
  }
11190
11346
  function resourceNameFor(type, labels) {
11191
11347
  if (!labels) return null;
@@ -11223,14 +11379,14 @@ function mapLogEntryToSignal(entry2) {
11223
11379
  if (!req2) return null;
11224
11380
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
11225
11381
  const method = req2.requestMethod.toUpperCase();
11226
- const path54 = pathFromRequestUrl(req2.requestUrl);
11227
- if (path54 === null) return null;
11382
+ const path55 = pathFromRequestUrl(req2.requestUrl);
11383
+ if (path55 === null) return null;
11228
11384
  const timestamp = entry2.timestamp;
11229
11385
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
11230
11386
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
11231
11387
  return {
11232
11388
  targetKind: resourceType,
11233
- targetName: packFirebaseTargetName({ resourceName, method, path: path54 }),
11389
+ targetName: packFirebaseTargetName({ resourceName, method, path: path55 }),
11234
11390
  callCount: 1,
11235
11391
  errorCount: isError ? 1 : 0,
11236
11392
  lastObservedIso: timestamp
@@ -11247,7 +11403,7 @@ function mapLogEntriesToSignals(entries) {
11247
11403
 
11248
11404
  // src/connectors/firebase/resolve.ts
11249
11405
  init_cjs_shims();
11250
- var import_types42 = require("@neat.is/types");
11406
+ var import_types43 = require("@neat.is/types");
11251
11407
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
11252
11408
  switch (resourceType) {
11253
11409
  case "cloud_function":
@@ -11262,7 +11418,7 @@ function routeEntriesFor(graph, serviceName) {
11262
11418
  const entries = [];
11263
11419
  graph.forEachNode((_id, attrs) => {
11264
11420
  const node = attrs;
11265
- if (node.type !== import_types42.NodeType.RouteNode) return;
11421
+ if (node.type !== import_types43.NodeType.RouteNode) return;
11266
11422
  const route = attrs;
11267
11423
  if (route.service !== serviceName) return;
11268
11424
  entries.push({
@@ -11294,7 +11450,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
11294
11450
  return {
11295
11451
  targetNodeId: match.routeNodeId,
11296
11452
  serviceName,
11297
- edgeType: import_types42.EdgeType.CALLS
11453
+ edgeType: import_types43.EdgeType.CALLS
11298
11454
  };
11299
11455
  };
11300
11456
  }
@@ -11321,7 +11477,7 @@ init_cjs_shims();
11321
11477
 
11322
11478
  // src/connectors/cloudflare/connector.ts
11323
11479
  init_cjs_shims();
11324
- var import_types44 = require("@neat.is/types");
11480
+ var import_types45 = require("@neat.is/types");
11325
11481
 
11326
11482
  // src/connectors/cloudflare/client.ts
11327
11483
  init_cjs_shims();
@@ -11437,7 +11593,7 @@ function mapEventToSignal(event) {
11437
11593
  if (Number.isNaN(observedAt.getTime())) return null;
11438
11594
  const statusCode = metadata?.statusCode;
11439
11595
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
11440
- const path54 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11596
+ const path55 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11441
11597
  return {
11442
11598
  targetKind: CLOUDFLARE_TARGET_KIND,
11443
11599
  targetName: scriptName,
@@ -11445,7 +11601,7 @@ function mapEventToSignal(event) {
11445
11601
  errorCount: isError ? 1 : 0,
11446
11602
  lastObservedIso: observedAt.toISOString(),
11447
11603
  method,
11448
- ...path54 ? { path: path54 } : {},
11604
+ ...path55 ? { path: path55 } : {},
11449
11605
  ...typeof statusCode === "number" ? { statusCode } : {},
11450
11606
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
11451
11607
  };
@@ -11485,19 +11641,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
11485
11641
  graph.forEachNode((id, attrs) => {
11486
11642
  if (found) return;
11487
11643
  const a = attrs;
11488
- if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11644
+ if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11489
11645
  found = id;
11490
11646
  }
11491
11647
  });
11492
11648
  return found;
11493
11649
  }
11494
- function findMatchingRouteNode(graph, serviceName, method, path54) {
11495
- const normalizedPath = normalizePathTemplate(path54);
11650
+ function findMatchingRouteNode(graph, serviceName, method, path55) {
11651
+ const normalizedPath = normalizePathTemplate(path55);
11496
11652
  let found = null;
11497
11653
  graph.forEachNode((id, attrs) => {
11498
11654
  if (found) return;
11499
11655
  const a = attrs;
11500
- if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
11656
+ if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
11501
11657
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11502
11658
  const routeMethod = (a.method ?? "").toUpperCase();
11503
11659
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11509,18 +11665,18 @@ function createCloudflareResolveTarget(config, graph) {
11509
11665
  return (signal) => {
11510
11666
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
11511
11667
  const scriptName = signal.targetName;
11512
- const { method, path: path54 } = signal;
11668
+ const { method, path: path55 } = signal;
11513
11669
  const resolveRouteGrain = (serviceName, wholeFileId) => {
11514
- if (!method || !path54) return wholeFileId;
11515
- return findMatchingRouteNode(graph, serviceName, method, path54) ?? wholeFileId;
11670
+ if (!method || !path55) return wholeFileId;
11671
+ return findMatchingRouteNode(graph, serviceName, method, path55) ?? wholeFileId;
11516
11672
  };
11517
11673
  const mapping = config.workers?.[scriptName];
11518
11674
  if (mapping) {
11519
- const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
11675
+ const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
11520
11676
  return {
11521
11677
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11522
11678
  serviceName: mapping.service,
11523
- edgeType: import_types44.EdgeType.CALLS
11679
+ edgeType: import_types45.EdgeType.CALLS
11524
11680
  };
11525
11681
  }
11526
11682
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11529,13 +11685,13 @@ function createCloudflareResolveTarget(config, graph) {
11529
11685
  return {
11530
11686
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11531
11687
  serviceName: fileNode.service,
11532
- edgeType: import_types44.EdgeType.CALLS
11688
+ edgeType: import_types45.EdgeType.CALLS
11533
11689
  };
11534
11690
  }
11535
11691
  return {
11536
- targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
11692
+ targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
11537
11693
  serviceName: scriptName,
11538
- edgeType: import_types44.EdgeType.CALLS,
11694
+ edgeType: import_types45.EdgeType.CALLS,
11539
11695
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11540
11696
  };
11541
11697
  };
@@ -11979,7 +12135,7 @@ init_auth();
11979
12135
  // src/unrouted.ts
11980
12136
  init_cjs_shims();
11981
12137
  var import_node_fs30 = require("fs");
11982
- var import_node_path50 = __toESM(require("path"), 1);
12138
+ var import_node_path51 = __toESM(require("path"), 1);
11983
12139
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
11984
12140
  return {
11985
12141
  timestamp: now.toISOString(),
@@ -11989,34 +12145,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
11989
12145
  };
11990
12146
  }
11991
12147
  async function appendUnroutedSpan(neatHome4, record) {
11992
- const target = import_node_path50.default.join(neatHome4, "errors.ndjson");
12148
+ const target = import_node_path51.default.join(neatHome4, "errors.ndjson");
11993
12149
  await import_node_fs30.promises.mkdir(neatHome4, { recursive: true });
11994
12150
  await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
11995
12151
  }
11996
12152
  function unroutedErrorsPath(neatHome4) {
11997
- return import_node_path50.default.join(neatHome4, "errors.ndjson");
12153
+ return import_node_path51.default.join(neatHome4, "errors.ndjson");
11998
12154
  }
11999
12155
 
12000
12156
  // src/daemon.ts
12001
- var import_types47 = require("@neat.is/types");
12157
+ var import_types48 = require("@neat.is/types");
12002
12158
  function daemonJsonPath(scanPath) {
12003
- return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
12159
+ return import_node_path52.default.join(scanPath, "neat-out", "daemon.json");
12004
12160
  }
12005
12161
  function daemonsDiscoveryDir(home) {
12006
12162
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
12007
- return import_node_path51.default.join(base, "daemons");
12163
+ return import_node_path52.default.join(base, "daemons");
12008
12164
  }
12009
12165
  function daemonDiscoveryPath(project, home) {
12010
- return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12166
+ return import_node_path52.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12011
12167
  }
12012
12168
  function sanitizeDiscoveryName(project) {
12013
12169
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
12014
12170
  }
12015
12171
  function neatHomeFromEnv() {
12016
12172
  const env = process.env.NEAT_HOME;
12017
- if (env && env.length > 0) return import_node_path51.default.resolve(env);
12173
+ if (env && env.length > 0) return import_node_path52.default.resolve(env);
12018
12174
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12019
- return import_node_path51.default.join(home, ".neat");
12175
+ return import_node_path52.default.join(home, ".neat");
12020
12176
  }
12021
12177
  function resolveNeatVersion() {
12022
12178
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -12085,11 +12241,11 @@ function teardownSlot(slot) {
12085
12241
  }
12086
12242
  }
12087
12243
  function neatHomeFor(opts) {
12088
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
12244
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path52.default.resolve(opts.neatHome);
12089
12245
  const env = process.env.NEAT_HOME;
12090
- if (env && env.length > 0) return import_node_path51.default.resolve(env);
12246
+ if (env && env.length > 0) return import_node_path52.default.resolve(env);
12091
12247
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12092
- return import_node_path51.default.join(home, ".neat");
12248
+ return import_node_path52.default.join(home, ".neat");
12093
12249
  }
12094
12250
  function routeSpanToProject(serviceName, projects) {
12095
12251
  if (!serviceName) return DEFAULT_PROJECT;
@@ -12133,11 +12289,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
12133
12289
  if (!serviceName) return true;
12134
12290
  if (serviceNameMatchesProject(serviceName, project)) return true;
12135
12291
  return graph.someNode(
12136
- (_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
12292
+ (_id, attrs) => attrs.type === import_types48.NodeType.ServiceNode && attrs.name === serviceName
12137
12293
  );
12138
12294
  }
12139
12295
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
12140
- const paths = pathsForProject(entry2.name, import_node_path51.default.join(entry2.path, "neat-out"));
12296
+ const paths = pathsForProject(entry2.name, import_node_path52.default.join(entry2.path, "neat-out"));
12141
12297
  try {
12142
12298
  const stat = await import_node_fs31.promises.stat(entry2.path);
12143
12299
  if (!stat.isDirectory()) {
@@ -12268,7 +12424,7 @@ async function startDaemon(opts = {}) {
12268
12424
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
12269
12425
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
12270
12426
  const singleProject = projectArg;
12271
- const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
12427
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path52.default.resolve(projectPathArg) : null;
12272
12428
  if (singleProject && !singleProjectPath) {
12273
12429
  throw new Error(
12274
12430
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -12283,7 +12439,7 @@ async function startDaemon(opts = {}) {
12283
12439
  );
12284
12440
  }
12285
12441
  }
12286
- const pidPath = import_node_path51.default.join(home, "neatd.pid");
12442
+ const pidPath = import_node_path52.default.join(home, "neatd.pid");
12287
12443
  await writeAtomically(pidPath, `${process.pid}
12288
12444
  `);
12289
12445
  const slots = /* @__PURE__ */ new Map();
@@ -12690,8 +12846,8 @@ async function startDaemon(opts = {}) {
12690
12846
  let registryWatcher = null;
12691
12847
  let reloadTimer = null;
12692
12848
  if (!singleProject) try {
12693
- const regDir = import_node_path51.default.dirname(regPath);
12694
- const regBase = import_node_path51.default.basename(regPath);
12849
+ const regDir = import_node_path52.default.dirname(regPath);
12850
+ const regBase = import_node_path52.default.basename(regPath);
12695
12851
  registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
12696
12852
  if (filename !== null && filename !== regBase) return;
12697
12853
  if (reloadTimer) clearTimeout(reloadTimer);
@@ -12766,7 +12922,7 @@ init_cjs_shims();
12766
12922
  var import_node_child_process2 = require("child_process");
12767
12923
  var import_node_fs32 = require("fs");
12768
12924
  var import_node_net = __toESM(require("net"), 1);
12769
- var import_node_path52 = __toESM(require("path"), 1);
12925
+ var import_node_path53 = __toESM(require("path"), 1);
12770
12926
  var DEFAULT_WEB_PORT = 6328;
12771
12927
  var DEFAULT_REST_PORT = 8080;
12772
12928
  function asValidPort(value) {
@@ -12775,11 +12931,11 @@ function asValidPort(value) {
12775
12931
  }
12776
12932
  function projectRoot() {
12777
12933
  const fromEnv = process.env.NEAT_SCAN_PATH;
12778
- return import_node_path52.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
12934
+ return import_node_path53.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
12779
12935
  }
12780
12936
  async function readDaemonPorts(root) {
12781
12937
  try {
12782
- const raw = await import_node_fs32.promises.readFile(import_node_path52.default.join(root, "neat-out", "daemon.json"), "utf8");
12938
+ const raw = await import_node_fs32.promises.readFile(import_node_path53.default.join(root, "neat-out", "daemon.json"), "utf8");
12783
12939
  const parsed = JSON.parse(raw);
12784
12940
  const ports = parsed?.ports ?? {};
12785
12941
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -12824,10 +12980,10 @@ function resolveWebPackageDir() {
12824
12980
  eval("require")
12825
12981
  );
12826
12982
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
12827
- return import_node_path52.default.dirname(pkgJsonPath);
12983
+ return import_node_path53.default.dirname(pkgJsonPath);
12828
12984
  }
12829
12985
  function resolveStandaloneServerEntry(webDir) {
12830
- return import_node_path52.default.join(webDir, ".next/standalone/packages/web/server.js");
12986
+ return import_node_path53.default.join(webDir, ".next/standalone/packages/web/server.js");
12831
12987
  }
12832
12988
  async function pickInternalPort() {
12833
12989
  return new Promise((resolve, reject) => {
@@ -12880,7 +13036,7 @@ async function spawnWebUI(restPort, opts = {}) {
12880
13036
  NEAT_API_URL: apiUrl
12881
13037
  };
12882
13038
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
12883
- cwd: import_node_path52.default.dirname(serverEntry),
13039
+ cwd: import_node_path53.default.dirname(serverEntry),
12884
13040
  env,
12885
13041
  stdio: ["ignore", "inherit", "inherit"],
12886
13042
  detached: false
@@ -13056,14 +13212,14 @@ function localVersion() {
13056
13212
  }
13057
13213
  function neatHome3() {
13058
13214
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
13059
- return import_node_path53.default.resolve(process.env.NEAT_HOME);
13215
+ return import_node_path54.default.resolve(process.env.NEAT_HOME);
13060
13216
  }
13061
13217
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
13062
- return import_node_path53.default.join(home, ".neat");
13218
+ return import_node_path54.default.join(home, ".neat");
13063
13219
  }
13064
13220
  async function readPid() {
13065
13221
  try {
13066
- const raw = await import_node_fs33.promises.readFile(import_node_path53.default.join(neatHome3(), "neatd.pid"), "utf8");
13222
+ const raw = await import_node_fs33.promises.readFile(import_node_path54.default.join(neatHome3(), "neatd.pid"), "utf8");
13067
13223
  const n = Number.parseInt(raw.trim(), 10);
13068
13224
  return Number.isFinite(n) ? n : null;
13069
13225
  } catch {