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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path52 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path52) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path52)) {
63
+ const path53 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path53) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path53)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
193
193
  };
194
194
  }
195
195
  function resolveProtoRoot() {
196
- const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
- return import_node_path48.default.resolve(here, "..", "proto");
196
+ const here = import_node_path49.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
+ return import_node_path49.default.resolve(here, "..", "proto");
198
198
  }
199
199
  function loadTraceService() {
200
200
  const protoRoot = resolveProtoRoot();
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
262
262
  })
263
263
  };
264
264
  }
265
- var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
265
+ var import_node_url, import_node_path49, import_node_crypto2, grpc, protoLoader;
266
266
  var init_otel_grpc = __esm({
267
267
  "src/otel-grpc.ts"() {
268
268
  "use strict";
269
269
  init_cjs_shims();
270
270
  import_node_url = require("url");
271
- import_node_path48 = __toESM(require("path"), 1);
271
+ import_node_path49 = __toESM(require("path"), 1);
272
272
  import_node_crypto2 = require("crypto");
273
273
  grpc = __toESM(require("@grpc/grpc-js"), 1);
274
274
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -346,6 +346,14 @@ function normalizeDbSystem(attrs) {
346
346
  if (typeof raw !== "string") return void 0;
347
347
  return raw === "mongoose" ? "mongodb" : raw;
348
348
  }
349
+ function tableFromSqlStatement(sql) {
350
+ if (typeof sql !== "string" || sql.length === 0) return null;
351
+ if (/\bjoin\b/i.test(sql)) return null;
352
+ const froms = sql.match(/\bfrom\b/gi);
353
+ if (froms && froms.length > 1) return null;
354
+ const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
355
+ return m ? m[1] : null;
356
+ }
349
357
  function messagingDestinationOf(attrs) {
350
358
  for (const key of ["messaging.destination.name", "messaging.destination"]) {
351
359
  const v = attrs[key];
@@ -366,8 +374,8 @@ function websocketChannelPathOf(attrs) {
366
374
  const v = attrs[key];
367
375
  if (typeof v === "string" && v.length > 0) {
368
376
  const q = v.indexOf("?");
369
- const path52 = q === -1 ? v : v.slice(0, q);
370
- if (path52.length > 0) return path52;
377
+ const path53 = q === -1 ? v : v.slice(0, q);
378
+ if (path53.length > 0) return path53;
371
379
  }
372
380
  }
373
381
  return void 0;
@@ -403,6 +411,7 @@ function parseOtlpRequest(body) {
403
411
  dbSystem: normalizeDbSystem(attrs),
404
412
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
405
413
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
414
+ dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
406
415
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
407
416
  messagingDestination: messagingDestinationOf(attrs),
408
417
  graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
@@ -422,10 +431,10 @@ function parseOtlpRequest(body) {
422
431
  return out;
423
432
  }
424
433
  function loadProtoRoot() {
425
- const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
426
- const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
434
+ const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
435
+ const protoRoot = import_node_path50.default.resolve(here, "..", "proto");
427
436
  const root = new import_protobufjs.default.Root();
428
- root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
437
+ root.resolvePath = (_origin, target) => import_node_path50.default.resolve(protoRoot, target);
429
438
  root.loadSync(
430
439
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
431
440
  { keepCase: true }
@@ -661,12 +670,12 @@ function logSpanHandler(span) {
661
670
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
662
671
  );
663
672
  }
664
- 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;
673
+ 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;
665
674
  var init_otel = __esm({
666
675
  "src/otel.ts"() {
667
676
  "use strict";
668
677
  init_cjs_shims();
669
- import_node_path49 = __toESM(require("path"), 1);
678
+ import_node_path50 = __toESM(require("path"), 1);
670
679
  import_node_url2 = require("url");
671
680
  import_fastify2 = __toESM(require("fastify"), 1);
672
681
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -1256,19 +1265,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1256
1265
  function longestIncomingWalk(graph, start, maxDepth) {
1257
1266
  let best = { path: [start], edges: [] };
1258
1267
  const visited = /* @__PURE__ */ new Set([start]);
1259
- function step(node, path52, edges) {
1260
- if (path52.length > best.path.length) {
1261
- best = { path: [...path52], edges: [...edges] };
1268
+ function step(node, path53, edges) {
1269
+ if (path53.length > best.path.length) {
1270
+ best = { path: [...path53], edges: [...edges] };
1262
1271
  }
1263
- if (path52.length - 1 >= maxDepth) return;
1272
+ if (path53.length - 1 >= maxDepth) return;
1264
1273
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1265
1274
  for (const [srcId, edge] of incoming) {
1266
1275
  if (visited.has(srcId)) continue;
1267
1276
  visited.add(srcId);
1268
- path52.push(srcId);
1277
+ path53.push(srcId);
1269
1278
  edges.push(edge);
1270
- step(srcId, path52, edges);
1271
- path52.pop();
1279
+ step(srcId, path53, edges);
1280
+ path53.pop();
1272
1281
  edges.pop();
1273
1282
  visited.delete(srcId);
1274
1283
  }
@@ -1276,11 +1285,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1276
1285
  step(start, [start], []);
1277
1286
  return best;
1278
1287
  }
1279
- function databaseRootCauseShape(graph, origin, walk3) {
1288
+ function databaseRootCauseShape(graph, origin, walk4) {
1280
1289
  const targetDb = origin;
1281
1290
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1282
1291
  if (candidatePairs.length === 0) return null;
1283
- for (const id of walk3.path) {
1292
+ for (const id of walk4.path) {
1284
1293
  const owner = resolveOwningService(graph, id);
1285
1294
  if (!owner) continue;
1286
1295
  const { id: serviceId6, svc } = owner;
@@ -1307,8 +1316,8 @@ function databaseRootCauseShape(graph, origin, walk3) {
1307
1316
  }
1308
1317
  return null;
1309
1318
  }
1310
- function serviceRootCauseShape(graph, _origin, walk3) {
1311
- for (const id of walk3.path) {
1319
+ function serviceRootCauseShape(graph, _origin, walk4) {
1320
+ for (const id of walk4.path) {
1312
1321
  const owner = resolveOwningService(graph, id);
1313
1322
  if (!owner) continue;
1314
1323
  const { id: serviceId6, svc } = owner;
@@ -1344,10 +1353,10 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1344
1353
  }
1345
1354
  return null;
1346
1355
  }
1347
- function fileRootCauseShape(graph, origin, walk3) {
1356
+ function fileRootCauseShape(graph, origin, walk4) {
1348
1357
  const owner = resolveOwningService(graph, origin.id);
1349
1358
  if (!owner) return null;
1350
- return serviceRootCauseShape(graph, owner.svc, walk3);
1359
+ return serviceRootCauseShape(graph, owner.svc, walk4);
1351
1360
  }
1352
1361
  var rootCauseShapes = {
1353
1362
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1359,16 +1368,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1359
1368
  const origin = graph.getNodeAttributes(errorNodeId);
1360
1369
  const shape = rootCauseShapes[origin.type];
1361
1370
  if (shape) {
1362
- const walk3 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1363
- const match = shape(graph, origin, walk3);
1371
+ const walk4 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1372
+ const match = shape(graph, origin, walk4);
1364
1373
  if (match) {
1365
1374
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1366
1375
  return import_types.RootCauseResultSchema.parse({
1367
1376
  rootCauseNode: match.rootCauseNode,
1368
1377
  rootCauseReason: reason,
1369
- traversalPath: walk3.path,
1370
- edgeProvenances: walk3.edges.map((e) => e.provenance),
1371
- confidence: confidenceFromMix(walk3.edges),
1378
+ traversalPath: walk4.path,
1379
+ edgeProvenances: walk4.edges.map((e) => e.provenance),
1380
+ confidence: confidenceFromMix(walk4.edges),
1372
1381
  fixRecommendation: match.fixRecommendation
1373
1382
  });
1374
1383
  }
@@ -1389,8 +1398,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
1389
1398
  if (relevant.length === 0) return null;
1390
1399
  const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
1391
1400
  const attrs = latest.attributes ?? {};
1392
- const filepath = typeof attrs["code.filepath"] === "string" ? attrs["code.filepath"] : void 0;
1393
- const lineno = typeof attrs["code.lineno"] === "number" ? attrs["code.lineno"] : void 0;
1401
+ const filepath = codeFilepathOf(attrs);
1402
+ const lineno = codeLinenoOf(attrs);
1394
1403
  const route = typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0;
1395
1404
  const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
1396
1405
  const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage);
@@ -1462,26 +1471,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
1462
1471
  return best;
1463
1472
  }
1464
1473
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1465
- const path52 = [originServiceId];
1474
+ const path53 = [originServiceId];
1466
1475
  const edges = [];
1467
1476
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1468
1477
  let current = originServiceId;
1469
1478
  for (let depth = 0; depth < maxDepth; depth++) {
1470
1479
  const hop = dominantFailingCall(graph, current, visited);
1471
1480
  if (!hop) break;
1472
- path52.push(hop.nextService);
1481
+ path53.push(hop.nextService);
1473
1482
  edges.push(hop.edge);
1474
1483
  visited.add(hop.nextService);
1475
1484
  current = hop.nextService;
1476
1485
  }
1477
1486
  if (edges.length === 0) return null;
1478
- return { path: path52, edges, culprit: current };
1487
+ return { path: path53, edges, culprit: current };
1479
1488
  }
1480
1489
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1481
1490
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1482
1491
  if (!chain) return null;
1483
1492
  const culprit = chain.culprit;
1484
- const path52 = [...chain.path];
1493
+ const path53 = [...chain.path];
1485
1494
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1486
1495
  const baseConfidence = confidenceFromMix(chain.edges);
1487
1496
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1489,14 +1498,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1489
1498
  if (loc) {
1490
1499
  let rootCauseNode = culprit;
1491
1500
  if (loc.fileNode) {
1492
- path52.push(loc.fileNode);
1501
+ path53.push(loc.fileNode);
1493
1502
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1494
1503
  rootCauseNode = loc.fileNode;
1495
1504
  }
1496
1505
  return import_types.RootCauseResultSchema.parse({
1497
1506
  rootCauseNode,
1498
1507
  rootCauseReason: loc.rootCauseReason,
1499
- traversalPath: path52,
1508
+ traversalPath: path53,
1500
1509
  edgeProvenances,
1501
1510
  confidence,
1502
1511
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1508,7 +1517,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1508
1517
  return import_types.RootCauseResultSchema.parse({
1509
1518
  rootCauseNode: culprit,
1510
1519
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1511
- traversalPath: path52,
1520
+ traversalPath: path53,
1512
1521
  edgeProvenances,
1513
1522
  confidence,
1514
1523
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2266,9 +2275,30 @@ function isLoopbackHost(host) {
2266
2275
  const h = host.toLowerCase();
2267
2276
  return h === "localhost" || h === "ip6-localhost" || h === "::1" || h === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(h);
2268
2277
  }
2278
+ var CODE_FILE_PATH_ATTR = "code.file.path";
2269
2279
  var CODE_FILEPATH_ATTR = "code.filepath";
2280
+ var CODE_LINE_NUMBER_ATTR = "code.line.number";
2270
2281
  var CODE_LINENO_ATTR = "code.lineno";
2282
+ var CODE_FUNCTION_NAME_ATTR = "code.function.name";
2271
2283
  var CODE_FUNCTION_ATTR = "code.function";
2284
+ function codeFilepathOf(attrs) {
2285
+ const stable = attrs[CODE_FILE_PATH_ATTR];
2286
+ if (typeof stable === "string" && stable.length > 0) return stable;
2287
+ const prior = attrs[CODE_FILEPATH_ATTR];
2288
+ return typeof prior === "string" && prior.length > 0 ? prior : void 0;
2289
+ }
2290
+ function codeLinenoOf(attrs) {
2291
+ const stable = attrs[CODE_LINE_NUMBER_ATTR];
2292
+ if (typeof stable === "number" && Number.isFinite(stable)) return stable;
2293
+ const prior = attrs[CODE_LINENO_ATTR];
2294
+ return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
2295
+ }
2296
+ function codeFunctionOf(attrs) {
2297
+ const stable = attrs[CODE_FUNCTION_NAME_ATTR];
2298
+ if (typeof stable === "string" && stable.length > 0) return stable;
2299
+ const prior = attrs[CODE_FUNCTION_ATTR];
2300
+ return typeof prior === "string" && prior.length > 0 ? prior : void 0;
2301
+ }
2272
2302
  function toPosix(p) {
2273
2303
  return p.split("\\").join("/");
2274
2304
  }
@@ -2346,10 +2376,9 @@ function resolveDistToSrc(absFilepath, line) {
2346
2376
  }
2347
2377
  }
2348
2378
  function callSiteFromSpan(span, serviceNode, scanPath) {
2349
- const filepath = span.attributes[CODE_FILEPATH_ATTR];
2350
- if (typeof filepath !== "string" || filepath.length === 0) return null;
2351
- const linenoRaw = span.attributes[CODE_LINENO_ATTR];
2352
- let line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
2379
+ const filepath = codeFilepathOf(span.attributes);
2380
+ if (filepath === void 0) return null;
2381
+ let line = codeLinenoOf(span.attributes);
2353
2382
  const abs = toPosix(filepath).replace(/^file:\/\//, "");
2354
2383
  const resolved = resolveDistToSrc(abs, line);
2355
2384
  let effectivePath = filepath;
@@ -2364,8 +2393,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
2364
2393
  if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
2365
2394
  warnNoSourceMaps(serviceNode.name);
2366
2395
  }
2367
- const fnRaw = span.attributes[CODE_FUNCTION_ATTR];
2368
- const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
2396
+ const fn = codeFunctionOf(span.attributes);
2369
2397
  return {
2370
2398
  relPath,
2371
2399
  ...line !== void 0 ? { line } : {},
@@ -2947,6 +2975,18 @@ async function handleSpan(ctx, span) {
2947
2975
  callSiteEvidence
2948
2976
  );
2949
2977
  }
2978
+ if (span.dbTable) {
2979
+ const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
2980
+ upsertObservedEdge(
2981
+ ctx.graph,
2982
+ import_types3.EdgeType.CALLS,
2983
+ observedSource(),
2984
+ tableId,
2985
+ ts,
2986
+ isError,
2987
+ callSiteEvidence
2988
+ );
2989
+ }
2950
2990
  }
2951
2991
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
2952
2992
  const targetId = ensureMessagingDestinationNode(
@@ -4114,14 +4154,14 @@ function buildServiceHostIndex(services) {
4114
4154
  }
4115
4155
  async function walkSourceFiles(dir) {
4116
4156
  const out = [];
4117
- async function walk3(current) {
4157
+ async function walk4(current) {
4118
4158
  const entries = await import_node_fs10.promises.readdir(current, { withFileTypes: true }).catch(() => []);
4119
4159
  for (const entry of entries) {
4120
4160
  const full = import_node_path10.default.join(current, entry.name);
4121
4161
  if (entry.isDirectory()) {
4122
4162
  if (IGNORED_DIRS.has(entry.name)) continue;
4123
4163
  if (await isPythonVenvDir(full)) continue;
4124
- await walk3(full);
4164
+ await walk4(full);
4125
4165
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path10.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
4126
4166
  // would attribute our instrumentation imports to the user's service.
4127
4167
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -4129,7 +4169,7 @@ async function walkSourceFiles(dir) {
4129
4169
  }
4130
4170
  }
4131
4171
  }
4132
- await walk3(dir);
4172
+ await walk4(dir);
4133
4173
  return out;
4134
4174
  }
4135
4175
  async function loadSourceFiles(dir) {
@@ -5279,20 +5319,20 @@ var import_node_path21 = __toESM(require("path"), 1);
5279
5319
  var import_types10 = require("@neat.is/types");
5280
5320
  async function walkConfigFiles(dir) {
5281
5321
  const out = [];
5282
- async function walk3(current) {
5322
+ async function walk4(current) {
5283
5323
  const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
5284
5324
  for (const entry of entries) {
5285
5325
  const full = import_node_path21.default.join(current, entry.name);
5286
5326
  if (entry.isDirectory()) {
5287
5327
  if (IGNORED_DIRS.has(entry.name)) continue;
5288
5328
  if (await isPythonVenvDir(full)) continue;
5289
- await walk3(full);
5329
+ await walk4(full);
5290
5330
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
5291
5331
  out.push(full);
5292
5332
  }
5293
5333
  }
5294
5334
  }
5295
- await walk3(dir);
5335
+ await walk4(dir);
5296
5336
  return out;
5297
5337
  }
5298
5338
  async function addConfigNodes(graph, services, scanPath) {
@@ -5819,20 +5859,20 @@ function grpcMethodsFromProto(content, fqPackage) {
5819
5859
  }
5820
5860
  async function walkProtoFiles(dir) {
5821
5861
  const out = [];
5822
- async function walk3(current) {
5862
+ async function walk4(current) {
5823
5863
  const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true }).catch(() => []);
5824
5864
  for (const entry of entries) {
5825
5865
  const full = import_node_path23.default.join(current, entry.name);
5826
5866
  if (entry.isDirectory()) {
5827
5867
  if (IGNORED_DIRS.has(entry.name)) continue;
5828
5868
  if (await isPythonVenvDir(full)) continue;
5829
- await walk3(full);
5869
+ await walk4(full);
5830
5870
  } else if (entry.isFile() && import_node_path23.default.extname(entry.name) === PROTO_EXTENSION) {
5831
5871
  out.push(full);
5832
5872
  }
5833
5873
  }
5834
5874
  }
5835
- await walk3(dir);
5875
+ await walk4(dir);
5836
5876
  return out;
5837
5877
  }
5838
5878
  async function addGrpcMethods(graph, services) {
@@ -5900,7 +5940,7 @@ async function addGrpcMethods(graph, services) {
5900
5940
 
5901
5941
  // src/extract/calls/index.ts
5902
5942
  init_cjs_shims();
5903
- var import_types21 = require("@neat.is/types");
5943
+ var import_types22 = require("@neat.is/types");
5904
5944
 
5905
5945
  // src/extract/calls/http.ts
5906
5946
  init_cjs_shims();
@@ -6909,15 +6949,130 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
6909
6949
  return out;
6910
6950
  }
6911
6951
 
6952
+ // src/extract/calls/sqlalchemy.ts
6953
+ init_cjs_shims();
6954
+ var import_node_path32 = __toESM(require("path"), 1);
6955
+ var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
6956
+ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
6957
+ var import_types21 = require("@neat.is/types");
6958
+ var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
6959
+ var PARSE_CHUNK5 = 16384;
6960
+ function makePyParser4() {
6961
+ const p = new import_tree_sitter5.default();
6962
+ p.setLanguage(import_tree_sitter_python4.default);
6963
+ return p;
6964
+ }
6965
+ function parseSource5(parser, source) {
6966
+ return parser.parse(
6967
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
6968
+ );
6969
+ }
6970
+ function flaskSqlalchemyTableName(className) {
6971
+ return className.replace(/((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g, "_$1").toLowerCase().replace(/^_+/, "");
6972
+ }
6973
+ function namedChildren(node) {
6974
+ const out = [];
6975
+ for (let i = 0; i < node.namedChildCount; i++) {
6976
+ const c = node.namedChild(i);
6977
+ if (c) out.push(c);
6978
+ }
6979
+ return out;
6980
+ }
6981
+ function pyStaticStringText2(node) {
6982
+ if (node.type !== "string") return null;
6983
+ for (const child of namedChildren(node)) {
6984
+ if (child.type === "interpolation") return null;
6985
+ if (child.type === "string_content") return child.text;
6986
+ }
6987
+ return "";
6988
+ }
6989
+ function explicitTablename(body) {
6990
+ for (const stmt of namedChildren(body)) {
6991
+ if (stmt.type !== "expression_statement") continue;
6992
+ const assign = stmt.namedChild(0);
6993
+ if (assign?.type !== "assignment") continue;
6994
+ if (assign.childForFieldName("left")?.text !== "__tablename__") continue;
6995
+ const right = assign.childForFieldName("right");
6996
+ if (right?.type === "string") {
6997
+ const s = pyStaticStringText2(right);
6998
+ return s ? { name: s } : "computed";
6999
+ }
7000
+ return "computed";
7001
+ }
7002
+ return null;
7003
+ }
7004
+ function extendsFlaskModel(cls) {
7005
+ const supers = cls.childForFieldName("superclasses");
7006
+ if (!supers) return false;
7007
+ for (const a of namedChildren(supers)) {
7008
+ const t = a.text;
7009
+ if (t === "db.Model" || t === "Model" || t.endsWith(".Model")) return true;
7010
+ }
7011
+ return false;
7012
+ }
7013
+ function walk3(node, visit) {
7014
+ visit(node);
7015
+ for (const c of namedChildren(node)) walk3(c, visit);
7016
+ }
7017
+ function sqlalchemyEndpointsFromFile(file, serviceDir) {
7018
+ if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
7019
+ const tree = parseSource5(makePyParser4(), file.content);
7020
+ const out = [];
7021
+ const seen = /* @__PURE__ */ new Set();
7022
+ const push = (name, line) => {
7023
+ if (seen.has(name)) return;
7024
+ seen.add(name);
7025
+ out.push({
7026
+ infraId: (0, import_types21.infraId)("sql-table", name),
7027
+ name,
7028
+ kind: "sql-table",
7029
+ edgeType: "CALLS",
7030
+ confidenceKind: "verified-call-site",
7031
+ evidence: {
7032
+ file: import_node_path32.default.relative(serviceDir, file.path),
7033
+ line,
7034
+ snippet: snippet(file.content, line)
7035
+ }
7036
+ });
7037
+ };
7038
+ walk3(tree.rootNode, (node) => {
7039
+ if (node.type === "class_definition") {
7040
+ const body = node.childForFieldName("body");
7041
+ const nameNode = node.childForFieldName("name");
7042
+ if (!body || !nameNode) return;
7043
+ const line = node.startPosition.row + 1;
7044
+ const explicit = explicitTablename(body);
7045
+ if (explicit === "computed") return;
7046
+ if (explicit) {
7047
+ push(explicit.name, line);
7048
+ return;
7049
+ }
7050
+ if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
7051
+ return;
7052
+ }
7053
+ if (node.type === "call") {
7054
+ const fn = node.childForFieldName("function");
7055
+ const fnText = fn?.text;
7056
+ if (fnText !== "Table" && fnText !== "sa.Table" && fnText !== "sqlalchemy.Table") return;
7057
+ const first = node.childForFieldName("arguments")?.namedChild(0);
7058
+ if (first?.type === "string") {
7059
+ const s = pyStaticStringText2(first);
7060
+ if (s) push(s, node.startPosition.row + 1);
7061
+ }
7062
+ }
7063
+ });
7064
+ return out;
7065
+ }
7066
+
6912
7067
  // src/extract/calls/index.ts
6913
7068
  function edgeTypeFromEndpoint(ep) {
6914
7069
  switch (ep.edgeType) {
6915
7070
  case "PUBLISHES_TO":
6916
- return import_types21.EdgeType.PUBLISHES_TO;
7071
+ return import_types22.EdgeType.PUBLISHES_TO;
6917
7072
  case "CONSUMES_FROM":
6918
- return import_types21.EdgeType.CONSUMES_FROM;
7073
+ return import_types22.EdgeType.CONSUMES_FROM;
6919
7074
  default:
6920
- return import_types21.EdgeType.CALLS;
7075
+ return import_types22.EdgeType.CALLS;
6921
7076
  }
6922
7077
  }
6923
7078
  function isAwsKind(kind) {
@@ -6941,6 +7096,7 @@ async function addExternalEndpointEdges(graph, services) {
6941
7096
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
6942
7097
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
6943
7098
  endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
7099
+ endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
6944
7100
  }
6945
7101
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
6946
7102
  if (endpoints.length === 0) continue;
@@ -6949,7 +7105,7 @@ async function addExternalEndpointEdges(graph, services) {
6949
7105
  if (!graph.hasNode(ep.infraId)) {
6950
7106
  const node = {
6951
7107
  id: ep.infraId,
6952
- type: import_types21.NodeType.InfraNode,
7108
+ type: import_types22.NodeType.InfraNode,
6953
7109
  name: ep.name,
6954
7110
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
6955
7111
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -6961,7 +7117,7 @@ async function addExternalEndpointEdges(graph, services) {
6961
7117
  nodesAdded++;
6962
7118
  }
6963
7119
  const edgeType = edgeTypeFromEndpoint(ep);
6964
- const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
7120
+ const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
6965
7121
  const relFile = toPosix2(ep.evidence.file);
6966
7122
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
6967
7123
  graph,
@@ -6971,7 +7127,7 @@ async function addExternalEndpointEdges(graph, services) {
6971
7127
  );
6972
7128
  nodesAdded += n;
6973
7129
  edgesAdded += e;
6974
- if (!(0, import_types21.passesExtractedFloor)(confidence)) {
7130
+ if (!(0, import_types22.passesExtractedFloor)(confidence)) {
6975
7131
  noteExtractedDropped({
6976
7132
  source: fileNodeId,
6977
7133
  target: ep.infraId,
@@ -6991,7 +7147,7 @@ async function addExternalEndpointEdges(graph, services) {
6991
7147
  source: fileNodeId,
6992
7148
  target: ep.infraId,
6993
7149
  type: edgeType,
6994
- provenance: import_types21.Provenance.EXTRACTED,
7150
+ provenance: import_types22.Provenance.EXTRACTED,
6995
7151
  confidence,
6996
7152
  evidence: ep.evidence
6997
7153
  };
@@ -7017,16 +7173,16 @@ init_cjs_shims();
7017
7173
 
7018
7174
  // src/extract/infra/docker-compose.ts
7019
7175
  init_cjs_shims();
7020
- var import_node_path32 = __toESM(require("path"), 1);
7021
- var import_types23 = require("@neat.is/types");
7176
+ var import_node_path33 = __toESM(require("path"), 1);
7177
+ var import_types24 = require("@neat.is/types");
7022
7178
 
7023
7179
  // src/extract/infra/shared.ts
7024
7180
  init_cjs_shims();
7025
- var import_types22 = require("@neat.is/types");
7181
+ var import_types23 = require("@neat.is/types");
7026
7182
  function makeInfraNode(kind, name, provider = "self", extras) {
7027
7183
  return {
7028
- id: (0, import_types22.infraId)(kind, name),
7029
- type: import_types22.NodeType.InfraNode,
7184
+ id: (0, import_types23.infraId)(kind, name),
7185
+ type: import_types23.NodeType.InfraNode,
7030
7186
  name,
7031
7187
  provider,
7032
7188
  kind,
@@ -7070,8 +7226,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
7070
7226
  source: anchorId,
7071
7227
  target: node.id,
7072
7228
  type: edgeType,
7073
- provenance: import_types22.Provenance.EXTRACTED,
7074
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7229
+ provenance: import_types23.Provenance.EXTRACTED,
7230
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
7075
7231
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7076
7232
  };
7077
7233
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7088,7 +7244,7 @@ function dependsOnList(value) {
7088
7244
  }
7089
7245
  function serviceNameToServiceNode(name, services) {
7090
7246
  for (const s of services) {
7091
- if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
7247
+ if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
7092
7248
  }
7093
7249
  return null;
7094
7250
  }
@@ -7097,7 +7253,7 @@ async function addComposeInfra(graph, scanPath, services) {
7097
7253
  let edgesAdded = 0;
7098
7254
  let composePath = null;
7099
7255
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
7100
- const abs = import_node_path32.default.join(scanPath, name);
7256
+ const abs = import_node_path33.default.join(scanPath, name);
7101
7257
  if (await exists(abs)) {
7102
7258
  composePath = abs;
7103
7259
  break;
@@ -7110,13 +7266,13 @@ async function addComposeInfra(graph, scanPath, services) {
7110
7266
  } catch (err) {
7111
7267
  recordExtractionError(
7112
7268
  "infra docker-compose",
7113
- import_node_path32.default.relative(scanPath, composePath),
7269
+ import_node_path33.default.relative(scanPath, composePath),
7114
7270
  err
7115
7271
  );
7116
7272
  return { nodesAdded, edgesAdded };
7117
7273
  }
7118
7274
  if (!compose?.services) return { nodesAdded, edgesAdded };
7119
- const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
7275
+ const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
7120
7276
  const composeNameToNodeId = /* @__PURE__ */ new Map();
7121
7277
  for (const [composeName, svc] of Object.entries(compose.services)) {
7122
7278
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -7138,15 +7294,15 @@ async function addComposeInfra(graph, scanPath, services) {
7138
7294
  for (const dep of dependsOnList(svc.depends_on)) {
7139
7295
  const targetId = composeNameToNodeId.get(dep);
7140
7296
  if (!targetId) continue;
7141
- const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
7297
+ const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
7142
7298
  if (graph.hasEdge(edgeId)) continue;
7143
7299
  const edge = {
7144
7300
  id: edgeId,
7145
7301
  source: sourceId,
7146
7302
  target: targetId,
7147
- type: import_types23.EdgeType.DEPENDS_ON,
7148
- provenance: import_types23.Provenance.EXTRACTED,
7149
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7303
+ type: import_types24.EdgeType.DEPENDS_ON,
7304
+ provenance: import_types24.Provenance.EXTRACTED,
7305
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
7150
7306
  evidence: { file: evidenceFile }
7151
7307
  };
7152
7308
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7158,9 +7314,9 @@ async function addComposeInfra(graph, scanPath, services) {
7158
7314
 
7159
7315
  // src/extract/infra/dockerfile.ts
7160
7316
  init_cjs_shims();
7161
- var import_node_path33 = __toESM(require("path"), 1);
7317
+ var import_node_path34 = __toESM(require("path"), 1);
7162
7318
  var import_node_fs16 = require("fs");
7163
- var import_types24 = require("@neat.is/types");
7319
+ var import_types25 = require("@neat.is/types");
7164
7320
  function readDockerfile(content) {
7165
7321
  let image = null;
7166
7322
  const ports = [];
@@ -7189,7 +7345,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7189
7345
  let nodesAdded = 0;
7190
7346
  let edgesAdded = 0;
7191
7347
  for (const service of services) {
7192
- const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
7348
+ const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
7193
7349
  if (!await exists(dockerfilePath)) continue;
7194
7350
  let content;
7195
7351
  try {
@@ -7197,7 +7353,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7197
7353
  } catch (err) {
7198
7354
  recordExtractionError(
7199
7355
  "infra dockerfile",
7200
- import_node_path33.default.relative(scanPath, dockerfilePath),
7356
+ import_node_path34.default.relative(scanPath, dockerfilePath),
7201
7357
  err
7202
7358
  );
7203
7359
  continue;
@@ -7209,8 +7365,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7209
7365
  graph.addNode(node.id, node);
7210
7366
  nodesAdded++;
7211
7367
  }
7212
- const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
7213
- const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
7368
+ const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
7369
+ const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
7214
7370
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7215
7371
  graph,
7216
7372
  service.pkg.name,
@@ -7219,15 +7375,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7219
7375
  );
7220
7376
  nodesAdded += fn;
7221
7377
  edgesAdded += fe;
7222
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
7378
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
7223
7379
  if (!graph.hasEdge(edgeId)) {
7224
7380
  const edge = {
7225
7381
  id: edgeId,
7226
7382
  source: fileNodeId,
7227
7383
  target: node.id,
7228
- type: import_types24.EdgeType.RUNS_ON,
7229
- provenance: import_types24.Provenance.EXTRACTED,
7230
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7384
+ type: import_types25.EdgeType.RUNS_ON,
7385
+ provenance: import_types25.Provenance.EXTRACTED,
7386
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7231
7387
  evidence: {
7232
7388
  file: evidenceFile,
7233
7389
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -7242,15 +7398,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7242
7398
  graph.addNode(portNode.id, portNode);
7243
7399
  nodesAdded++;
7244
7400
  }
7245
- const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
7401
+ const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
7246
7402
  if (graph.hasEdge(portEdgeId)) continue;
7247
7403
  const portEdge = {
7248
7404
  id: portEdgeId,
7249
7405
  source: fileNodeId,
7250
7406
  target: portNode.id,
7251
- type: import_types24.EdgeType.CONNECTS_TO,
7252
- provenance: import_types24.Provenance.EXTRACTED,
7253
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7407
+ type: import_types25.EdgeType.CONNECTS_TO,
7408
+ provenance: import_types25.Provenance.EXTRACTED,
7409
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7254
7410
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
7255
7411
  };
7256
7412
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -7263,8 +7419,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7263
7419
  // src/extract/infra/terraform.ts
7264
7420
  init_cjs_shims();
7265
7421
  var import_node_fs17 = require("fs");
7266
- var import_node_path34 = __toESM(require("path"), 1);
7267
- var import_types25 = require("@neat.is/types");
7422
+ var import_node_path35 = __toESM(require("path"), 1);
7423
+ var import_types26 = require("@neat.is/types");
7268
7424
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
7269
7425
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
7270
7426
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -7274,11 +7430,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
7274
7430
  for (const entry of entries) {
7275
7431
  if (entry.isDirectory()) {
7276
7432
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
7277
- const child = import_node_path34.default.join(start, entry.name);
7433
+ const child = import_node_path35.default.join(start, entry.name);
7278
7434
  if (await isPythonVenvDir(child)) continue;
7279
7435
  out.push(...await walkTfFiles(child, depth + 1, max));
7280
7436
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
7281
- out.push(import_node_path34.default.join(start, entry.name));
7437
+ out.push(import_node_path35.default.join(start, entry.name));
7282
7438
  }
7283
7439
  }
7284
7440
  return out;
@@ -7310,7 +7466,7 @@ async function addTerraformResources(graph, scanPath) {
7310
7466
  const files = await walkTfFiles(scanPath);
7311
7467
  for (const file of files) {
7312
7468
  const content = await import_node_fs17.promises.readFile(file, "utf8");
7313
- const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
7469
+ const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
7314
7470
  const resources = [];
7315
7471
  const byKey = /* @__PURE__ */ new Map();
7316
7472
  RESOURCE_RE.lastIndex = 0;
@@ -7345,16 +7501,16 @@ async function addTerraformResources(graph, scanPath) {
7345
7501
  if (!target) continue;
7346
7502
  if (seen.has(target.nodeId)) continue;
7347
7503
  seen.add(target.nodeId);
7348
- const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
7504
+ const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
7349
7505
  if (graph.hasEdge(edgeId)) continue;
7350
7506
  const line = lineAt2(content, resource.bodyOffset + ref.index);
7351
7507
  const edge = {
7352
7508
  id: edgeId,
7353
7509
  source: resource.nodeId,
7354
7510
  target: target.nodeId,
7355
- type: import_types25.EdgeType.DEPENDS_ON,
7356
- provenance: import_types25.Provenance.EXTRACTED,
7357
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7511
+ type: import_types26.EdgeType.DEPENDS_ON,
7512
+ provenance: import_types26.Provenance.EXTRACTED,
7513
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7358
7514
  evidence: { file: evidenceFile, line, snippet: key }
7359
7515
  };
7360
7516
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7368,7 +7524,7 @@ async function addTerraformResources(graph, scanPath) {
7368
7524
  // src/extract/infra/k8s.ts
7369
7525
  init_cjs_shims();
7370
7526
  var import_node_fs18 = require("fs");
7371
- var import_node_path35 = __toESM(require("path"), 1);
7527
+ var import_node_path36 = __toESM(require("path"), 1);
7372
7528
  var import_yaml3 = require("yaml");
7373
7529
  var K8S_KIND_TO_INFRA_KIND = {
7374
7530
  Service: "k8s-service",
@@ -7386,11 +7542,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
7386
7542
  for (const entry of entries) {
7387
7543
  if (entry.isDirectory()) {
7388
7544
  if (IGNORED_DIRS.has(entry.name)) continue;
7389
- const child = import_node_path35.default.join(start, entry.name);
7545
+ const child = import_node_path36.default.join(start, entry.name);
7390
7546
  if (await isPythonVenvDir(child)) continue;
7391
7547
  out.push(...await walkYamlFiles2(child, depth + 1, max));
7392
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry.name))) {
7393
- out.push(import_node_path35.default.join(start, entry.name));
7548
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry.name))) {
7549
+ out.push(import_node_path36.default.join(start, entry.name));
7394
7550
  }
7395
7551
  }
7396
7552
  return out;
@@ -7424,13 +7580,13 @@ async function addK8sResources(graph, scanPath) {
7424
7580
  // src/extract/infra/cloudflare.ts
7425
7581
  init_cjs_shims();
7426
7582
  var import_node_fs19 = require("fs");
7427
- var import_node_path36 = __toESM(require("path"), 1);
7583
+ var import_node_path37 = __toESM(require("path"), 1);
7428
7584
  var import_smol_toml2 = require("smol-toml");
7429
- var import_types26 = require("@neat.is/types");
7585
+ var import_types27 = require("@neat.is/types");
7430
7586
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7431
7587
  async function readWranglerConfig(dir) {
7432
7588
  for (const filename of WRANGLER_FILENAMES) {
7433
- const abs = import_node_path36.default.join(dir, filename);
7589
+ const abs = import_node_path37.default.join(dir, filename);
7434
7590
  if (!await exists(abs)) continue;
7435
7591
  const raw = await import_node_fs19.promises.readFile(abs, "utf8");
7436
7592
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7474,8 +7630,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7474
7630
  source: anchorId,
7475
7631
  target: node.id,
7476
7632
  type: edgeType,
7477
- provenance: import_types26.Provenance.EXTRACTED,
7478
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7633
+ provenance: import_types27.Provenance.EXTRACTED,
7634
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7479
7635
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7480
7636
  };
7481
7637
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7493,11 +7649,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7493
7649
  try {
7494
7650
  read = await readWranglerConfig(service.dir);
7495
7651
  } catch (err) {
7496
- recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
7652
+ recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
7497
7653
  continue;
7498
7654
  }
7499
7655
  if (!read || !read.config.name) continue;
7500
- const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
7656
+ const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
7501
7657
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7502
7658
  }
7503
7659
  for (const worker of discovered) {
@@ -7509,7 +7665,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7509
7665
  }
7510
7666
  let anchorId = service.node.id;
7511
7667
  if (config.main) {
7512
- const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
7668
+ const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
7513
7669
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7514
7670
  graph,
7515
7671
  service.pkg.name,
@@ -7536,15 +7692,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7536
7692
  nodesAdded++;
7537
7693
  }
7538
7694
  if (runtimeNode.id !== anchorId) {
7539
- const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
7695
+ const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
7540
7696
  if (!graph.hasEdge(runsOnId)) {
7541
7697
  const edge = {
7542
7698
  id: runsOnId,
7543
7699
  source: anchorId,
7544
7700
  target: runtimeNode.id,
7545
- type: import_types26.EdgeType.RUNS_ON,
7546
- provenance: import_types26.Provenance.EXTRACTED,
7547
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7701
+ type: import_types27.EdgeType.RUNS_ON,
7702
+ provenance: import_types27.Provenance.EXTRACTED,
7703
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7548
7704
  evidence: {
7549
7705
  file: evidenceFile,
7550
7706
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7558,7 +7714,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7558
7714
  const result = addResourceEdge(
7559
7715
  graph,
7560
7716
  anchorId,
7561
- import_types26.EdgeType.CONNECTS_TO,
7717
+ import_types27.EdgeType.CONNECTS_TO,
7562
7718
  "cloudflare-route",
7563
7719
  route,
7564
7720
  evidenceFile,
@@ -7582,7 +7738,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7582
7738
  const result = addResourceEdge(
7583
7739
  graph,
7584
7740
  anchorId,
7585
- import_types26.EdgeType.DEPENDS_ON,
7741
+ import_types27.EdgeType.DEPENDS_ON,
7586
7742
  group.kind,
7587
7743
  name,
7588
7744
  evidenceFile,
@@ -7596,7 +7752,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7596
7752
  const result = addResourceEdge(
7597
7753
  graph,
7598
7754
  anchorId,
7599
- import_types26.EdgeType.DEPENDS_ON,
7755
+ import_types27.EdgeType.DEPENDS_ON,
7600
7756
  "cloudflare-cron",
7601
7757
  cron,
7602
7758
  evidenceFile,
@@ -7609,7 +7765,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7609
7765
  const result = addResourceEdge(
7610
7766
  graph,
7611
7767
  anchorId,
7612
- import_types26.EdgeType.DEPENDS_ON,
7768
+ import_types27.EdgeType.DEPENDS_ON,
7613
7769
  "cloudflare-env-var",
7614
7770
  varName,
7615
7771
  evidenceFile,
@@ -7622,15 +7778,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7622
7778
  if (!svc.service) continue;
7623
7779
  const target = workerIndex.get(svc.service);
7624
7780
  if (target && target.anchorId !== anchorId) {
7625
- const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
7781
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
7626
7782
  if (!graph.hasEdge(edgeId)) {
7627
7783
  const edge = {
7628
7784
  id: edgeId,
7629
7785
  source: anchorId,
7630
7786
  target: target.anchorId,
7631
- type: import_types26.EdgeType.CALLS,
7632
- provenance: import_types26.Provenance.EXTRACTED,
7633
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7787
+ type: import_types27.EdgeType.CALLS,
7788
+ provenance: import_types27.Provenance.EXTRACTED,
7789
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7634
7790
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7635
7791
  };
7636
7792
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7641,7 +7797,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7641
7797
  const result = addResourceEdge(
7642
7798
  graph,
7643
7799
  anchorId,
7644
- import_types26.EdgeType.DEPENDS_ON,
7800
+ import_types27.EdgeType.DEPENDS_ON,
7645
7801
  "cloudflare-service-binding",
7646
7802
  svc.service,
7647
7803
  evidenceFile,
@@ -7657,12 +7813,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7657
7813
  // src/extract/infra/vercel.ts
7658
7814
  init_cjs_shims();
7659
7815
  var import_node_fs20 = require("fs");
7660
- var import_node_path37 = __toESM(require("path"), 1);
7661
- var import_types27 = require("@neat.is/types");
7816
+ var import_node_path38 = __toESM(require("path"), 1);
7817
+ var import_types28 = require("@neat.is/types");
7662
7818
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7663
7819
  async function readVercelConfig(dir) {
7664
7820
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7665
- const abs = import_node_path37.default.join(dir, filename);
7821
+ const abs = import_node_path38.default.join(dir, filename);
7666
7822
  if (!await exists(abs)) continue;
7667
7823
  const raw = await import_node_fs20.promises.readFile(abs, "utf8");
7668
7824
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7671,7 +7827,7 @@ async function readVercelConfig(dir) {
7671
7827
  return null;
7672
7828
  }
7673
7829
  async function readLinkedProjectName(dir) {
7674
- const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
7830
+ const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
7675
7831
  if (!await exists(abs)) return void 0;
7676
7832
  const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
7677
7833
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7689,7 +7845,7 @@ async function addVercelServices(graph, services, scanPath) {
7689
7845
  read = await readVercelConfig(service.dir);
7690
7846
  projectName = await readLinkedProjectName(service.dir);
7691
7847
  } catch (err) {
7692
- recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
7848
+ recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
7693
7849
  continue;
7694
7850
  }
7695
7851
  if (!read && !projectName) continue;
@@ -7705,7 +7861,7 @@ async function addVercelServices(graph, services, scanPath) {
7705
7861
  const anchorId = service.node.id;
7706
7862
  if (!read) continue;
7707
7863
  const { config, relFile, raw } = read;
7708
- const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
7864
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7709
7865
  const add = (edgeType, kind, name) => {
7710
7866
  if (!name) return;
7711
7867
  const result = emitPlatformResourceEdge(
@@ -7721,12 +7877,12 @@ async function addVercelServices(graph, services, scanPath) {
7721
7877
  nodesAdded += result.nodesAdded;
7722
7878
  edgesAdded += result.edgesAdded;
7723
7879
  };
7724
- add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7725
- for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7726
- for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7727
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7880
+ add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
7881
+ for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7882
+ for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7883
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7728
7884
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7729
- add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7885
+ add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7730
7886
  }
7731
7887
  }
7732
7888
  return { nodesAdded, edgesAdded };
@@ -7735,13 +7891,13 @@ async function addVercelServices(graph, services, scanPath) {
7735
7891
  // src/extract/infra/railway.ts
7736
7892
  init_cjs_shims();
7737
7893
  var import_node_fs21 = require("fs");
7738
- var import_node_path38 = __toESM(require("path"), 1);
7894
+ var import_node_path39 = __toESM(require("path"), 1);
7739
7895
  var import_smol_toml3 = require("smol-toml");
7740
- var import_types28 = require("@neat.is/types");
7896
+ var import_types29 = require("@neat.is/types");
7741
7897
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7742
7898
  async function readRailwayConfig(dir) {
7743
7899
  for (const filename of RAILWAY_FILENAMES) {
7744
- const abs = import_node_path38.default.join(dir, filename);
7900
+ const abs = import_node_path39.default.join(dir, filename);
7745
7901
  if (!await exists(abs)) continue;
7746
7902
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7747
7903
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7757,7 +7913,7 @@ async function addRailwayServices(graph, services, scanPath) {
7757
7913
  try {
7758
7914
  read = await readRailwayConfig(service.dir);
7759
7915
  } catch (err) {
7760
- recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
7916
+ recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
7761
7917
  continue;
7762
7918
  }
7763
7919
  if (!read) continue;
@@ -7767,7 +7923,7 @@ async function addRailwayServices(graph, services, scanPath) {
7767
7923
  }
7768
7924
  const anchorId = service.node.id;
7769
7925
  const { config, relFile, raw } = read;
7770
- const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7926
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7771
7927
  const add = (edgeType, kind, name) => {
7772
7928
  if (!name) return;
7773
7929
  const result = emitPlatformResourceEdge(
@@ -7783,9 +7939,9 @@ async function addRailwayServices(graph, services, scanPath) {
7783
7939
  nodesAdded += result.nodesAdded;
7784
7940
  edgesAdded += result.edgesAdded;
7785
7941
  };
7786
- add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7787
- add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7788
- add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7942
+ add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
7943
+ add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7944
+ add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7789
7945
  }
7790
7946
  return { nodesAdded, edgesAdded };
7791
7947
  }
@@ -7793,12 +7949,12 @@ async function addRailwayServices(graph, services, scanPath) {
7793
7949
  // src/extract/infra/supabase.ts
7794
7950
  init_cjs_shims();
7795
7951
  var import_node_fs22 = require("fs");
7796
- var import_node_path39 = __toESM(require("path"), 1);
7952
+ var import_node_path40 = __toESM(require("path"), 1);
7797
7953
  var import_smol_toml4 = require("smol-toml");
7798
- var import_types29 = require("@neat.is/types");
7954
+ var import_types30 = require("@neat.is/types");
7799
7955
  async function readSupabaseConfig(dir) {
7800
- const relFile = import_node_path39.default.join("supabase", "config.toml");
7801
- const abs = import_node_path39.default.join(dir, relFile);
7956
+ const relFile = import_node_path40.default.join("supabase", "config.toml");
7957
+ const abs = import_node_path40.default.join(dir, relFile);
7802
7958
  if (!await exists(abs)) return null;
7803
7959
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7804
7960
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7812,7 +7968,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7812
7968
  try {
7813
7969
  read = await readSupabaseConfig(service.dir);
7814
7970
  } catch (err) {
7815
- recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
7971
+ recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
7816
7972
  continue;
7817
7973
  }
7818
7974
  if (!read) continue;
@@ -7827,7 +7983,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7827
7983
  });
7828
7984
  }
7829
7985
  const anchorId = service.node.id;
7830
- const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7986
+ const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
7831
7987
  const add = (edgeType, kind, name) => {
7832
7988
  if (!name) return;
7833
7989
  const result = emitPlatformResourceEdge(
@@ -7843,10 +7999,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7843
7999
  nodesAdded += result.nodesAdded;
7844
8000
  edgesAdded += result.edgesAdded;
7845
8001
  };
7846
- add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7847
- for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7848
- if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7849
- if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
8002
+ add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
8003
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
8004
+ if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
8005
+ if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7850
8006
  }
7851
8007
  return { nodesAdded, edgesAdded };
7852
8008
  }
@@ -7868,17 +8024,17 @@ async function addInfra(graph, scanPath, services) {
7868
8024
  }
7869
8025
 
7870
8026
  // src/extract/index.ts
7871
- var import_node_path41 = __toESM(require("path"), 1);
8027
+ var import_node_path42 = __toESM(require("path"), 1);
7872
8028
 
7873
8029
  // src/extract/retire.ts
7874
8030
  init_cjs_shims();
7875
8031
  var import_node_fs23 = require("fs");
7876
- var import_node_path40 = __toESM(require("path"), 1);
7877
- var import_types30 = require("@neat.is/types");
8032
+ var import_node_path41 = __toESM(require("path"), 1);
8033
+ var import_types31 = require("@neat.is/types");
7878
8034
  function dropOrphanedFileNodes(graph) {
7879
8035
  const orphans = [];
7880
8036
  graph.forEachNode((id, attrs) => {
7881
- if (attrs.type !== import_types30.NodeType.FileNode) return;
8037
+ if (attrs.type !== import_types31.NodeType.FileNode) return;
7882
8038
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7883
8039
  orphans.push(id);
7884
8040
  }
@@ -7891,14 +8047,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7891
8047
  const bases = [scanPath, ...serviceDirs];
7892
8048
  graph.forEachEdge((id, attrs) => {
7893
8049
  const edge = attrs;
7894
- if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
8050
+ if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
7895
8051
  const evidenceFile = edge.evidence?.file;
7896
8052
  if (!evidenceFile) return;
7897
- if (import_node_path40.default.isAbsolute(evidenceFile)) {
8053
+ if (import_node_path41.default.isAbsolute(evidenceFile)) {
7898
8054
  if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
7899
8055
  return;
7900
8056
  }
7901
- const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path40.default.join(base, evidenceFile)));
8057
+ const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path41.default.join(base, evidenceFile)));
7902
8058
  if (!found) toDrop.push(id);
7903
8059
  });
7904
8060
  for (const id of toDrop) graph.dropEdge(id);
@@ -7940,7 +8096,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7940
8096
  }
7941
8097
  const droppedEntries = drainDroppedExtracted();
7942
8098
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7943
- const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
8099
+ const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
7944
8100
  try {
7945
8101
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7946
8102
  } catch (err) {
@@ -7975,8 +8131,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7975
8131
  // src/persist.ts
7976
8132
  init_cjs_shims();
7977
8133
  var import_node_fs24 = require("fs");
7978
- var import_node_path42 = __toESM(require("path"), 1);
7979
- var import_types31 = require("@neat.is/types");
8134
+ var import_node_path43 = __toESM(require("path"), 1);
8135
+ var import_types32 = require("@neat.is/types");
7980
8136
  var SCHEMA_VERSION = 4;
7981
8137
  function migrateV1ToV2(payload) {
7982
8138
  const nodes = payload.graph.nodes;
@@ -7998,12 +8154,12 @@ function migrateV2ToV3(payload) {
7998
8154
  for (const edge of edges) {
7999
8155
  const attrs = edge.attributes;
8000
8156
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
8001
- attrs.provenance = import_types31.Provenance.OBSERVED;
8157
+ attrs.provenance = import_types32.Provenance.OBSERVED;
8002
8158
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
8003
8159
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
8004
8160
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
8005
8161
  if (type && source && target) {
8006
- const newId = (0, import_types31.observedEdgeId)(source, target, type);
8162
+ const newId = (0, import_types32.observedEdgeId)(source, target, type);
8007
8163
  attrs.id = newId;
8008
8164
  if (edge.key) edge.key = newId;
8009
8165
  }
@@ -8012,7 +8168,7 @@ function migrateV2ToV3(payload) {
8012
8168
  return { ...payload, schemaVersion: 3 };
8013
8169
  }
8014
8170
  async function ensureDir(filePath) {
8015
- await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
8171
+ await import_node_fs24.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
8016
8172
  }
8017
8173
  async function saveGraphToDisk(graph, outPath) {
8018
8174
  await ensureDir(outPath);
@@ -8095,19 +8251,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
8095
8251
  init_cjs_shims();
8096
8252
  var import_fastify = __toESM(require("fastify"), 1);
8097
8253
  var import_cors = __toESM(require("@fastify/cors"), 1);
8098
- var import_types34 = require("@neat.is/types");
8254
+ var import_types35 = require("@neat.is/types");
8099
8255
 
8100
8256
  // src/extend/index.ts
8101
8257
  init_cjs_shims();
8102
8258
  var import_node_fs26 = require("fs");
8103
- var import_node_path44 = __toESM(require("path"), 1);
8259
+ var import_node_path45 = __toESM(require("path"), 1);
8104
8260
  var import_node_os2 = __toESM(require("os"), 1);
8105
8261
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
8106
8262
 
8107
8263
  // src/installers/package-manager.ts
8108
8264
  init_cjs_shims();
8109
8265
  var import_node_fs25 = require("fs");
8110
- var import_node_path43 = __toESM(require("path"), 1);
8266
+ var import_node_path44 = __toESM(require("path"), 1);
8111
8267
  var import_node_child_process = require("child_process");
8112
8268
  var LOCKFILE_PRIORITY = [
8113
8269
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -8129,22 +8285,22 @@ async function exists2(p) {
8129
8285
  }
8130
8286
  }
8131
8287
  async function detectPackageManager(serviceDir) {
8132
- let dir = import_node_path43.default.resolve(serviceDir);
8288
+ let dir = import_node_path44.default.resolve(serviceDir);
8133
8289
  const stops = /* @__PURE__ */ new Set();
8134
8290
  for (let i = 0; i < 64; i++) {
8135
8291
  if (stops.has(dir)) break;
8136
8292
  stops.add(dir);
8137
8293
  for (const candidate of LOCKFILE_PRIORITY) {
8138
- const lockPath = import_node_path43.default.join(dir, candidate.lockfile);
8294
+ const lockPath = import_node_path44.default.join(dir, candidate.lockfile);
8139
8295
  if (await exists2(lockPath)) {
8140
8296
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
8141
8297
  }
8142
8298
  }
8143
- const parent = import_node_path43.default.dirname(dir);
8299
+ const parent = import_node_path44.default.dirname(dir);
8144
8300
  if (parent === dir) break;
8145
8301
  dir = parent;
8146
8302
  }
8147
- return { pm: "npm", cwd: import_node_path43.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8303
+ return { pm: "npm", cwd: import_node_path44.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8148
8304
  }
8149
8305
  async function runPackageManagerInstall(cmd) {
8150
8306
  return new Promise((resolve) => {
@@ -8193,7 +8349,7 @@ async function fileExists2(p) {
8193
8349
  }
8194
8350
  }
8195
8351
  async function readPackageJson(scanPath) {
8196
- const pkgPath = import_node_path44.default.join(scanPath, "package.json");
8352
+ const pkgPath = import_node_path45.default.join(scanPath, "package.json");
8197
8353
  const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
8198
8354
  return JSON.parse(raw);
8199
8355
  }
@@ -8207,27 +8363,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
8207
8363
  ]);
8208
8364
  async function findHookFiles(scanPath) {
8209
8365
  const found = [];
8210
- const walk3 = async (dir) => {
8366
+ const walk4 = async (dir) => {
8211
8367
  const entries = await import_node_fs26.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
8212
8368
  for (const entry of entries) {
8213
8369
  if (entry.isDirectory()) {
8214
8370
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
8215
- await walk3(import_node_path44.default.join(dir, entry.name));
8371
+ await walk4(import_node_path45.default.join(dir, entry.name));
8216
8372
  } else if (entry.isFile()) {
8217
8373
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
8218
- const rel = import_node_path44.default.relative(scanPath, import_node_path44.default.join(dir, entry.name));
8219
- found.push(rel.split(import_node_path44.default.sep).join("/"));
8374
+ const rel = import_node_path45.default.relative(scanPath, import_node_path45.default.join(dir, entry.name));
8375
+ found.push(rel.split(import_node_path45.default.sep).join("/"));
8220
8376
  }
8221
8377
  }
8222
8378
  }
8223
8379
  };
8224
- await walk3(scanPath);
8380
+ await walk4(scanPath);
8225
8381
  return found.sort();
8226
8382
  }
8227
8383
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8228
8384
  let fallback = null;
8229
8385
  for (const file of hookFiles) {
8230
- const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(scanPath, file), "utf8");
8386
+ const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(scanPath, file), "utf8");
8231
8387
  const patched = splicedContent(content, snippet2);
8232
8388
  if (patched !== null) return { file, content, patched };
8233
8389
  if (fallback === null) fallback = { file, content };
@@ -8235,11 +8391,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8235
8391
  return { file: fallback.file, content: fallback.content, patched: null };
8236
8392
  }
8237
8393
  function extendLogPath() {
8238
- return process.env.NEAT_EXTEND_LOG ?? import_node_path44.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8394
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path45.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8239
8395
  }
8240
8396
  async function appendExtendLog(entry) {
8241
8397
  const logPath = extendLogPath();
8242
- await import_node_fs26.promises.mkdir(import_node_path44.default.dirname(logPath), { recursive: true });
8398
+ await import_node_fs26.promises.mkdir(import_node_path45.default.dirname(logPath), { recursive: true });
8243
8399
  await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
8244
8400
  }
8245
8401
  function splicedContent(fileContent, snippet2) {
@@ -8298,7 +8454,7 @@ function lookupInstrumentation(library, installedVersion) {
8298
8454
  }
8299
8455
  async function describeProjectInstrumentation(ctx) {
8300
8456
  const hookFiles = await findHookFiles(ctx.scanPath);
8301
- const envNeat = await fileExists2(import_node_path44.default.join(ctx.scanPath, ".env.neat"));
8457
+ const envNeat = await fileExists2(import_node_path45.default.join(ctx.scanPath, ".env.neat"));
8302
8458
  const registryInstrPackages = new Set(
8303
8459
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
8304
8460
  );
@@ -8320,7 +8476,7 @@ async function applyExtension(ctx, args, options) {
8320
8476
  );
8321
8477
  }
8322
8478
  for (const file of hookFiles) {
8323
- const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(ctx.scanPath, file), "utf8");
8479
+ const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
8324
8480
  if (content.includes(args.registration_snippet)) {
8325
8481
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
8326
8482
  }
@@ -8332,10 +8488,10 @@ async function applyExtension(ctx, args, options) {
8332
8488
  );
8333
8489
  }
8334
8490
  const primaryFile = primary.file;
8335
- const primaryPath = import_node_path44.default.join(ctx.scanPath, primaryFile);
8491
+ const primaryPath = import_node_path45.default.join(ctx.scanPath, primaryFile);
8336
8492
  const filesTouched = [];
8337
8493
  const depsAdded = [];
8338
- const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
8494
+ const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
8339
8495
  const pkg = await readPackageJson(ctx.scanPath);
8340
8496
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
8341
8497
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -8374,7 +8530,7 @@ async function dryRunExtension(ctx, args) {
8374
8530
  };
8375
8531
  }
8376
8532
  for (const file of hookFiles) {
8377
- const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(ctx.scanPath, file), "utf8");
8533
+ const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
8378
8534
  if (content.includes(args.registration_snippet)) {
8379
8535
  return {
8380
8536
  library: args.library,
@@ -8415,7 +8571,7 @@ async function rollbackExtension(ctx, args) {
8415
8571
  if (!match) {
8416
8572
  return { undone: false, message: "no apply found for library" };
8417
8573
  }
8418
- const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
8574
+ const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
8419
8575
  if (await fileExists2(pkgPath)) {
8420
8576
  const pkg = await readPackageJson(ctx.scanPath);
8421
8577
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -8426,7 +8582,7 @@ async function rollbackExtension(ctx, args) {
8426
8582
  }
8427
8583
  const hookFiles = await findHookFiles(ctx.scanPath);
8428
8584
  for (const file of hookFiles) {
8429
- const filePath = import_node_path44.default.join(ctx.scanPath, file);
8585
+ const filePath = import_node_path45.default.join(ctx.scanPath, file);
8430
8586
  const content = await import_node_fs26.promises.readFile(filePath, "utf8");
8431
8587
  if (content.includes(match.registration_snippet)) {
8432
8588
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -8442,39 +8598,39 @@ async function rollbackExtension(ctx, args) {
8442
8598
 
8443
8599
  // src/divergences.ts
8444
8600
  init_cjs_shims();
8445
- var import_types32 = require("@neat.is/types");
8601
+ var import_types33 = require("@neat.is/types");
8446
8602
  function bucketKey(source, target, type) {
8447
8603
  return `${type}|${source}|${target}`;
8448
8604
  }
8449
8605
  function bucketSourceFor(graph, edge) {
8450
- if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
8451
- const parsed = (0, import_types32.parseFileId)(edge.source);
8606
+ if (edge.type !== import_types33.EdgeType.CONNECTS_TO) return edge.source;
8607
+ const parsed = (0, import_types33.parseFileId)(edge.source);
8452
8608
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
8453
8609
  const target = graph.getNodeAttributes(edge.target);
8454
- if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
8455
- return (0, import_types32.serviceId)(parsed.service);
8610
+ if (target.type !== import_types33.NodeType.DatabaseNode) return edge.source;
8611
+ return (0, import_types33.serviceId)(parsed.service);
8456
8612
  }
8457
8613
  function bucketEdges(graph) {
8458
8614
  const buckets2 = /* @__PURE__ */ new Map();
8459
8615
  graph.forEachEdge((id, attrs) => {
8460
8616
  const e = attrs;
8461
- const parsed = (0, import_types32.parseEdgeId)(id);
8617
+ const parsed = (0, import_types33.parseEdgeId)(id);
8462
8618
  const provenance = parsed?.provenance ?? e.provenance;
8463
8619
  const source = bucketSourceFor(graph, e);
8464
8620
  const key = bucketKey(source, e.target, e.type);
8465
8621
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
8466
8622
  switch (provenance) {
8467
- case import_types32.Provenance.EXTRACTED:
8623
+ case import_types33.Provenance.EXTRACTED:
8468
8624
  cur.extracted = e;
8469
8625
  break;
8470
- case import_types32.Provenance.OBSERVED:
8626
+ case import_types33.Provenance.OBSERVED:
8471
8627
  cur.observed = e;
8472
8628
  break;
8473
- case import_types32.Provenance.INFERRED:
8629
+ case import_types33.Provenance.INFERRED:
8474
8630
  cur.inferred = e;
8475
8631
  break;
8476
8632
  default:
8477
- if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
8633
+ if (e.provenance === import_types33.Provenance.STALE) cur.stale = e;
8478
8634
  }
8479
8635
  buckets2.set(key, cur);
8480
8636
  });
@@ -8483,12 +8639,12 @@ function bucketEdges(graph) {
8483
8639
  function nodeIsFrontier(graph, nodeId) {
8484
8640
  if (!graph.hasNode(nodeId)) return false;
8485
8641
  const attrs = graph.getNodeAttributes(nodeId);
8486
- return attrs.type === import_types32.NodeType.FrontierNode;
8642
+ return attrs.type === import_types33.NodeType.FrontierNode;
8487
8643
  }
8488
8644
  function nodeIsWebsocketChannel(graph, nodeId) {
8489
8645
  if (!graph.hasNode(nodeId)) return false;
8490
8646
  const attrs = graph.getNodeAttributes(nodeId);
8491
- return attrs.type === import_types32.NodeType.WebSocketChannelNode;
8647
+ return attrs.type === import_types33.NodeType.WebSocketChannelNode;
8492
8648
  }
8493
8649
  function clampConfidence(n) {
8494
8650
  if (!Number.isFinite(n)) return 0;
@@ -8508,14 +8664,14 @@ function gradedConfidence(edge) {
8508
8664
  return clampConfidence(confidenceForEdge(edge));
8509
8665
  }
8510
8666
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
8511
- import_types32.EdgeType.CALLS,
8512
- import_types32.EdgeType.CONNECTS_TO,
8513
- import_types32.EdgeType.PUBLISHES_TO,
8514
- import_types32.EdgeType.CONSUMES_FROM
8667
+ import_types33.EdgeType.CALLS,
8668
+ import_types33.EdgeType.CONNECTS_TO,
8669
+ import_types33.EdgeType.PUBLISHES_TO,
8670
+ import_types33.EdgeType.CONSUMES_FROM
8515
8671
  ]);
8516
8672
  function detectMissingDivergences(graph, bucket) {
8517
8673
  const out = [];
8518
- if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
8674
+ if (bucket.type === import_types33.EdgeType.CONTAINS) return out;
8519
8675
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
8520
8676
  if (!nodeIsFrontier(graph, bucket.target)) {
8521
8677
  out.push({
@@ -8556,7 +8712,7 @@ function declaredHostFor(svc) {
8556
8712
  function hasExtractedConfiguredBy(graph, svcId) {
8557
8713
  for (const edgeId of graph.outboundEdges(svcId)) {
8558
8714
  const e = graph.getEdgeAttributes(edgeId);
8559
- if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
8715
+ if (e.type === import_types33.EdgeType.CONFIGURED_BY && e.provenance === import_types33.Provenance.EXTRACTED) {
8560
8716
  return true;
8561
8717
  }
8562
8718
  }
@@ -8569,10 +8725,10 @@ function detectHostMismatch(graph, svcId, svc) {
8569
8725
  const out = [];
8570
8726
  for (const edgeId of graph.outboundEdges(svcId)) {
8571
8727
  const edge = graph.getEdgeAttributes(edgeId);
8572
- if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8573
- if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8728
+ if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
8729
+ if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
8574
8730
  const target = graph.getNodeAttributes(edge.target);
8575
- if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8731
+ if (target.type !== import_types33.NodeType.DatabaseNode) continue;
8576
8732
  const observedHost = target.host?.trim();
8577
8733
  if (!observedHost) continue;
8578
8734
  if (observedHost === declaredHost) continue;
@@ -8594,10 +8750,10 @@ function detectCompatDivergences(graph, svcId, svc) {
8594
8750
  const deps = svc.dependencies ?? {};
8595
8751
  for (const edgeId of graph.outboundEdges(svcId)) {
8596
8752
  const edge = graph.getEdgeAttributes(edgeId);
8597
- if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8598
- if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8753
+ if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
8754
+ if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
8599
8755
  const target = graph.getNodeAttributes(edge.target);
8600
- if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8756
+ if (target.type !== import_types33.NodeType.DatabaseNode) continue;
8601
8757
  for (const pair of compatPairs()) {
8602
8758
  if (pair.engine !== target.engine) continue;
8603
8759
  const declared = deps[pair.driver];
@@ -8656,7 +8812,7 @@ function suppressHostMismatchHalves(all) {
8656
8812
  for (const d of all) {
8657
8813
  if (d.type !== "host-mismatch") continue;
8658
8814
  observedHalf.add(`${d.source}->${d.target}`);
8659
- declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
8815
+ declaredHalf.add((0, import_types33.databaseId)(d.extractedHost));
8660
8816
  }
8661
8817
  if (observedHalf.size === 0) return all;
8662
8818
  return all.filter((d) => {
@@ -8675,7 +8831,7 @@ function computeDivergences(graph, opts = {}) {
8675
8831
  }
8676
8832
  graph.forEachNode((nodeId, attrs) => {
8677
8833
  const n = attrs;
8678
- if (n.type !== import_types32.NodeType.ServiceNode) return;
8834
+ if (n.type !== import_types33.NodeType.ServiceNode) return;
8679
8835
  const svc = n;
8680
8836
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
8681
8837
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -8709,7 +8865,7 @@ function computeDivergences(graph, opts = {}) {
8709
8865
  if (a.source !== b.source) return a.source.localeCompare(b.source);
8710
8866
  return a.target.localeCompare(b.target);
8711
8867
  });
8712
- return import_types32.DivergenceResultSchema.parse({
8868
+ return import_types33.DivergenceResultSchema.parse({
8713
8869
  divergences: filtered,
8714
8870
  totalAffected: filtered.length,
8715
8871
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -8840,23 +8996,23 @@ function canonicalJson(value) {
8840
8996
 
8841
8997
  // src/projects.ts
8842
8998
  init_cjs_shims();
8843
- var import_node_path45 = __toESM(require("path"), 1);
8999
+ var import_node_path46 = __toESM(require("path"), 1);
8844
9000
  function pathsForProject(project, baseDir) {
8845
9001
  if (project === DEFAULT_PROJECT) {
8846
9002
  return {
8847
- snapshotPath: import_node_path45.default.join(baseDir, "graph.json"),
8848
- errorsPath: import_node_path45.default.join(baseDir, "errors.ndjson"),
8849
- staleEventsPath: import_node_path45.default.join(baseDir, "stale-events.ndjson"),
8850
- embeddingsCachePath: import_node_path45.default.join(baseDir, "embeddings.json"),
8851
- policyViolationsPath: import_node_path45.default.join(baseDir, "policy-violations.ndjson")
9003
+ snapshotPath: import_node_path46.default.join(baseDir, "graph.json"),
9004
+ errorsPath: import_node_path46.default.join(baseDir, "errors.ndjson"),
9005
+ staleEventsPath: import_node_path46.default.join(baseDir, "stale-events.ndjson"),
9006
+ embeddingsCachePath: import_node_path46.default.join(baseDir, "embeddings.json"),
9007
+ policyViolationsPath: import_node_path46.default.join(baseDir, "policy-violations.ndjson")
8852
9008
  };
8853
9009
  }
8854
9010
  return {
8855
- snapshotPath: import_node_path45.default.join(baseDir, `${project}.json`),
8856
- errorsPath: import_node_path45.default.join(baseDir, `errors.${project}.ndjson`),
8857
- staleEventsPath: import_node_path45.default.join(baseDir, `stale-events.${project}.ndjson`),
8858
- embeddingsCachePath: import_node_path45.default.join(baseDir, `embeddings.${project}.json`),
8859
- policyViolationsPath: import_node_path45.default.join(baseDir, `policy-violations.${project}.ndjson`)
9011
+ snapshotPath: import_node_path46.default.join(baseDir, `${project}.json`),
9012
+ errorsPath: import_node_path46.default.join(baseDir, `errors.${project}.ndjson`),
9013
+ staleEventsPath: import_node_path46.default.join(baseDir, `stale-events.${project}.ndjson`),
9014
+ embeddingsCachePath: import_node_path46.default.join(baseDir, `embeddings.${project}.json`),
9015
+ policyViolationsPath: import_node_path46.default.join(baseDir, `policy-violations.${project}.ndjson`)
8860
9016
  };
8861
9017
  }
8862
9018
  var Projects = class {
@@ -8894,23 +9050,23 @@ var Projects = class {
8894
9050
  init_cjs_shims();
8895
9051
  var import_node_fs28 = require("fs");
8896
9052
  var import_node_os3 = __toESM(require("os"), 1);
8897
- var import_node_path46 = __toESM(require("path"), 1);
8898
- var import_types33 = require("@neat.is/types");
9053
+ var import_node_path47 = __toESM(require("path"), 1);
9054
+ var import_types34 = require("@neat.is/types");
8899
9055
  var LOCK_TIMEOUT_MS = 5e3;
8900
9056
  var LOCK_RETRY_MS = 50;
8901
9057
  function neatHome() {
8902
9058
  const override = process.env.NEAT_HOME;
8903
- if (override && override.length > 0) return import_node_path46.default.resolve(override);
8904
- return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
9059
+ if (override && override.length > 0) return import_node_path47.default.resolve(override);
9060
+ return import_node_path47.default.join(import_node_os3.default.homedir(), ".neat");
8905
9061
  }
8906
9062
  function registryPath() {
8907
- return import_node_path46.default.join(neatHome(), "projects.json");
9063
+ return import_node_path47.default.join(neatHome(), "projects.json");
8908
9064
  }
8909
9065
  function registryLockPath() {
8910
- return import_node_path46.default.join(neatHome(), "projects.json.lock");
9066
+ return import_node_path47.default.join(neatHome(), "projects.json.lock");
8911
9067
  }
8912
9068
  function daemonPidPath() {
8913
- return import_node_path46.default.join(neatHome(), "neatd.pid");
9069
+ return import_node_path47.default.join(neatHome(), "neatd.pid");
8914
9070
  }
8915
9071
  function isPidAliveDefault(pid) {
8916
9072
  try {
@@ -8970,7 +9126,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
8970
9126
  }
8971
9127
  }
8972
9128
  async function normalizeProjectPath(input) {
8973
- const resolved = import_node_path46.default.resolve(input);
9129
+ const resolved = import_node_path47.default.resolve(input);
8974
9130
  try {
8975
9131
  return await import_node_fs28.promises.realpath(resolved);
8976
9132
  } catch {
@@ -8978,7 +9134,7 @@ async function normalizeProjectPath(input) {
8978
9134
  }
8979
9135
  }
8980
9136
  async function writeAtomically(target, contents) {
8981
- await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
9137
+ await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(target), { recursive: true });
8982
9138
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
8983
9139
  const fd = await import_node_fs28.promises.open(tmp, "w");
8984
9140
  try {
@@ -8991,7 +9147,7 @@ async function writeAtomically(target, contents) {
8991
9147
  }
8992
9148
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
8993
9149
  const deadline = Date.now() + timeoutMs;
8994
- await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
9150
+ await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(lockPath), { recursive: true });
8995
9151
  let probedHolder = false;
8996
9152
  while (true) {
8997
9153
  try {
@@ -9044,10 +9200,10 @@ async function readRegistry() {
9044
9200
  throw err;
9045
9201
  }
9046
9202
  const parsed = JSON.parse(raw);
9047
- return import_types33.RegistryFileSchema.parse(parsed);
9203
+ return import_types34.RegistryFileSchema.parse(parsed);
9048
9204
  }
9049
9205
  async function writeRegistry(reg) {
9050
- const validated = import_types33.RegistryFileSchema.parse(reg);
9206
+ const validated = import_types34.RegistryFileSchema.parse(reg);
9051
9207
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
9052
9208
  }
9053
9209
  var ProjectNameCollisionError = class extends Error {
@@ -9242,7 +9398,7 @@ init_auth();
9242
9398
  // src/connectors-config.ts
9243
9399
  init_cjs_shims();
9244
9400
  var import_node_os4 = __toESM(require("os"), 1);
9245
- var import_node_path47 = __toESM(require("path"), 1);
9401
+ var import_node_path48 = __toESM(require("path"), 1);
9246
9402
  var import_node_fs29 = require("fs");
9247
9403
  var CONNECTORS_CONFIG_VERSION = 1;
9248
9404
  var EnvRefUnsetError = class extends Error {
@@ -9257,11 +9413,11 @@ var EnvRefUnsetError = class extends Error {
9257
9413
  };
9258
9414
  function neatHome2() {
9259
9415
  const override = process.env.NEAT_HOME;
9260
- if (override && override.length > 0) return import_node_path47.default.resolve(override);
9261
- return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
9416
+ if (override && override.length > 0) return import_node_path48.default.resolve(override);
9417
+ return import_node_path48.default.join(import_node_os4.default.homedir(), ".neat");
9262
9418
  }
9263
9419
  function connectorsConfigPath(home = neatHome2()) {
9264
- return import_node_path47.default.join(home, "connectors.json");
9420
+ return import_node_path48.default.join(home, "connectors.json");
9265
9421
  }
9266
9422
  var MODE_MASK_LOOSER_THAN_0600 = 63;
9267
9423
  async function warnIfModeLooserThan0600(file) {
@@ -9597,11 +9753,11 @@ function registerRoutes(scope, ctx) {
9597
9753
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
9598
9754
  const parsed = [];
9599
9755
  for (const c of candidates) {
9600
- const r = import_types34.DivergenceTypeSchema.safeParse(c);
9756
+ const r = import_types35.DivergenceTypeSchema.safeParse(c);
9601
9757
  if (!r.success) {
9602
9758
  return reply.code(400).send({
9603
9759
  error: `unknown divergence type "${c}"`,
9604
- allowed: import_types34.DivergenceTypeSchema.options
9760
+ allowed: import_types35.DivergenceTypeSchema.options
9605
9761
  });
9606
9762
  }
9607
9763
  parsed.push(r.data);
@@ -9872,7 +10028,7 @@ function registerRoutes(scope, ctx) {
9872
10028
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
9873
10029
  let violations = await log.readAll();
9874
10030
  if (req.query.severity) {
9875
- const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
10031
+ const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
9876
10032
  if (!sev.success) {
9877
10033
  return reply.code(400).send({
9878
10034
  error: "invalid severity",
@@ -9911,7 +10067,7 @@ function registerRoutes(scope, ctx) {
9911
10067
  scope.post("/policies/check", async (req, reply) => {
9912
10068
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
9913
10069
  if (!proj) return;
9914
- const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
10070
+ const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
9915
10071
  if (!parsed.success) {
9916
10072
  return reply.code(400).send({
9917
10073
  error: "invalid /policies/check body",
@@ -10213,21 +10369,21 @@ init_otel_grpc();
10213
10369
  // src/daemon.ts
10214
10370
  init_cjs_shims();
10215
10371
  var import_node_fs31 = require("fs");
10216
- var import_node_path51 = __toESM(require("path"), 1);
10372
+ var import_node_path52 = __toESM(require("path"), 1);
10217
10373
  var import_node_module = require("module");
10218
10374
  init_otel();
10219
10375
 
10220
10376
  // src/connectors/index.ts
10221
10377
  init_cjs_shims();
10222
- var import_types35 = require("@neat.is/types");
10378
+ var import_types36 = require("@neat.is/types");
10223
10379
  var NO_ENV = "unknown";
10224
10380
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
10225
10381
  if (!graph.hasNode(targetNodeId)) return void 0;
10226
10382
  const sites = [];
10227
10383
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
10228
10384
  const edge = graph.getEdgeAttributes(edgeId);
10229
- if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
10230
- const parsed = (0, import_types35.parseFileId)(edge.source);
10385
+ if (edge.provenance !== import_types36.Provenance.EXTRACTED) continue;
10386
+ const parsed = (0, import_types36.parseFileId)(edge.source);
10231
10387
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
10232
10388
  const site = { relPath: edge.evidence.file };
10233
10389
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -10238,7 +10394,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
10238
10394
  function routeCallSiteFor(graph, targetNodeId) {
10239
10395
  if (!graph.hasNode(targetNodeId)) return void 0;
10240
10396
  const attrs = graph.getNodeAttributes(targetNodeId);
10241
- if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
10397
+ if (attrs.type !== import_types36.NodeType.RouteNode || !attrs.path) return void 0;
10242
10398
  const site = { relPath: attrs.path };
10243
10399
  if (attrs.line !== void 0) site.line = attrs.line;
10244
10400
  return site;
@@ -10709,10 +10865,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
10709
10865
  // src/connectors/supabase/map.ts
10710
10866
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
10711
10867
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
10712
- function targetFromRestPath(path52) {
10713
- const rpcMatch = REST_RPC_PATH_RE.exec(path52);
10868
+ function targetFromRestPath(path53) {
10869
+ const rpcMatch = REST_RPC_PATH_RE.exec(path53);
10714
10870
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
10715
- const tableMatch = REST_TABLE_PATH_RE.exec(path52);
10871
+ const tableMatch = REST_TABLE_PATH_RE.exec(path53);
10716
10872
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
10717
10873
  return null;
10718
10874
  }
@@ -10821,23 +10977,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10821
10977
 
10822
10978
  // src/connectors/supabase/resolve.ts
10823
10979
  init_cjs_shims();
10824
- var import_types37 = require("@neat.is/types");
10980
+ var import_types38 = require("@neat.is/types");
10825
10981
  function createSupabaseResolveTarget(graph, config) {
10826
10982
  return (signal, _ctx) => {
10827
10983
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10828
10984
  return null;
10829
10985
  }
10830
- const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10986
+ const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10831
10987
  if (graph.hasNode(subResourceId)) {
10832
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10988
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10833
10989
  }
10834
- const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
10990
+ const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
10835
10991
  if (graph.hasNode(bareResourceId)) {
10836
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10992
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10837
10993
  }
10838
- const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
10994
+ const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
10839
10995
  if (graph.hasNode(projectLevelId)) {
10840
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
10996
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
10841
10997
  }
10842
10998
  return null;
10843
10999
  };
@@ -10930,7 +11086,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10930
11086
 
10931
11087
  // src/connectors/railway/index.ts
10932
11088
  init_cjs_shims();
10933
- var import_types41 = require("@neat.is/types");
11089
+ var import_types42 = require("@neat.is/types");
10934
11090
 
10935
11091
  // src/connectors/railway/client.ts
10936
11092
  init_cjs_shims();
@@ -11056,7 +11212,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
11056
11212
  const out = [];
11057
11213
  graph.forEachNode((_id, attrs) => {
11058
11214
  const node = attrs;
11059
- if (node.type !== import_types41.NodeType.RouteNode) return;
11215
+ if (node.type !== import_types42.NodeType.RouteNode) return;
11060
11216
  const route = attrs;
11061
11217
  if (route.service !== serviceName) return;
11062
11218
  out.push({
@@ -11160,12 +11316,12 @@ function createRailwayResolveTarget(config) {
11160
11316
  const serviceName = config.serviceNameById[config.serviceId];
11161
11317
  if (!serviceName) return null;
11162
11318
  if (signal.targetKind === ROUTE_TARGET_KIND) {
11163
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
11319
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
11164
11320
  }
11165
11321
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
11166
11322
  const peerName = config.serviceNameById[signal.targetName];
11167
11323
  if (!peerName) return null;
11168
- return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
11324
+ return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
11169
11325
  }
11170
11326
  return null;
11171
11327
  };
@@ -11289,9 +11445,9 @@ function parseFirebaseTargetName(targetName) {
11289
11445
  const secondSep = rest.indexOf(FIELD_SEP);
11290
11446
  if (secondSep === -1) return null;
11291
11447
  const method = rest.slice(0, secondSep);
11292
- const path52 = rest.slice(secondSep + 1);
11293
- if (!resourceName || !method || !path52) return null;
11294
- return { resourceName, method, path: path52 };
11448
+ const path53 = rest.slice(secondSep + 1);
11449
+ if (!resourceName || !method || !path53) return null;
11450
+ return { resourceName, method, path: path53 };
11295
11451
  }
11296
11452
  function resourceNameFor(type, labels) {
11297
11453
  if (!labels) return null;
@@ -11329,14 +11485,14 @@ function mapLogEntryToSignal(entry) {
11329
11485
  if (!req) return null;
11330
11486
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
11331
11487
  const method = req.requestMethod.toUpperCase();
11332
- const path52 = pathFromRequestUrl(req.requestUrl);
11333
- if (path52 === null) return null;
11488
+ const path53 = pathFromRequestUrl(req.requestUrl);
11489
+ if (path53 === null) return null;
11334
11490
  const timestamp = entry.timestamp;
11335
11491
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
11336
11492
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
11337
11493
  return {
11338
11494
  targetKind: resourceType,
11339
- targetName: packFirebaseTargetName({ resourceName, method, path: path52 }),
11495
+ targetName: packFirebaseTargetName({ resourceName, method, path: path53 }),
11340
11496
  callCount: 1,
11341
11497
  errorCount: isError ? 1 : 0,
11342
11498
  lastObservedIso: timestamp
@@ -11353,7 +11509,7 @@ function mapLogEntriesToSignals(entries) {
11353
11509
 
11354
11510
  // src/connectors/firebase/resolve.ts
11355
11511
  init_cjs_shims();
11356
- var import_types42 = require("@neat.is/types");
11512
+ var import_types43 = require("@neat.is/types");
11357
11513
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
11358
11514
  switch (resourceType) {
11359
11515
  case "cloud_function":
@@ -11368,7 +11524,7 @@ function routeEntriesFor(graph, serviceName) {
11368
11524
  const entries = [];
11369
11525
  graph.forEachNode((_id, attrs) => {
11370
11526
  const node = attrs;
11371
- if (node.type !== import_types42.NodeType.RouteNode) return;
11527
+ if (node.type !== import_types43.NodeType.RouteNode) return;
11372
11528
  const route = attrs;
11373
11529
  if (route.service !== serviceName) return;
11374
11530
  entries.push({
@@ -11400,7 +11556,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
11400
11556
  return {
11401
11557
  targetNodeId: match.routeNodeId,
11402
11558
  serviceName,
11403
- edgeType: import_types42.EdgeType.CALLS
11559
+ edgeType: import_types43.EdgeType.CALLS
11404
11560
  };
11405
11561
  };
11406
11562
  }
@@ -11427,7 +11583,7 @@ init_cjs_shims();
11427
11583
 
11428
11584
  // src/connectors/cloudflare/connector.ts
11429
11585
  init_cjs_shims();
11430
- var import_types44 = require("@neat.is/types");
11586
+ var import_types45 = require("@neat.is/types");
11431
11587
 
11432
11588
  // src/connectors/cloudflare/client.ts
11433
11589
  init_cjs_shims();
@@ -11543,7 +11699,7 @@ function mapEventToSignal(event) {
11543
11699
  if (Number.isNaN(observedAt.getTime())) return null;
11544
11700
  const statusCode = metadata?.statusCode;
11545
11701
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
11546
- const path52 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11702
+ const path53 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11547
11703
  return {
11548
11704
  targetKind: CLOUDFLARE_TARGET_KIND,
11549
11705
  targetName: scriptName,
@@ -11551,7 +11707,7 @@ function mapEventToSignal(event) {
11551
11707
  errorCount: isError ? 1 : 0,
11552
11708
  lastObservedIso: observedAt.toISOString(),
11553
11709
  method,
11554
- ...path52 ? { path: path52 } : {},
11710
+ ...path53 ? { path: path53 } : {},
11555
11711
  ...typeof statusCode === "number" ? { statusCode } : {},
11556
11712
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
11557
11713
  };
@@ -11591,19 +11747,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
11591
11747
  graph.forEachNode((id, attrs) => {
11592
11748
  if (found) return;
11593
11749
  const a = attrs;
11594
- if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11750
+ if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11595
11751
  found = id;
11596
11752
  }
11597
11753
  });
11598
11754
  return found;
11599
11755
  }
11600
- function findMatchingRouteNode(graph, serviceName, method, path52) {
11601
- const normalizedPath = normalizePathTemplate(path52);
11756
+ function findMatchingRouteNode(graph, serviceName, method, path53) {
11757
+ const normalizedPath = normalizePathTemplate(path53);
11602
11758
  let found = null;
11603
11759
  graph.forEachNode((id, attrs) => {
11604
11760
  if (found) return;
11605
11761
  const a = attrs;
11606
- if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
11762
+ if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
11607
11763
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11608
11764
  const routeMethod = (a.method ?? "").toUpperCase();
11609
11765
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11615,18 +11771,18 @@ function createCloudflareResolveTarget(config, graph) {
11615
11771
  return (signal) => {
11616
11772
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
11617
11773
  const scriptName = signal.targetName;
11618
- const { method, path: path52 } = signal;
11774
+ const { method, path: path53 } = signal;
11619
11775
  const resolveRouteGrain = (serviceName, wholeFileId) => {
11620
- if (!method || !path52) return wholeFileId;
11621
- return findMatchingRouteNode(graph, serviceName, method, path52) ?? wholeFileId;
11776
+ if (!method || !path53) return wholeFileId;
11777
+ return findMatchingRouteNode(graph, serviceName, method, path53) ?? wholeFileId;
11622
11778
  };
11623
11779
  const mapping = config.workers?.[scriptName];
11624
11780
  if (mapping) {
11625
- const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
11781
+ const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
11626
11782
  return {
11627
11783
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11628
11784
  serviceName: mapping.service,
11629
- edgeType: import_types44.EdgeType.CALLS
11785
+ edgeType: import_types45.EdgeType.CALLS
11630
11786
  };
11631
11787
  }
11632
11788
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11635,13 +11791,13 @@ function createCloudflareResolveTarget(config, graph) {
11635
11791
  return {
11636
11792
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11637
11793
  serviceName: fileNode.service,
11638
- edgeType: import_types44.EdgeType.CALLS
11794
+ edgeType: import_types45.EdgeType.CALLS
11639
11795
  };
11640
11796
  }
11641
11797
  return {
11642
- targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
11798
+ targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
11643
11799
  serviceName: scriptName,
11644
- edgeType: import_types44.EdgeType.CALLS,
11800
+ edgeType: import_types45.EdgeType.CALLS,
11645
11801
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11646
11802
  };
11647
11803
  };
@@ -12085,7 +12241,7 @@ init_auth();
12085
12241
  // src/unrouted.ts
12086
12242
  init_cjs_shims();
12087
12243
  var import_node_fs30 = require("fs");
12088
- var import_node_path50 = __toESM(require("path"), 1);
12244
+ var import_node_path51 = __toESM(require("path"), 1);
12089
12245
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
12090
12246
  return {
12091
12247
  timestamp: now.toISOString(),
@@ -12095,34 +12251,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
12095
12251
  };
12096
12252
  }
12097
12253
  async function appendUnroutedSpan(neatHome3, record) {
12098
- const target = import_node_path50.default.join(neatHome3, "errors.ndjson");
12254
+ const target = import_node_path51.default.join(neatHome3, "errors.ndjson");
12099
12255
  await import_node_fs30.promises.mkdir(neatHome3, { recursive: true });
12100
12256
  await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
12101
12257
  }
12102
12258
  function unroutedErrorsPath(neatHome3) {
12103
- return import_node_path50.default.join(neatHome3, "errors.ndjson");
12259
+ return import_node_path51.default.join(neatHome3, "errors.ndjson");
12104
12260
  }
12105
12261
 
12106
12262
  // src/daemon.ts
12107
- var import_types47 = require("@neat.is/types");
12263
+ var import_types48 = require("@neat.is/types");
12108
12264
  function daemonJsonPath(scanPath) {
12109
- return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
12265
+ return import_node_path52.default.join(scanPath, "neat-out", "daemon.json");
12110
12266
  }
12111
12267
  function daemonsDiscoveryDir(home) {
12112
12268
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
12113
- return import_node_path51.default.join(base, "daemons");
12269
+ return import_node_path52.default.join(base, "daemons");
12114
12270
  }
12115
12271
  function daemonDiscoveryPath(project, home) {
12116
- return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12272
+ return import_node_path52.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12117
12273
  }
12118
12274
  function sanitizeDiscoveryName(project) {
12119
12275
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
12120
12276
  }
12121
12277
  function neatHomeFromEnv() {
12122
12278
  const env = process.env.NEAT_HOME;
12123
- if (env && env.length > 0) return import_node_path51.default.resolve(env);
12279
+ if (env && env.length > 0) return import_node_path52.default.resolve(env);
12124
12280
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12125
- return import_node_path51.default.join(home, ".neat");
12281
+ return import_node_path52.default.join(home, ".neat");
12126
12282
  }
12127
12283
  function resolveNeatVersion() {
12128
12284
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -12177,11 +12333,11 @@ function teardownSlot(slot) {
12177
12333
  }
12178
12334
  }
12179
12335
  function neatHomeFor(opts) {
12180
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
12336
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path52.default.resolve(opts.neatHome);
12181
12337
  const env = process.env.NEAT_HOME;
12182
- if (env && env.length > 0) return import_node_path51.default.resolve(env);
12338
+ if (env && env.length > 0) return import_node_path52.default.resolve(env);
12183
12339
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12184
- return import_node_path51.default.join(home, ".neat");
12340
+ return import_node_path52.default.join(home, ".neat");
12185
12341
  }
12186
12342
  function routeSpanToProject(serviceName, projects) {
12187
12343
  if (!serviceName) return DEFAULT_PROJECT;
@@ -12225,11 +12381,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
12225
12381
  if (!serviceName) return true;
12226
12382
  if (serviceNameMatchesProject(serviceName, project)) return true;
12227
12383
  return graph.someNode(
12228
- (_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
12384
+ (_id, attrs) => attrs.type === import_types48.NodeType.ServiceNode && attrs.name === serviceName
12229
12385
  );
12230
12386
  }
12231
12387
  async function bootstrapProject(entry, connectors = [], neatHome3) {
12232
- const paths = pathsForProject(entry.name, import_node_path51.default.join(entry.path, "neat-out"));
12388
+ const paths = pathsForProject(entry.name, import_node_path52.default.join(entry.path, "neat-out"));
12233
12389
  try {
12234
12390
  const stat = await import_node_fs31.promises.stat(entry.path);
12235
12391
  if (!stat.isDirectory()) {
@@ -12360,7 +12516,7 @@ async function startDaemon(opts = {}) {
12360
12516
  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;
12361
12517
  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;
12362
12518
  const singleProject = projectArg;
12363
- const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
12519
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path52.default.resolve(projectPathArg) : null;
12364
12520
  if (singleProject && !singleProjectPath) {
12365
12521
  throw new Error(
12366
12522
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -12375,7 +12531,7 @@ async function startDaemon(opts = {}) {
12375
12531
  );
12376
12532
  }
12377
12533
  }
12378
- const pidPath = import_node_path51.default.join(home, "neatd.pid");
12534
+ const pidPath = import_node_path52.default.join(home, "neatd.pid");
12379
12535
  await writeAtomically(pidPath, `${process.pid}
12380
12536
  `);
12381
12537
  const slots = /* @__PURE__ */ new Map();
@@ -12782,8 +12938,8 @@ async function startDaemon(opts = {}) {
12782
12938
  let registryWatcher = null;
12783
12939
  let reloadTimer = null;
12784
12940
  if (!singleProject) try {
12785
- const regDir = import_node_path51.default.dirname(regPath);
12786
- const regBase = import_node_path51.default.basename(regPath);
12941
+ const regDir = import_node_path52.default.dirname(regPath);
12942
+ const regBase = import_node_path52.default.basename(regPath);
12787
12943
  registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
12788
12944
  if (filename !== null && filename !== regBase) return;
12789
12945
  if (reloadTimer) clearTimeout(reloadTimer);