@neat.is/core 0.4.28-dev.20260708 → 0.4.28-dev.20260710

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/neatd.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req2, reply, done) => {
64
- const path49 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path49) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path49)) {
64
+ const path50 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path50) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path50)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path42.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
- return import_node_path42.default.resolve(here, "..", "proto");
197
+ const here = import_node_path43.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
+ return import_node_path43.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url, import_node_path42, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url, import_node_path43, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url = require("url");
272
- import_node_path42 = __toESM(require("path"), 1);
272
+ import_node_path43 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -362,8 +362,8 @@ function websocketChannelPathOf(attrs) {
362
362
  const v = attrs[key];
363
363
  if (typeof v === "string" && v.length > 0) {
364
364
  const q = v.indexOf("?");
365
- const path49 = q === -1 ? v : v.slice(0, q);
366
- if (path49.length > 0) return path49;
365
+ const path50 = q === -1 ? v : v.slice(0, q);
366
+ if (path50.length > 0) return path50;
367
367
  }
368
368
  }
369
369
  return void 0;
@@ -417,10 +417,10 @@ function parseOtlpRequest(body) {
417
417
  return out;
418
418
  }
419
419
  function loadProtoRoot() {
420
- const here = import_node_path43.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
421
- const protoRoot = import_node_path43.default.resolve(here, "..", "proto");
420
+ const here = import_node_path44.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
421
+ const protoRoot = import_node_path44.default.resolve(here, "..", "proto");
422
422
  const root = new import_protobufjs.default.Root();
423
- root.resolvePath = (_origin, target) => import_node_path43.default.resolve(protoRoot, target);
423
+ root.resolvePath = (_origin, target) => import_node_path44.default.resolve(protoRoot, target);
424
424
  root.loadSync(
425
425
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
426
426
  { keepCase: true }
@@ -648,12 +648,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
648
648
  }
649
649
  }
650
650
  }
651
- var import_node_path43, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
651
+ var import_node_path44, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
652
652
  var init_otel = __esm({
653
653
  "src/otel.ts"() {
654
654
  "use strict";
655
655
  init_cjs_shims();
656
- import_node_path43 = __toESM(require("path"), 1);
656
+ import_node_path44 = __toESM(require("path"), 1);
657
657
  import_node_url2 = require("url");
658
658
  import_fastify2 = __toESM(require("fastify"), 1);
659
659
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -676,14 +676,14 @@ __export(neatd_exports, {
676
676
  });
677
677
  module.exports = __toCommonJS(neatd_exports);
678
678
  init_cjs_shims();
679
- var import_node_fs29 = require("fs");
680
- var import_node_path48 = __toESM(require("path"), 1);
679
+ var import_node_fs30 = require("fs");
680
+ var import_node_path49 = __toESM(require("path"), 1);
681
681
  var import_node_module2 = require("module");
682
682
 
683
683
  // src/daemon.ts
684
684
  init_cjs_shims();
685
- var import_node_fs27 = require("fs");
686
- var import_node_path46 = __toESM(require("path"), 1);
685
+ var import_node_fs28 = require("fs");
686
+ var import_node_path47 = __toESM(require("path"), 1);
687
687
  var import_node_module = require("module");
688
688
 
689
689
  // src/graph.ts
@@ -1213,19 +1213,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1213
1213
  function longestIncomingWalk(graph, start, maxDepth) {
1214
1214
  let best = { path: [start], edges: [] };
1215
1215
  const visited = /* @__PURE__ */ new Set([start]);
1216
- function step(node, path49, edges) {
1217
- if (path49.length > best.path.length) {
1218
- best = { path: [...path49], edges: [...edges] };
1216
+ function step(node, path50, edges) {
1217
+ if (path50.length > best.path.length) {
1218
+ best = { path: [...path50], edges: [...edges] };
1219
1219
  }
1220
- if (path49.length - 1 >= maxDepth) return;
1220
+ if (path50.length - 1 >= maxDepth) return;
1221
1221
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1222
1222
  for (const [srcId, edge] of incoming) {
1223
1223
  if (visited.has(srcId)) continue;
1224
1224
  visited.add(srcId);
1225
- path49.push(srcId);
1225
+ path50.push(srcId);
1226
1226
  edges.push(edge);
1227
- step(srcId, path49, edges);
1228
- path49.pop();
1227
+ step(srcId, path50, edges);
1228
+ path50.pop();
1229
1229
  edges.pop();
1230
1230
  visited.delete(srcId);
1231
1231
  }
@@ -1419,26 +1419,26 @@ function dominantFailingCall(graph, serviceId5, visited) {
1419
1419
  return best;
1420
1420
  }
1421
1421
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1422
- const path49 = [originServiceId];
1422
+ const path50 = [originServiceId];
1423
1423
  const edges = [];
1424
1424
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1425
1425
  let current = originServiceId;
1426
1426
  for (let depth = 0; depth < maxDepth; depth++) {
1427
1427
  const hop = dominantFailingCall(graph, current, visited);
1428
1428
  if (!hop) break;
1429
- path49.push(hop.nextService);
1429
+ path50.push(hop.nextService);
1430
1430
  edges.push(hop.edge);
1431
1431
  visited.add(hop.nextService);
1432
1432
  current = hop.nextService;
1433
1433
  }
1434
1434
  if (edges.length === 0) return null;
1435
- return { path: path49, edges, culprit: current };
1435
+ return { path: path50, edges, culprit: current };
1436
1436
  }
1437
1437
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1438
1438
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1439
1439
  if (!chain) return null;
1440
1440
  const culprit = chain.culprit;
1441
- const path49 = [...chain.path];
1441
+ const path50 = [...chain.path];
1442
1442
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1443
1443
  const baseConfidence = confidenceFromMix(chain.edges);
1444
1444
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1446,14 +1446,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1446
1446
  if (loc) {
1447
1447
  let rootCauseNode = culprit;
1448
1448
  if (loc.fileNode) {
1449
- path49.push(loc.fileNode);
1449
+ path50.push(loc.fileNode);
1450
1450
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1451
1451
  rootCauseNode = loc.fileNode;
1452
1452
  }
1453
1453
  return import_types.RootCauseResultSchema.parse({
1454
1454
  rootCauseNode,
1455
1455
  rootCauseReason: loc.rootCauseReason,
1456
- traversalPath: path49,
1456
+ traversalPath: path50,
1457
1457
  edgeProvenances,
1458
1458
  confidence,
1459
1459
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1465,7 +1465,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1465
1465
  return import_types.RootCauseResultSchema.parse({
1466
1466
  rootCauseNode: culprit,
1467
1467
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1468
- traversalPath: path49,
1468
+ traversalPath: path50,
1469
1469
  edgeProvenances,
1470
1470
  confidence,
1471
1471
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2542,6 +2542,19 @@ function ensureServiceNode(graph, serviceName, env) {
2542
2542
  graph.addNode(id, node);
2543
2543
  return id;
2544
2544
  }
2545
+ function ensureInfraNode(graph, kind, name, provider) {
2546
+ const id = (0, import_types3.infraId)(kind, name);
2547
+ if (graph.hasNode(id)) return id;
2548
+ const node = {
2549
+ id,
2550
+ type: import_types3.NodeType.InfraNode,
2551
+ name,
2552
+ provider,
2553
+ kind
2554
+ };
2555
+ graph.addNode(id, node);
2556
+ return id;
2557
+ }
2545
2558
  function ensureDatabaseNode(graph, host, engine) {
2546
2559
  const id = (0, import_types3.databaseId)(host);
2547
2560
  if (graph.hasNode(id)) return id;
@@ -5297,10 +5310,10 @@ function fastifyRouteMethods(objNode) {
5297
5310
  }
5298
5311
  return [];
5299
5312
  }
5300
- function serverRoutesFromSource(source, parser, hasExpress, hasFastify) {
5313
+ function serverRoutesFromSource(source, parser, hasExpress, hasFastify, hasHono = false) {
5301
5314
  const tree = parseSource2(parser, source);
5302
5315
  const out = [];
5303
- const framework = hasExpress ? "express" : "fastify";
5316
+ const framework = hasExpress ? "express" : hasFastify ? "fastify" : hasHono ? "hono" : "unknown";
5304
5317
  walk(tree.rootNode, (node) => {
5305
5318
  if (node.type !== "call_expression") return;
5306
5319
  const fn = node.childForFieldName("function");
@@ -5450,8 +5463,9 @@ async function addRoutes(graph, services) {
5450
5463
  };
5451
5464
  const hasExpress = deps["express"] !== void 0;
5452
5465
  const hasFastify = deps["fastify"] !== void 0;
5466
+ const hasHono = deps["hono"] !== void 0;
5453
5467
  const hasNext = deps["next"] !== void 0;
5454
- if (!hasExpress && !hasFastify && !hasNext) continue;
5468
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext) continue;
5455
5469
  const files = await loadSourceFiles(service.dir);
5456
5470
  for (const file of files) {
5457
5471
  if (isTestPath(file.path)) continue;
@@ -5461,8 +5475,8 @@ async function addRoutes(graph, services) {
5461
5475
  try {
5462
5476
  if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
5463
5477
  routes = nextRoutesFromFile(file.content, relFile, jsParser);
5464
- } else if (hasExpress || hasFastify) {
5465
- routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify);
5478
+ } else if (hasExpress || hasFastify || hasHono) {
5479
+ routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify, hasHono);
5466
5480
  } else {
5467
5481
  routes = [];
5468
5482
  }
@@ -6786,30 +6800,264 @@ async function addK8sResources(graph, scanPath) {
6786
6800
  return { nodesAdded, edgesAdded: 0 };
6787
6801
  }
6788
6802
 
6803
+ // src/extract/infra/cloudflare.ts
6804
+ init_cjs_shims();
6805
+ var import_node_fs19 = require("fs");
6806
+ var import_node_path35 = __toESM(require("path"), 1);
6807
+ var import_smol_toml2 = require("smol-toml");
6808
+ var import_types25 = require("@neat.is/types");
6809
+ var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
6810
+ async function readWranglerConfig(dir) {
6811
+ for (const filename of WRANGLER_FILENAMES) {
6812
+ const abs = import_node_path35.default.join(dir, filename);
6813
+ if (!await exists(abs)) continue;
6814
+ const raw = await import_node_fs19.promises.readFile(abs, "utf8");
6815
+ const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
6816
+ return { config, relFile: filename, raw };
6817
+ }
6818
+ return null;
6819
+ }
6820
+ function lineContaining(raw, needle) {
6821
+ if (!needle) return void 0;
6822
+ const idx = raw.indexOf(needle);
6823
+ if (idx === -1) return void 0;
6824
+ let line = 1;
6825
+ for (let i = 0; i < idx; i++) if (raw[i] === "\n") line++;
6826
+ return line;
6827
+ }
6828
+ function normalizeRoutes(config) {
6829
+ const out = [];
6830
+ const pushOne = (r) => {
6831
+ if (typeof r === "string") out.push(r);
6832
+ else if (r && typeof r === "object" && typeof r.pattern === "string") {
6833
+ out.push(r.pattern);
6834
+ }
6835
+ };
6836
+ if (Array.isArray(config.routes)) config.routes.forEach(pushOne);
6837
+ else if (config.route) pushOne(config.route);
6838
+ return out;
6839
+ }
6840
+ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, line) {
6841
+ let nodesAdded = 0;
6842
+ let edgesAdded = 0;
6843
+ const node = makeInfraNode(kind, name, "cloudflare");
6844
+ if (!graph.hasNode(node.id)) {
6845
+ graph.addNode(node.id, node);
6846
+ nodesAdded++;
6847
+ }
6848
+ if (node.id === anchorId) return { nodesAdded, edgesAdded };
6849
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, node.id, edgeType);
6850
+ if (!graph.hasEdge(edgeId)) {
6851
+ const edge = {
6852
+ id: edgeId,
6853
+ source: anchorId,
6854
+ target: node.id,
6855
+ type: edgeType,
6856
+ provenance: import_types25.Provenance.EXTRACTED,
6857
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
6858
+ evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
6859
+ };
6860
+ graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
6861
+ edgesAdded++;
6862
+ }
6863
+ return { nodesAdded, edgesAdded };
6864
+ }
6865
+ async function addCloudflareWorkers(graph, services, scanPath) {
6866
+ let nodesAdded = 0;
6867
+ let edgesAdded = 0;
6868
+ const discovered = [];
6869
+ const workerIndex = /* @__PURE__ */ new Map();
6870
+ for (const service of services) {
6871
+ let read;
6872
+ try {
6873
+ read = await readWranglerConfig(service.dir);
6874
+ } catch (err) {
6875
+ recordExtractionError("infra cloudflare", import_node_path35.default.relative(scanPath, service.dir), err);
6876
+ continue;
6877
+ }
6878
+ if (!read || !read.config.name) continue;
6879
+ const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, import_node_path35.default.join(service.dir, read.relFile)));
6880
+ discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
6881
+ }
6882
+ for (const worker of discovered) {
6883
+ const { service, config } = worker;
6884
+ const serviceNode = graph.getNodeAttributes(service.node.id);
6885
+ if (serviceNode.platform !== "cloudflare") {
6886
+ const updated = { ...serviceNode, platform: "cloudflare" };
6887
+ graph.replaceNodeAttributes(service.node.id, updated);
6888
+ }
6889
+ let anchorId = service.node.id;
6890
+ if (config.main) {
6891
+ const entryRelPath = toPosix2(import_node_path35.default.normalize(config.main));
6892
+ const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
6893
+ graph,
6894
+ service.pkg.name,
6895
+ service.node.id,
6896
+ entryRelPath
6897
+ );
6898
+ nodesAdded += fn;
6899
+ edgesAdded += fe;
6900
+ const fileNode = graph.getNodeAttributes(fileNodeId);
6901
+ if (fileNode.platform !== "cloudflare" || fileNode.platformName !== config.name) {
6902
+ const updated = { ...fileNode, platform: "cloudflare", platformName: config.name };
6903
+ graph.replaceNodeAttributes(fileNodeId, updated);
6904
+ }
6905
+ anchorId = fileNodeId;
6906
+ }
6907
+ workerIndex.set(config.name, { anchorId });
6908
+ }
6909
+ for (const worker of discovered) {
6910
+ const { config, evidenceFile, raw } = worker;
6911
+ const anchorId = workerIndex.get(config.name).anchorId;
6912
+ const runtimeNode = makeInfraNode("workerd", "cloudflare", "cloudflare");
6913
+ if (!graph.hasNode(runtimeNode.id)) {
6914
+ graph.addNode(runtimeNode.id, runtimeNode);
6915
+ nodesAdded++;
6916
+ }
6917
+ if (runtimeNode.id !== anchorId) {
6918
+ const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types25.EdgeType.RUNS_ON);
6919
+ if (!graph.hasEdge(runsOnId)) {
6920
+ const edge = {
6921
+ id: runsOnId,
6922
+ source: anchorId,
6923
+ target: runtimeNode.id,
6924
+ type: import_types25.EdgeType.RUNS_ON,
6925
+ provenance: import_types25.Provenance.EXTRACTED,
6926
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
6927
+ evidence: {
6928
+ file: evidenceFile,
6929
+ ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
6930
+ }
6931
+ };
6932
+ graph.addEdgeWithKey(runsOnId, edge.source, edge.target, edge);
6933
+ edgesAdded++;
6934
+ }
6935
+ }
6936
+ for (const route of normalizeRoutes(config)) {
6937
+ const result = addResourceEdge(
6938
+ graph,
6939
+ anchorId,
6940
+ import_types25.EdgeType.CONNECTS_TO,
6941
+ "cloudflare-route",
6942
+ route,
6943
+ evidenceFile,
6944
+ lineContaining(raw, route)
6945
+ );
6946
+ nodesAdded += result.nodesAdded;
6947
+ edgesAdded += result.edgesAdded;
6948
+ }
6949
+ const bindingGroups = [
6950
+ { kind: "cloudflare-kv", entries: config.kv_namespaces ?? [], nameOf: (b) => b.binding },
6951
+ { kind: "cloudflare-d1", entries: config.d1_databases ?? [], nameOf: (b) => b.binding },
6952
+ { kind: "cloudflare-r2", entries: config.r2_buckets ?? [], nameOf: (b) => b.binding },
6953
+ { kind: "cloudflare-durable-object", entries: config.durable_objects?.bindings ?? [], nameOf: (b) => b.name },
6954
+ { kind: "cloudflare-queue", entries: config.queues?.producers ?? [], nameOf: (b) => b.queue },
6955
+ { kind: "cloudflare-queue", entries: config.queues?.consumers ?? [], nameOf: (b) => b.queue }
6956
+ ];
6957
+ for (const group of bindingGroups) {
6958
+ for (const entry2 of group.entries) {
6959
+ const name = group.nameOf(entry2);
6960
+ if (!name) continue;
6961
+ const result = addResourceEdge(
6962
+ graph,
6963
+ anchorId,
6964
+ import_types25.EdgeType.DEPENDS_ON,
6965
+ group.kind,
6966
+ name,
6967
+ evidenceFile,
6968
+ lineContaining(raw, name)
6969
+ );
6970
+ nodesAdded += result.nodesAdded;
6971
+ edgesAdded += result.edgesAdded;
6972
+ }
6973
+ }
6974
+ for (const cron of config.triggers?.crons ?? []) {
6975
+ const result = addResourceEdge(
6976
+ graph,
6977
+ anchorId,
6978
+ import_types25.EdgeType.DEPENDS_ON,
6979
+ "cloudflare-cron",
6980
+ cron,
6981
+ evidenceFile,
6982
+ lineContaining(raw, cron)
6983
+ );
6984
+ nodesAdded += result.nodesAdded;
6985
+ edgesAdded += result.edgesAdded;
6986
+ }
6987
+ for (const varName of Object.keys(config.vars ?? {})) {
6988
+ const result = addResourceEdge(
6989
+ graph,
6990
+ anchorId,
6991
+ import_types25.EdgeType.DEPENDS_ON,
6992
+ "cloudflare-env-var",
6993
+ varName,
6994
+ evidenceFile,
6995
+ lineContaining(raw, varName)
6996
+ );
6997
+ nodesAdded += result.nodesAdded;
6998
+ edgesAdded += result.edgesAdded;
6999
+ }
7000
+ for (const svc of config.services ?? []) {
7001
+ if (!svc.service) continue;
7002
+ const target = workerIndex.get(svc.service);
7003
+ if (target && target.anchorId !== anchorId) {
7004
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types25.EdgeType.CALLS);
7005
+ if (!graph.hasEdge(edgeId)) {
7006
+ const edge = {
7007
+ id: edgeId,
7008
+ source: anchorId,
7009
+ target: target.anchorId,
7010
+ type: import_types25.EdgeType.CALLS,
7011
+ provenance: import_types25.Provenance.EXTRACTED,
7012
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7013
+ evidence: { file: evidenceFile, line: lineContaining(raw, svc.service) }
7014
+ };
7015
+ graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
7016
+ edgesAdded++;
7017
+ }
7018
+ continue;
7019
+ }
7020
+ const result = addResourceEdge(
7021
+ graph,
7022
+ anchorId,
7023
+ import_types25.EdgeType.DEPENDS_ON,
7024
+ "cloudflare-service-binding",
7025
+ svc.service,
7026
+ evidenceFile,
7027
+ lineContaining(raw, svc.service)
7028
+ );
7029
+ nodesAdded += result.nodesAdded;
7030
+ edgesAdded += result.edgesAdded;
7031
+ }
7032
+ }
7033
+ return { nodesAdded, edgesAdded };
7034
+ }
7035
+
6789
7036
  // src/extract/infra/index.ts
6790
7037
  async function addInfra(graph, scanPath, services) {
6791
7038
  const compose = await addComposeInfra(graph, scanPath, services);
6792
7039
  const dockerfile = await addDockerfileRuntimes(graph, services, scanPath);
6793
7040
  const terraform = await addTerraformResources(graph, scanPath);
6794
7041
  const k8s = await addK8sResources(graph, scanPath);
7042
+ const cloudflare = await addCloudflareWorkers(graph, services, scanPath);
6795
7043
  return {
6796
- nodesAdded: compose.nodesAdded + dockerfile.nodesAdded + terraform.nodesAdded + k8s.nodesAdded,
6797
- edgesAdded: compose.edgesAdded + dockerfile.edgesAdded + terraform.edgesAdded + k8s.edgesAdded
7044
+ nodesAdded: compose.nodesAdded + dockerfile.nodesAdded + terraform.nodesAdded + k8s.nodesAdded + cloudflare.nodesAdded,
7045
+ edgesAdded: compose.edgesAdded + dockerfile.edgesAdded + terraform.edgesAdded + k8s.edgesAdded + cloudflare.edgesAdded
6798
7046
  };
6799
7047
  }
6800
7048
 
6801
7049
  // src/extract/index.ts
6802
- var import_node_path36 = __toESM(require("path"), 1);
7050
+ var import_node_path37 = __toESM(require("path"), 1);
6803
7051
 
6804
7052
  // src/extract/retire.ts
6805
7053
  init_cjs_shims();
6806
- var import_node_fs19 = require("fs");
6807
- var import_node_path35 = __toESM(require("path"), 1);
6808
- var import_types25 = require("@neat.is/types");
7054
+ var import_node_fs20 = require("fs");
7055
+ var import_node_path36 = __toESM(require("path"), 1);
7056
+ var import_types26 = require("@neat.is/types");
6809
7057
  function dropOrphanedFileNodes(graph) {
6810
7058
  const orphans = [];
6811
7059
  graph.forEachNode((id, attrs) => {
6812
- if (attrs.type !== import_types25.NodeType.FileNode) return;
7060
+ if (attrs.type !== import_types26.NodeType.FileNode) return;
6813
7061
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
6814
7062
  orphans.push(id);
6815
7063
  }
@@ -6822,14 +7070,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
6822
7070
  const bases = [scanPath, ...serviceDirs];
6823
7071
  graph.forEachEdge((id, attrs) => {
6824
7072
  const edge = attrs;
6825
- if (edge.provenance !== import_types25.Provenance.EXTRACTED) return;
7073
+ if (edge.provenance !== import_types26.Provenance.EXTRACTED) return;
6826
7074
  const evidenceFile = edge.evidence?.file;
6827
7075
  if (!evidenceFile) return;
6828
- if (import_node_path35.default.isAbsolute(evidenceFile)) {
6829
- if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
7076
+ if (import_node_path36.default.isAbsolute(evidenceFile)) {
7077
+ if (!(0, import_node_fs20.existsSync)(evidenceFile)) toDrop.push(id);
6830
7078
  return;
6831
7079
  }
6832
- const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path35.default.join(base, evidenceFile)));
7080
+ const found = bases.some((base) => (0, import_node_fs20.existsSync)(import_node_path36.default.join(base, evidenceFile)));
6833
7081
  if (!found) toDrop.push(id);
6834
7082
  });
6835
7083
  for (const id of toDrop) graph.dropEdge(id);
@@ -6871,7 +7119,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
6871
7119
  }
6872
7120
  const droppedEntries = drainDroppedExtracted();
6873
7121
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
6874
- const rejectedPath = import_node_path36.default.join(import_node_path36.default.dirname(opts.errorsPath), "rejected.ndjson");
7122
+ const rejectedPath = import_node_path37.default.join(import_node_path37.default.dirname(opts.errorsPath), "rejected.ndjson");
6875
7123
  try {
6876
7124
  await writeRejectedExtracted(droppedEntries, rejectedPath);
6877
7125
  } catch (err) {
@@ -6905,9 +7153,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
6905
7153
 
6906
7154
  // src/persist.ts
6907
7155
  init_cjs_shims();
6908
- var import_node_fs20 = require("fs");
6909
- var import_node_path37 = __toESM(require("path"), 1);
6910
- var import_types26 = require("@neat.is/types");
7156
+ var import_node_fs21 = require("fs");
7157
+ var import_node_path38 = __toESM(require("path"), 1);
7158
+ var import_types27 = require("@neat.is/types");
6911
7159
  var SCHEMA_VERSION = 4;
6912
7160
  function migrateV1ToV2(payload) {
6913
7161
  const nodes = payload.graph.nodes;
@@ -6929,12 +7177,12 @@ function migrateV2ToV3(payload) {
6929
7177
  for (const edge of edges) {
6930
7178
  const attrs = edge.attributes;
6931
7179
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
6932
- attrs.provenance = import_types26.Provenance.OBSERVED;
7180
+ attrs.provenance = import_types27.Provenance.OBSERVED;
6933
7181
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
6934
7182
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
6935
7183
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
6936
7184
  if (type && source && target) {
6937
- const newId = (0, import_types26.observedEdgeId)(source, target, type);
7185
+ const newId = (0, import_types27.observedEdgeId)(source, target, type);
6938
7186
  attrs.id = newId;
6939
7187
  if (edge.key) edge.key = newId;
6940
7188
  }
@@ -6943,7 +7191,7 @@ function migrateV2ToV3(payload) {
6943
7191
  return { ...payload, schemaVersion: 3 };
6944
7192
  }
6945
7193
  async function ensureDir(filePath) {
6946
- await import_node_fs20.promises.mkdir(import_node_path37.default.dirname(filePath), { recursive: true });
7194
+ await import_node_fs21.promises.mkdir(import_node_path38.default.dirname(filePath), { recursive: true });
6947
7195
  }
6948
7196
  async function saveGraphToDisk(graph, outPath) {
6949
7197
  await ensureDir(outPath);
@@ -6953,13 +7201,13 @@ async function saveGraphToDisk(graph, outPath) {
6953
7201
  graph: graph.export()
6954
7202
  };
6955
7203
  const tmp = `${outPath}.tmp`;
6956
- await import_node_fs20.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
6957
- await import_node_fs20.promises.rename(tmp, outPath);
7204
+ await import_node_fs21.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
7205
+ await import_node_fs21.promises.rename(tmp, outPath);
6958
7206
  }
6959
7207
  async function loadGraphFromDisk(graph, outPath) {
6960
7208
  let raw;
6961
7209
  try {
6962
- raw = await import_node_fs20.promises.readFile(outPath, "utf8");
7210
+ raw = await import_node_fs21.promises.readFile(outPath, "utf8");
6963
7211
  } catch (err) {
6964
7212
  if (err.code === "ENOENT") return;
6965
7213
  throw err;
@@ -7024,23 +7272,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
7024
7272
 
7025
7273
  // src/projects.ts
7026
7274
  init_cjs_shims();
7027
- var import_node_path38 = __toESM(require("path"), 1);
7275
+ var import_node_path39 = __toESM(require("path"), 1);
7028
7276
  function pathsForProject(project, baseDir) {
7029
7277
  if (project === DEFAULT_PROJECT) {
7030
7278
  return {
7031
- snapshotPath: import_node_path38.default.join(baseDir, "graph.json"),
7032
- errorsPath: import_node_path38.default.join(baseDir, "errors.ndjson"),
7033
- staleEventsPath: import_node_path38.default.join(baseDir, "stale-events.ndjson"),
7034
- embeddingsCachePath: import_node_path38.default.join(baseDir, "embeddings.json"),
7035
- policyViolationsPath: import_node_path38.default.join(baseDir, "policy-violations.ndjson")
7279
+ snapshotPath: import_node_path39.default.join(baseDir, "graph.json"),
7280
+ errorsPath: import_node_path39.default.join(baseDir, "errors.ndjson"),
7281
+ staleEventsPath: import_node_path39.default.join(baseDir, "stale-events.ndjson"),
7282
+ embeddingsCachePath: import_node_path39.default.join(baseDir, "embeddings.json"),
7283
+ policyViolationsPath: import_node_path39.default.join(baseDir, "policy-violations.ndjson")
7036
7284
  };
7037
7285
  }
7038
7286
  return {
7039
- snapshotPath: import_node_path38.default.join(baseDir, `${project}.json`),
7040
- errorsPath: import_node_path38.default.join(baseDir, `errors.${project}.ndjson`),
7041
- staleEventsPath: import_node_path38.default.join(baseDir, `stale-events.${project}.ndjson`),
7042
- embeddingsCachePath: import_node_path38.default.join(baseDir, `embeddings.${project}.json`),
7043
- policyViolationsPath: import_node_path38.default.join(baseDir, `policy-violations.${project}.ndjson`)
7287
+ snapshotPath: import_node_path39.default.join(baseDir, `${project}.json`),
7288
+ errorsPath: import_node_path39.default.join(baseDir, `errors.${project}.ndjson`),
7289
+ staleEventsPath: import_node_path39.default.join(baseDir, `stale-events.${project}.ndjson`),
7290
+ embeddingsCachePath: import_node_path39.default.join(baseDir, `embeddings.${project}.json`),
7291
+ policyViolationsPath: import_node_path39.default.join(baseDir, `policy-violations.${project}.ndjson`)
7044
7292
  };
7045
7293
  }
7046
7294
  var Projects = class {
@@ -7078,19 +7326,19 @@ var Projects = class {
7078
7326
  init_cjs_shims();
7079
7327
  var import_fastify = __toESM(require("fastify"), 1);
7080
7328
  var import_cors = __toESM(require("@fastify/cors"), 1);
7081
- var import_types29 = require("@neat.is/types");
7329
+ var import_types30 = require("@neat.is/types");
7082
7330
 
7083
7331
  // src/extend/index.ts
7084
7332
  init_cjs_shims();
7085
- var import_node_fs22 = require("fs");
7086
- var import_node_path40 = __toESM(require("path"), 1);
7333
+ var import_node_fs23 = require("fs");
7334
+ var import_node_path41 = __toESM(require("path"), 1);
7087
7335
  var import_node_os2 = __toESM(require("os"), 1);
7088
7336
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
7089
7337
 
7090
7338
  // src/installers/package-manager.ts
7091
7339
  init_cjs_shims();
7092
- var import_node_fs21 = require("fs");
7093
- var import_node_path39 = __toESM(require("path"), 1);
7340
+ var import_node_fs22 = require("fs");
7341
+ var import_node_path40 = __toESM(require("path"), 1);
7094
7342
  var import_node_child_process = require("child_process");
7095
7343
  var LOCKFILE_PRIORITY = [
7096
7344
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -7105,29 +7353,29 @@ var LOCKFILE_PRIORITY = [
7105
7353
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
7106
7354
  async function exists2(p) {
7107
7355
  try {
7108
- await import_node_fs21.promises.access(p);
7356
+ await import_node_fs22.promises.access(p);
7109
7357
  return true;
7110
7358
  } catch {
7111
7359
  return false;
7112
7360
  }
7113
7361
  }
7114
7362
  async function detectPackageManager(serviceDir) {
7115
- let dir = import_node_path39.default.resolve(serviceDir);
7363
+ let dir = import_node_path40.default.resolve(serviceDir);
7116
7364
  const stops = /* @__PURE__ */ new Set();
7117
7365
  for (let i = 0; i < 64; i++) {
7118
7366
  if (stops.has(dir)) break;
7119
7367
  stops.add(dir);
7120
7368
  for (const candidate of LOCKFILE_PRIORITY) {
7121
- const lockPath = import_node_path39.default.join(dir, candidate.lockfile);
7369
+ const lockPath = import_node_path40.default.join(dir, candidate.lockfile);
7122
7370
  if (await exists2(lockPath)) {
7123
7371
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
7124
7372
  }
7125
7373
  }
7126
- const parent = import_node_path39.default.dirname(dir);
7374
+ const parent = import_node_path40.default.dirname(dir);
7127
7375
  if (parent === dir) break;
7128
7376
  dir = parent;
7129
7377
  }
7130
- return { pm: "npm", cwd: import_node_path39.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
7378
+ return { pm: "npm", cwd: import_node_path40.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
7131
7379
  }
7132
7380
  async function runPackageManagerInstall(cmd) {
7133
7381
  return new Promise((resolve) => {
@@ -7169,30 +7417,30 @@ ${err.message}`
7169
7417
  // src/extend/index.ts
7170
7418
  async function fileExists2(p) {
7171
7419
  try {
7172
- await import_node_fs22.promises.access(p);
7420
+ await import_node_fs23.promises.access(p);
7173
7421
  return true;
7174
7422
  } catch {
7175
7423
  return false;
7176
7424
  }
7177
7425
  }
7178
7426
  async function readPackageJson(scanPath) {
7179
- const pkgPath = import_node_path40.default.join(scanPath, "package.json");
7180
- const raw = await import_node_fs22.promises.readFile(pkgPath, "utf8");
7427
+ const pkgPath = import_node_path41.default.join(scanPath, "package.json");
7428
+ const raw = await import_node_fs23.promises.readFile(pkgPath, "utf8");
7181
7429
  return JSON.parse(raw);
7182
7430
  }
7183
7431
  async function findHookFiles(scanPath) {
7184
- const entries = await import_node_fs22.promises.readdir(scanPath);
7432
+ const entries = await import_node_fs23.promises.readdir(scanPath);
7185
7433
  return entries.filter(
7186
7434
  (e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
7187
7435
  ).sort();
7188
7436
  }
7189
7437
  function extendLogPath() {
7190
- return process.env.NEAT_EXTEND_LOG ?? import_node_path40.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
7438
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path41.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
7191
7439
  }
7192
7440
  async function appendExtendLog(entry2) {
7193
7441
  const logPath = extendLogPath();
7194
- await import_node_fs22.promises.mkdir(import_node_path40.default.dirname(logPath), { recursive: true });
7195
- await import_node_fs22.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
7442
+ await import_node_fs23.promises.mkdir(import_node_path41.default.dirname(logPath), { recursive: true });
7443
+ await import_node_fs23.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
7196
7444
  }
7197
7445
  function splicedContent(fileContent, snippet2) {
7198
7446
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -7250,7 +7498,7 @@ function lookupInstrumentation(library, installedVersion) {
7250
7498
  }
7251
7499
  async function describeProjectInstrumentation(ctx) {
7252
7500
  const hookFiles = await findHookFiles(ctx.scanPath);
7253
- const envNeat = await fileExists2(import_node_path40.default.join(ctx.scanPath, ".env.neat"));
7501
+ const envNeat = await fileExists2(import_node_path41.default.join(ctx.scanPath, ".env.neat"));
7254
7502
  const registryInstrPackages = new Set(
7255
7503
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
7256
7504
  );
@@ -7272,31 +7520,31 @@ async function applyExtension(ctx, args, options) {
7272
7520
  );
7273
7521
  }
7274
7522
  for (const file of hookFiles) {
7275
- const content = await import_node_fs22.promises.readFile(import_node_path40.default.join(ctx.scanPath, file), "utf8");
7523
+ const content = await import_node_fs23.promises.readFile(import_node_path41.default.join(ctx.scanPath, file), "utf8");
7276
7524
  if (content.includes(args.registration_snippet)) {
7277
7525
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
7278
7526
  }
7279
7527
  }
7280
7528
  const primaryFile = hookFiles[0];
7281
- const primaryPath = import_node_path40.default.join(ctx.scanPath, primaryFile);
7529
+ const primaryPath = import_node_path41.default.join(ctx.scanPath, primaryFile);
7282
7530
  const filesTouched = [];
7283
7531
  const depsAdded = [];
7284
- const pkgPath = import_node_path40.default.join(ctx.scanPath, "package.json");
7532
+ const pkgPath = import_node_path41.default.join(ctx.scanPath, "package.json");
7285
7533
  const pkg = await readPackageJson(ctx.scanPath);
7286
7534
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
7287
7535
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
7288
- await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
7536
+ await import_node_fs23.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
7289
7537
  filesTouched.push("package.json");
7290
7538
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
7291
7539
  }
7292
- const hookContent = await import_node_fs22.promises.readFile(primaryPath, "utf8");
7540
+ const hookContent = await import_node_fs23.promises.readFile(primaryPath, "utf8");
7293
7541
  const patched = splicedContent(hookContent, args.registration_snippet);
7294
7542
  if (!patched) {
7295
7543
  throw new Error(
7296
7544
  `Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
7297
7545
  );
7298
7546
  }
7299
- await import_node_fs22.promises.writeFile(primaryPath, patched, "utf8");
7547
+ await import_node_fs23.promises.writeFile(primaryPath, patched, "utf8");
7300
7548
  filesTouched.push(primaryFile);
7301
7549
  const cmd = await detectPackageManager(ctx.scanPath);
7302
7550
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -7327,7 +7575,7 @@ async function dryRunExtension(ctx, args) {
7327
7575
  };
7328
7576
  }
7329
7577
  for (const file of hookFiles) {
7330
- const content = await import_node_fs22.promises.readFile(import_node_path40.default.join(ctx.scanPath, file), "utf8");
7578
+ const content = await import_node_fs23.promises.readFile(import_node_path41.default.join(ctx.scanPath, file), "utf8");
7331
7579
  if (content.includes(args.registration_snippet)) {
7332
7580
  return {
7333
7581
  library: args.library,
@@ -7349,7 +7597,7 @@ async function dryRunExtension(ctx, args) {
7349
7597
  depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
7350
7598
  filesTouched.push("package.json");
7351
7599
  }
7352
- const hookContent = await import_node_fs22.promises.readFile(import_node_path40.default.join(ctx.scanPath, primaryFile), "utf8");
7600
+ const hookContent = await import_node_fs23.promises.readFile(import_node_path41.default.join(ctx.scanPath, primaryFile), "utf8");
7353
7601
  const patched = splicedContent(hookContent, args.registration_snippet);
7354
7602
  if (patched) {
7355
7603
  filesTouched.push(primaryFile);
@@ -7364,28 +7612,28 @@ async function rollbackExtension(ctx, args) {
7364
7612
  if (!await fileExists2(logPath)) {
7365
7613
  return { undone: false, message: "no apply found for library" };
7366
7614
  }
7367
- const raw = await import_node_fs22.promises.readFile(logPath, "utf8");
7615
+ const raw = await import_node_fs23.promises.readFile(logPath, "utf8");
7368
7616
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
7369
7617
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
7370
7618
  if (!match) {
7371
7619
  return { undone: false, message: "no apply found for library" };
7372
7620
  }
7373
- const pkgPath = import_node_path40.default.join(ctx.scanPath, "package.json");
7621
+ const pkgPath = import_node_path41.default.join(ctx.scanPath, "package.json");
7374
7622
  if (await fileExists2(pkgPath)) {
7375
7623
  const pkg = await readPackageJson(ctx.scanPath);
7376
7624
  if (pkg.dependencies?.[match.instrumentation_package]) {
7377
7625
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
7378
7626
  pkg.dependencies = rest;
7379
- await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
7627
+ await import_node_fs23.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
7380
7628
  }
7381
7629
  }
7382
7630
  const hookFiles = await findHookFiles(ctx.scanPath);
7383
7631
  for (const file of hookFiles) {
7384
- const filePath = import_node_path40.default.join(ctx.scanPath, file);
7385
- const content = await import_node_fs22.promises.readFile(filePath, "utf8");
7632
+ const filePath = import_node_path41.default.join(ctx.scanPath, file);
7633
+ const content = await import_node_fs23.promises.readFile(filePath, "utf8");
7386
7634
  if (content.includes(match.registration_snippet)) {
7387
7635
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
7388
- await import_node_fs22.promises.writeFile(filePath, filtered, "utf8");
7636
+ await import_node_fs23.promises.writeFile(filePath, filtered, "utf8");
7389
7637
  break;
7390
7638
  }
7391
7639
  }
@@ -7397,7 +7645,7 @@ async function rollbackExtension(ctx, args) {
7397
7645
 
7398
7646
  // src/divergences.ts
7399
7647
  init_cjs_shims();
7400
- var import_types27 = require("@neat.is/types");
7648
+ var import_types28 = require("@neat.is/types");
7401
7649
  function bucketKey(source, target, type) {
7402
7650
  return `${type}|${source}|${target}`;
7403
7651
  }
@@ -7405,22 +7653,22 @@ function bucketEdges(graph) {
7405
7653
  const buckets2 = /* @__PURE__ */ new Map();
7406
7654
  graph.forEachEdge((id, attrs) => {
7407
7655
  const e = attrs;
7408
- const parsed = (0, import_types27.parseEdgeId)(id);
7656
+ const parsed = (0, import_types28.parseEdgeId)(id);
7409
7657
  const provenance = parsed?.provenance ?? e.provenance;
7410
7658
  const key = bucketKey(e.source, e.target, e.type);
7411
7659
  const cur = buckets2.get(key) ?? { source: e.source, target: e.target, type: e.type };
7412
7660
  switch (provenance) {
7413
- case import_types27.Provenance.EXTRACTED:
7661
+ case import_types28.Provenance.EXTRACTED:
7414
7662
  cur.extracted = e;
7415
7663
  break;
7416
- case import_types27.Provenance.OBSERVED:
7664
+ case import_types28.Provenance.OBSERVED:
7417
7665
  cur.observed = e;
7418
7666
  break;
7419
- case import_types27.Provenance.INFERRED:
7667
+ case import_types28.Provenance.INFERRED:
7420
7668
  cur.inferred = e;
7421
7669
  break;
7422
7670
  default:
7423
- if (e.provenance === import_types27.Provenance.STALE) cur.stale = e;
7671
+ if (e.provenance === import_types28.Provenance.STALE) cur.stale = e;
7424
7672
  }
7425
7673
  buckets2.set(key, cur);
7426
7674
  });
@@ -7429,12 +7677,12 @@ function bucketEdges(graph) {
7429
7677
  function nodeIsFrontier(graph, nodeId) {
7430
7678
  if (!graph.hasNode(nodeId)) return false;
7431
7679
  const attrs = graph.getNodeAttributes(nodeId);
7432
- return attrs.type === import_types27.NodeType.FrontierNode;
7680
+ return attrs.type === import_types28.NodeType.FrontierNode;
7433
7681
  }
7434
7682
  function nodeIsWebsocketChannel(graph, nodeId) {
7435
7683
  if (!graph.hasNode(nodeId)) return false;
7436
7684
  const attrs = graph.getNodeAttributes(nodeId);
7437
- return attrs.type === import_types27.NodeType.WebSocketChannelNode;
7685
+ return attrs.type === import_types28.NodeType.WebSocketChannelNode;
7438
7686
  }
7439
7687
  function clampConfidence(n) {
7440
7688
  if (!Number.isFinite(n)) return 0;
@@ -7454,14 +7702,14 @@ function gradedConfidence(edge) {
7454
7702
  return clampConfidence(confidenceForEdge(edge));
7455
7703
  }
7456
7704
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
7457
- import_types27.EdgeType.CALLS,
7458
- import_types27.EdgeType.CONNECTS_TO,
7459
- import_types27.EdgeType.PUBLISHES_TO,
7460
- import_types27.EdgeType.CONSUMES_FROM
7705
+ import_types28.EdgeType.CALLS,
7706
+ import_types28.EdgeType.CONNECTS_TO,
7707
+ import_types28.EdgeType.PUBLISHES_TO,
7708
+ import_types28.EdgeType.CONSUMES_FROM
7461
7709
  ]);
7462
7710
  function detectMissingDivergences(graph, bucket) {
7463
7711
  const out = [];
7464
- if (bucket.type === import_types27.EdgeType.CONTAINS) return out;
7712
+ if (bucket.type === import_types28.EdgeType.CONTAINS) return out;
7465
7713
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
7466
7714
  if (!nodeIsFrontier(graph, bucket.target)) {
7467
7715
  out.push({
@@ -7502,7 +7750,7 @@ function declaredHostFor(svc) {
7502
7750
  function hasExtractedConfiguredBy(graph, svcId) {
7503
7751
  for (const edgeId of graph.outboundEdges(svcId)) {
7504
7752
  const e = graph.getEdgeAttributes(edgeId);
7505
- if (e.type === import_types27.EdgeType.CONFIGURED_BY && e.provenance === import_types27.Provenance.EXTRACTED) {
7753
+ if (e.type === import_types28.EdgeType.CONFIGURED_BY && e.provenance === import_types28.Provenance.EXTRACTED) {
7506
7754
  return true;
7507
7755
  }
7508
7756
  }
@@ -7515,10 +7763,10 @@ function detectHostMismatch(graph, svcId, svc) {
7515
7763
  const out = [];
7516
7764
  for (const edgeId of graph.outboundEdges(svcId)) {
7517
7765
  const edge = graph.getEdgeAttributes(edgeId);
7518
- if (edge.type !== import_types27.EdgeType.CONNECTS_TO) continue;
7519
- if (edge.provenance !== import_types27.Provenance.OBSERVED) continue;
7766
+ if (edge.type !== import_types28.EdgeType.CONNECTS_TO) continue;
7767
+ if (edge.provenance !== import_types28.Provenance.OBSERVED) continue;
7520
7768
  const target = graph.getNodeAttributes(edge.target);
7521
- if (target.type !== import_types27.NodeType.DatabaseNode) continue;
7769
+ if (target.type !== import_types28.NodeType.DatabaseNode) continue;
7522
7770
  const observedHost = target.host?.trim();
7523
7771
  if (!observedHost) continue;
7524
7772
  if (observedHost === declaredHost) continue;
@@ -7540,10 +7788,10 @@ function detectCompatDivergences(graph, svcId, svc) {
7540
7788
  const deps = svc.dependencies ?? {};
7541
7789
  for (const edgeId of graph.outboundEdges(svcId)) {
7542
7790
  const edge = graph.getEdgeAttributes(edgeId);
7543
- if (edge.type !== import_types27.EdgeType.CONNECTS_TO) continue;
7544
- if (edge.provenance !== import_types27.Provenance.OBSERVED) continue;
7791
+ if (edge.type !== import_types28.EdgeType.CONNECTS_TO) continue;
7792
+ if (edge.provenance !== import_types28.Provenance.OBSERVED) continue;
7545
7793
  const target = graph.getNodeAttributes(edge.target);
7546
- if (target.type !== import_types27.NodeType.DatabaseNode) continue;
7794
+ if (target.type !== import_types28.NodeType.DatabaseNode) continue;
7547
7795
  for (const pair of compatPairs()) {
7548
7796
  if (pair.engine !== target.engine) continue;
7549
7797
  const declared = deps[pair.driver];
@@ -7602,7 +7850,7 @@ function suppressHostMismatchHalves(all) {
7602
7850
  for (const d of all) {
7603
7851
  if (d.type !== "host-mismatch") continue;
7604
7852
  observedHalf.add(`${d.source}->${d.target}`);
7605
- declaredHalf.add((0, import_types27.databaseId)(d.extractedHost));
7853
+ declaredHalf.add((0, import_types28.databaseId)(d.extractedHost));
7606
7854
  }
7607
7855
  if (observedHalf.size === 0) return all;
7608
7856
  return all.filter((d) => {
@@ -7621,7 +7869,7 @@ function computeDivergences(graph, opts = {}) {
7621
7869
  }
7622
7870
  graph.forEachNode((nodeId, attrs) => {
7623
7871
  const n = attrs;
7624
- if (n.type !== import_types27.NodeType.ServiceNode) return;
7872
+ if (n.type !== import_types28.NodeType.ServiceNode) return;
7625
7873
  const svc = n;
7626
7874
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
7627
7875
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -7655,7 +7903,7 @@ function computeDivergences(graph, opts = {}) {
7655
7903
  if (a.source !== b.source) return a.source.localeCompare(b.source);
7656
7904
  return a.target.localeCompare(b.target);
7657
7905
  });
7658
- return import_types27.DivergenceResultSchema.parse({
7906
+ return import_types28.DivergenceResultSchema.parse({
7659
7907
  divergences: filtered,
7660
7908
  totalAffected: filtered.length,
7661
7909
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -7709,7 +7957,7 @@ function queryLogEntries(opts) {
7709
7957
 
7710
7958
  // src/diff.ts
7711
7959
  init_cjs_shims();
7712
- var import_node_fs23 = require("fs");
7960
+ var import_node_fs24 = require("fs");
7713
7961
  async function loadSnapshotForDiff(target) {
7714
7962
  if (/^https?:\/\//i.test(target)) {
7715
7963
  const res = await fetch(target);
@@ -7718,7 +7966,7 @@ async function loadSnapshotForDiff(target) {
7718
7966
  }
7719
7967
  return await res.json();
7720
7968
  }
7721
- const raw = await import_node_fs23.promises.readFile(target, "utf8");
7969
+ const raw = await import_node_fs24.promises.readFile(target, "utf8");
7722
7970
  return JSON.parse(raw);
7723
7971
  }
7724
7972
  function indexEntries(entries) {
@@ -7786,25 +8034,25 @@ function canonicalJson(value) {
7786
8034
 
7787
8035
  // src/registry.ts
7788
8036
  init_cjs_shims();
7789
- var import_node_fs24 = require("fs");
8037
+ var import_node_fs25 = require("fs");
7790
8038
  var import_node_os3 = __toESM(require("os"), 1);
7791
- var import_node_path41 = __toESM(require("path"), 1);
7792
- var import_types28 = require("@neat.is/types");
8039
+ var import_node_path42 = __toESM(require("path"), 1);
8040
+ var import_types29 = require("@neat.is/types");
7793
8041
  var LOCK_TIMEOUT_MS = 5e3;
7794
8042
  var LOCK_RETRY_MS = 50;
7795
8043
  function neatHome() {
7796
8044
  const override = process.env.NEAT_HOME;
7797
- if (override && override.length > 0) return import_node_path41.default.resolve(override);
7798
- return import_node_path41.default.join(import_node_os3.default.homedir(), ".neat");
8045
+ if (override && override.length > 0) return import_node_path42.default.resolve(override);
8046
+ return import_node_path42.default.join(import_node_os3.default.homedir(), ".neat");
7799
8047
  }
7800
8048
  function registryPath() {
7801
- return import_node_path41.default.join(neatHome(), "projects.json");
8049
+ return import_node_path42.default.join(neatHome(), "projects.json");
7802
8050
  }
7803
8051
  function registryLockPath() {
7804
- return import_node_path41.default.join(neatHome(), "projects.json.lock");
8052
+ return import_node_path42.default.join(neatHome(), "projects.json.lock");
7805
8053
  }
7806
8054
  function daemonPidPath() {
7807
- return import_node_path41.default.join(neatHome(), "neatd.pid");
8055
+ return import_node_path42.default.join(neatHome(), "neatd.pid");
7808
8056
  }
7809
8057
  function isPidAliveDefault(pid) {
7810
8058
  try {
@@ -7816,7 +8064,7 @@ function isPidAliveDefault(pid) {
7816
8064
  }
7817
8065
  async function readPidFile(file) {
7818
8066
  try {
7819
- const raw = await import_node_fs24.promises.readFile(file, "utf8");
8067
+ const raw = await import_node_fs25.promises.readFile(file, "utf8");
7820
8068
  const pid = Number.parseInt(raw.trim(), 10);
7821
8069
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
7822
8070
  } catch {
@@ -7864,24 +8112,24 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
7864
8112
  }
7865
8113
  }
7866
8114
  async function writeAtomically(target, contents) {
7867
- await import_node_fs24.promises.mkdir(import_node_path41.default.dirname(target), { recursive: true });
8115
+ await import_node_fs25.promises.mkdir(import_node_path42.default.dirname(target), { recursive: true });
7868
8116
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
7869
- const fd = await import_node_fs24.promises.open(tmp, "w");
8117
+ const fd = await import_node_fs25.promises.open(tmp, "w");
7870
8118
  try {
7871
8119
  await fd.writeFile(contents, "utf8");
7872
8120
  await fd.sync();
7873
8121
  } finally {
7874
8122
  await fd.close();
7875
8123
  }
7876
- await import_node_fs24.promises.rename(tmp, target);
8124
+ await import_node_fs25.promises.rename(tmp, target);
7877
8125
  }
7878
8126
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
7879
8127
  const deadline = Date.now() + timeoutMs;
7880
- await import_node_fs24.promises.mkdir(import_node_path41.default.dirname(lockPath), { recursive: true });
8128
+ await import_node_fs25.promises.mkdir(import_node_path42.default.dirname(lockPath), { recursive: true });
7881
8129
  let probedHolder = false;
7882
8130
  while (true) {
7883
8131
  try {
7884
- const fd = await import_node_fs24.promises.open(lockPath, "wx");
8132
+ const fd = await import_node_fs25.promises.open(lockPath, "wx");
7885
8133
  try {
7886
8134
  await fd.writeFile(`${process.pid}
7887
8135
  `, "utf8");
@@ -7906,7 +8154,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
7906
8154
  }
7907
8155
  }
7908
8156
  async function releaseLock(lockPath) {
7909
- await import_node_fs24.promises.unlink(lockPath).catch(() => {
8157
+ await import_node_fs25.promises.unlink(lockPath).catch(() => {
7910
8158
  });
7911
8159
  }
7912
8160
  async function withLock(fn) {
@@ -7922,7 +8170,7 @@ async function readRegistry() {
7922
8170
  const file = registryPath();
7923
8171
  let raw;
7924
8172
  try {
7925
- raw = await import_node_fs24.promises.readFile(file, "utf8");
8173
+ raw = await import_node_fs25.promises.readFile(file, "utf8");
7926
8174
  } catch (err) {
7927
8175
  if (err.code === "ENOENT") {
7928
8176
  return { version: 1, projects: [] };
@@ -7930,10 +8178,10 @@ async function readRegistry() {
7930
8178
  throw err;
7931
8179
  }
7932
8180
  const parsed = JSON.parse(raw);
7933
- return import_types28.RegistryFileSchema.parse(parsed);
8181
+ return import_types29.RegistryFileSchema.parse(parsed);
7934
8182
  }
7935
8183
  async function writeRegistry(reg) {
7936
- const validated = import_types28.RegistryFileSchema.parse(reg);
8184
+ const validated = import_types29.RegistryFileSchema.parse(reg);
7937
8185
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
7938
8186
  }
7939
8187
  async function getProject(name) {
@@ -7977,7 +8225,7 @@ function pruneTtlMs() {
7977
8225
  }
7978
8226
  async function statPathStatus(p) {
7979
8227
  try {
7980
- const stat = await import_node_fs24.promises.stat(p);
8228
+ const stat = await import_node_fs25.promises.stat(p);
7981
8229
  return stat.isDirectory() ? "present" : "unknown";
7982
8230
  } catch (err) {
7983
8231
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -8224,11 +8472,11 @@ function registerRoutes(scope, ctx) {
8224
8472
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8225
8473
  const parsed = [];
8226
8474
  for (const c of candidates) {
8227
- const r = import_types29.DivergenceTypeSchema.safeParse(c);
8475
+ const r = import_types30.DivergenceTypeSchema.safeParse(c);
8228
8476
  if (!r.success) {
8229
8477
  return reply.code(400).send({
8230
8478
  error: `unknown divergence type "${c}"`,
8231
- allowed: import_types29.DivergenceTypeSchema.options
8479
+ allowed: import_types30.DivergenceTypeSchema.options
8232
8480
  });
8233
8481
  }
8234
8482
  parsed.push(r.data);
@@ -8479,7 +8727,7 @@ function registerRoutes(scope, ctx) {
8479
8727
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
8480
8728
  let violations = await log.readAll();
8481
8729
  if (req2.query.severity) {
8482
- const sev = import_types29.PolicySeveritySchema.safeParse(req2.query.severity);
8730
+ const sev = import_types30.PolicySeveritySchema.safeParse(req2.query.severity);
8483
8731
  if (!sev.success) {
8484
8732
  return reply.code(400).send({
8485
8733
  error: "invalid severity",
@@ -8518,7 +8766,7 @@ function registerRoutes(scope, ctx) {
8518
8766
  scope.post("/policies/check", async (req2, reply) => {
8519
8767
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
8520
8768
  if (!proj) return;
8521
- const parsed = import_types29.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
8769
+ const parsed = import_types30.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
8522
8770
  if (!parsed.success) {
8523
8771
  return reply.code(400).send({
8524
8772
  error: "invalid /policies/check body",
@@ -8791,6 +9039,10 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
8791
9039
  unresolved++;
8792
9040
  continue;
8793
9041
  }
9042
+ if (resolved.ensureInfraNode) {
9043
+ const { kind, name, provider } = resolved.ensureInfraNode;
9044
+ ensureInfraNode(graph, kind, name, provider);
9045
+ }
8794
9046
  const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
8795
9047
  const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : void 0;
8796
9048
  const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
@@ -9192,10 +9444,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
9192
9444
  // src/connectors/supabase/map.ts
9193
9445
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
9194
9446
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
9195
- function targetFromRestPath(path49) {
9196
- const rpcMatch = REST_RPC_PATH_RE.exec(path49);
9447
+ function targetFromRestPath(path50) {
9448
+ const rpcMatch = REST_RPC_PATH_RE.exec(path50);
9197
9449
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
9198
- const tableMatch = REST_TABLE_PATH_RE.exec(path49);
9450
+ const tableMatch = REST_TABLE_PATH_RE.exec(path50);
9199
9451
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
9200
9452
  return null;
9201
9453
  }
@@ -9298,19 +9550,19 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
9298
9550
 
9299
9551
  // src/connectors/supabase/resolve.ts
9300
9552
  init_cjs_shims();
9301
- var import_types31 = require("@neat.is/types");
9553
+ var import_types32 = require("@neat.is/types");
9302
9554
  function createSupabaseResolveTarget(graph, config) {
9303
9555
  return (signal, _ctx) => {
9304
9556
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
9305
9557
  return null;
9306
9558
  }
9307
- const subResourceId = (0, import_types31.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
9559
+ const subResourceId = (0, import_types32.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
9308
9560
  if (graph.hasNode(subResourceId)) {
9309
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types31.EdgeType.CALLS };
9561
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types32.EdgeType.CALLS };
9310
9562
  }
9311
- const projectLevelId = (0, import_types31.infraId)("supabase", config.nodeRef);
9563
+ const projectLevelId = (0, import_types32.infraId)("supabase", config.nodeRef);
9312
9564
  if (graph.hasNode(projectLevelId)) {
9313
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types31.EdgeType.CALLS };
9565
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types32.EdgeType.CALLS };
9314
9566
  }
9315
9567
  return null;
9316
9568
  };
@@ -9368,7 +9620,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
9368
9620
 
9369
9621
  // src/connectors/railway/index.ts
9370
9622
  init_cjs_shims();
9371
- var import_types35 = require("@neat.is/types");
9623
+ var import_types36 = require("@neat.is/types");
9372
9624
 
9373
9625
  // src/connectors/railway/client.ts
9374
9626
  init_cjs_shims();
@@ -9513,7 +9765,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
9513
9765
  const out = [];
9514
9766
  graph.forEachNode((_id, attrs) => {
9515
9767
  const node = attrs;
9516
- if (node.type !== import_types35.NodeType.RouteNode) return;
9768
+ if (node.type !== import_types36.NodeType.RouteNode) return;
9517
9769
  const route = attrs;
9518
9770
  if (route.service !== serviceName) return;
9519
9771
  out.push({
@@ -9609,12 +9861,12 @@ function createRailwayResolveTarget(config) {
9609
9861
  const serviceName = config.serviceNameById[config.serviceId];
9610
9862
  if (!serviceName) return null;
9611
9863
  if (signal.targetKind === ROUTE_TARGET_KIND) {
9612
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types35.EdgeType.CALLS };
9864
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types36.EdgeType.CALLS };
9613
9865
  }
9614
9866
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
9615
9867
  const peerName = config.serviceNameById[signal.targetName];
9616
9868
  if (!peerName) return null;
9617
- return { targetNodeId: (0, import_types35.serviceId)(peerName), serviceName, edgeType: import_types35.EdgeType.CONNECTS_TO };
9869
+ return { targetNodeId: (0, import_types36.serviceId)(peerName), serviceName, edgeType: import_types36.EdgeType.CONNECTS_TO };
9618
9870
  }
9619
9871
  return null;
9620
9872
  };
@@ -9729,9 +9981,9 @@ function parseFirebaseTargetName(targetName) {
9729
9981
  const secondSep = rest.indexOf(FIELD_SEP);
9730
9982
  if (secondSep === -1) return null;
9731
9983
  const method = rest.slice(0, secondSep);
9732
- const path49 = rest.slice(secondSep + 1);
9733
- if (!resourceName || !method || !path49) return null;
9734
- return { resourceName, method, path: path49 };
9984
+ const path50 = rest.slice(secondSep + 1);
9985
+ if (!resourceName || !method || !path50) return null;
9986
+ return { resourceName, method, path: path50 };
9735
9987
  }
9736
9988
  function resourceNameFor(type, labels) {
9737
9989
  if (!labels) return null;
@@ -9768,14 +10020,14 @@ function mapLogEntryToSignal(entry2) {
9768
10020
  if (!req2) return null;
9769
10021
  if (!req2.requestMethod) return null;
9770
10022
  const method = req2.requestMethod.toUpperCase();
9771
- const path49 = pathFromRequestUrl(req2.requestUrl);
9772
- if (path49 === null) return null;
10023
+ const path50 = pathFromRequestUrl(req2.requestUrl);
10024
+ if (path50 === null) return null;
9773
10025
  const timestamp = entry2.timestamp;
9774
10026
  if (!timestamp) return null;
9775
10027
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
9776
10028
  return {
9777
10029
  targetKind: resourceType,
9778
- targetName: packFirebaseTargetName({ resourceName, method, path: path49 }),
10030
+ targetName: packFirebaseTargetName({ resourceName, method, path: path50 }),
9779
10031
  callCount: 1,
9780
10032
  errorCount: isError ? 1 : 0,
9781
10033
  lastObservedIso: timestamp
@@ -9792,7 +10044,7 @@ function mapLogEntriesToSignals(entries) {
9792
10044
 
9793
10045
  // src/connectors/firebase/resolve.ts
9794
10046
  init_cjs_shims();
9795
- var import_types36 = require("@neat.is/types");
10047
+ var import_types37 = require("@neat.is/types");
9796
10048
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
9797
10049
  switch (resourceType) {
9798
10050
  case "cloud_function":
@@ -9807,7 +10059,7 @@ function routeEntriesFor(graph, serviceName) {
9807
10059
  const entries = [];
9808
10060
  graph.forEachNode((_id, attrs) => {
9809
10061
  const node = attrs;
9810
- if (node.type !== import_types36.NodeType.RouteNode) return;
10062
+ if (node.type !== import_types37.NodeType.RouteNode) return;
9811
10063
  const route = attrs;
9812
10064
  if (route.service !== serviceName) return;
9813
10065
  entries.push({
@@ -9839,7 +10091,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
9839
10091
  return {
9840
10092
  targetNodeId: match.routeNodeId,
9841
10093
  serviceName,
9842
- edgeType: import_types36.EdgeType.CALLS
10094
+ edgeType: import_types37.EdgeType.CALLS
9843
10095
  };
9844
10096
  };
9845
10097
  }
@@ -9866,7 +10118,7 @@ init_cjs_shims();
9866
10118
 
9867
10119
  // src/connectors/cloudflare/connector.ts
9868
10120
  init_cjs_shims();
9869
- var import_types38 = require("@neat.is/types");
10121
+ var import_types39 = require("@neat.is/types");
9870
10122
 
9871
10123
  // src/connectors/cloudflare/client.ts
9872
10124
  init_cjs_shims();
@@ -9916,7 +10168,14 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
9916
10168
  const message = payload.errors?.map((e) => e.message).join("; ") || "unknown error";
9917
10169
  throw new Error(`cloudflare connector: telemetry query returned an error (${message})`);
9918
10170
  }
9919
- return payload.result?.events?.events ?? [];
10171
+ const events = payload.result?.events?.events;
10172
+ if (events === void 0) {
10173
+ console.warn(
10174
+ "[neat connector] cloudflare: telemetry query returned success:true but no result.events.events array \u2014 the response shape may have changed; treating as zero events this tick"
10175
+ );
10176
+ return [];
10177
+ }
10178
+ return events;
9920
10179
  }
9921
10180
 
9922
10181
  // src/connectors/cloudflare/map.ts
@@ -9947,6 +10206,13 @@ function parseHttpMethodFromTrigger(trigger) {
9947
10206
  const method = token.toUpperCase();
9948
10207
  return HTTP_METHODS.has(method) ? method : null;
9949
10208
  }
10209
+ function parsePathFromTrigger(trigger) {
10210
+ const trimmed = trigger.trim();
10211
+ const spaceIdx = trimmed.indexOf(" ");
10212
+ if (spaceIdx === -1) return void 0;
10213
+ const rest = trimmed.slice(spaceIdx + 1).trim();
10214
+ return rest.length > 0 ? rest : void 0;
10215
+ }
9950
10216
  var ERROR_STATUS_THRESHOLD3 = 500;
9951
10217
  function mapEventToSignal(event) {
9952
10218
  const metadata = event.$metadata;
@@ -9959,6 +10225,7 @@ function mapEventToSignal(event) {
9959
10225
  if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs)) return null;
9960
10226
  const statusCode = metadata?.statusCode;
9961
10227
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
10228
+ const path50 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
9962
10229
  return {
9963
10230
  targetKind: CLOUDFLARE_TARGET_KIND,
9964
10231
  targetName: scriptName,
@@ -9966,6 +10233,7 @@ function mapEventToSignal(event) {
9966
10233
  errorCount: isError ? 1 : 0,
9967
10234
  lastObservedIso: new Date(timestampMs).toISOString(),
9968
10235
  method,
10236
+ ...path50 ? { path: path50 } : {},
9969
10237
  ...typeof statusCode === "number" ? { statusCode } : {},
9970
10238
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
9971
10239
  };
@@ -10000,15 +10268,63 @@ var CloudflareConnector = class {
10000
10268
  return signals;
10001
10269
  }
10002
10270
  };
10003
- function createCloudflareResolveTarget(config) {
10271
+ function findTaggedWorkerFileNode(graph, workerName) {
10272
+ let found = null;
10273
+ graph.forEachNode((id, attrs) => {
10274
+ if (found) return;
10275
+ const a = attrs;
10276
+ if (a.type === import_types39.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
10277
+ found = id;
10278
+ }
10279
+ });
10280
+ return found;
10281
+ }
10282
+ function findMatchingRouteNode(graph, serviceName, method, path50) {
10283
+ const normalizedPath = normalizePathTemplate(path50);
10284
+ let found = null;
10285
+ graph.forEachNode((id, attrs) => {
10286
+ if (found) return;
10287
+ const a = attrs;
10288
+ if (a.type !== import_types39.NodeType.RouteNode || a.service !== serviceName) return;
10289
+ if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
10290
+ const routeMethod = (a.method ?? "").toUpperCase();
10291
+ if (routeMethod !== "ALL" && routeMethod !== method) return;
10292
+ found = id;
10293
+ });
10294
+ return found;
10295
+ }
10296
+ function createCloudflareResolveTarget(config, graph) {
10004
10297
  return (signal) => {
10005
10298
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
10006
- const mapping = config.workers[signal.targetName];
10007
- if (!mapping) return null;
10299
+ const scriptName = signal.targetName;
10300
+ const { method, path: path50 } = signal;
10301
+ const resolveRouteGrain = (serviceName, wholeFileId) => {
10302
+ if (!method || !path50) return wholeFileId;
10303
+ return findMatchingRouteNode(graph, serviceName, method, path50) ?? wholeFileId;
10304
+ };
10305
+ const mapping = config.workers?.[scriptName];
10306
+ if (mapping) {
10307
+ const wholeFileId = (0, import_types39.fileId)(mapping.service, mapping.entryFile);
10308
+ return {
10309
+ targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
10310
+ serviceName: mapping.service,
10311
+ edgeType: import_types39.EdgeType.CALLS
10312
+ };
10313
+ }
10314
+ const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
10315
+ if (taggedFileId) {
10316
+ const fileNode = graph.getNodeAttributes(taggedFileId);
10317
+ return {
10318
+ targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
10319
+ serviceName: fileNode.service,
10320
+ edgeType: import_types39.EdgeType.CALLS
10321
+ };
10322
+ }
10008
10323
  return {
10009
- targetNodeId: (0, import_types38.fileId)(mapping.service, mapping.entryFile),
10010
- serviceName: mapping.service,
10011
- edgeType: import_types38.EdgeType.CALLS
10324
+ targetNodeId: (0, import_types39.infraId)("cloudflare-worker", scriptName),
10325
+ serviceName: scriptName,
10326
+ edgeType: import_types39.EdgeType.CALLS,
10327
+ ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
10012
10328
  };
10013
10329
  };
10014
10330
  }
@@ -10016,8 +10332,8 @@ function createCloudflareResolveTarget(config) {
10016
10332
  // src/connectors-config.ts
10017
10333
  init_cjs_shims();
10018
10334
  var import_node_os4 = __toESM(require("os"), 1);
10019
- var import_node_path44 = __toESM(require("path"), 1);
10020
- var import_node_fs25 = require("fs");
10335
+ var import_node_path45 = __toESM(require("path"), 1);
10336
+ var import_node_fs26 = require("fs");
10021
10337
  var CONNECTORS_CONFIG_VERSION = 1;
10022
10338
  var EnvRefUnsetError = class extends Error {
10023
10339
  ref;
@@ -10031,17 +10347,17 @@ var EnvRefUnsetError = class extends Error {
10031
10347
  };
10032
10348
  function neatHome2() {
10033
10349
  const override = process.env.NEAT_HOME;
10034
- if (override && override.length > 0) return import_node_path44.default.resolve(override);
10035
- return import_node_path44.default.join(import_node_os4.default.homedir(), ".neat");
10350
+ if (override && override.length > 0) return import_node_path45.default.resolve(override);
10351
+ return import_node_path45.default.join(import_node_os4.default.homedir(), ".neat");
10036
10352
  }
10037
10353
  function connectorsConfigPath(home = neatHome2()) {
10038
- return import_node_path44.default.join(home, "connectors.json");
10354
+ return import_node_path45.default.join(home, "connectors.json");
10039
10355
  }
10040
10356
  var MODE_MASK_LOOSER_THAN_0600 = 63;
10041
10357
  async function warnIfModeLooserThan0600(file) {
10042
10358
  if (process.platform === "win32") return;
10043
10359
  try {
10044
- const stat = await import_node_fs25.promises.stat(file);
10360
+ const stat = await import_node_fs26.promises.stat(file);
10045
10361
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
10046
10362
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
10047
10363
  console.warn(
@@ -10055,7 +10371,7 @@ async function readConnectorsConfig(home = neatHome2()) {
10055
10371
  const file = connectorsConfigPath(home);
10056
10372
  let raw;
10057
10373
  try {
10058
- raw = await import_node_fs25.promises.readFile(file, "utf8");
10374
+ raw = await import_node_fs26.promises.readFile(file, "utf8");
10059
10375
  } catch (err) {
10060
10376
  if (err.code === "ENOENT") {
10061
10377
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -10277,12 +10593,16 @@ var PROVIDER_DISPATCH = {
10277
10593
  provider: "cloudflare",
10278
10594
  primaryCredentialKey: "apiToken",
10279
10595
  requiredCredentialFields: ["apiToken"],
10280
- requiredOptionFields: ["accountId", "workers"],
10281
- build(_graph, options) {
10596
+ // `workers` dropped as a required field (ADR-133) — the mapping is now
10597
+ // derived from the extracted graph's platform tag; an `options.workers`
10598
+ // entry still works as an explicit override
10599
+ // (CloudflareConnectorConfig.workers).
10600
+ requiredOptionFields: ["accountId"],
10601
+ build(graph, options) {
10282
10602
  const config = options;
10283
10603
  return {
10284
10604
  connector: new CloudflareConnector(config),
10285
- resolveTarget: createCloudflareResolveTarget(config)
10605
+ resolveTarget: createCloudflareResolveTarget(config, graph)
10286
10606
  };
10287
10607
  },
10288
10608
  // GET /user/tokens/verify — Cloudflare's own purpose-built "is this API
@@ -10379,8 +10699,8 @@ init_auth();
10379
10699
 
10380
10700
  // src/unrouted.ts
10381
10701
  init_cjs_shims();
10382
- var import_node_fs26 = require("fs");
10383
- var import_node_path45 = __toESM(require("path"), 1);
10702
+ var import_node_fs27 = require("fs");
10703
+ var import_node_path46 = __toESM(require("path"), 1);
10384
10704
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
10385
10705
  return {
10386
10706
  timestamp: now.toISOString(),
@@ -10390,34 +10710,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
10390
10710
  };
10391
10711
  }
10392
10712
  async function appendUnroutedSpan(neatHome4, record) {
10393
- const target = import_node_path45.default.join(neatHome4, "errors.ndjson");
10394
- await import_node_fs26.promises.mkdir(neatHome4, { recursive: true });
10395
- await import_node_fs26.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
10713
+ const target = import_node_path46.default.join(neatHome4, "errors.ndjson");
10714
+ await import_node_fs27.promises.mkdir(neatHome4, { recursive: true });
10715
+ await import_node_fs27.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
10396
10716
  }
10397
10717
  function unroutedErrorsPath(neatHome4) {
10398
- return import_node_path45.default.join(neatHome4, "errors.ndjson");
10718
+ return import_node_path46.default.join(neatHome4, "errors.ndjson");
10399
10719
  }
10400
10720
 
10401
10721
  // src/daemon.ts
10402
- var import_types41 = require("@neat.is/types");
10722
+ var import_types42 = require("@neat.is/types");
10403
10723
  function daemonJsonPath(scanPath) {
10404
- return import_node_path46.default.join(scanPath, "neat-out", "daemon.json");
10724
+ return import_node_path47.default.join(scanPath, "neat-out", "daemon.json");
10405
10725
  }
10406
10726
  function daemonsDiscoveryDir(home) {
10407
10727
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
10408
- return import_node_path46.default.join(base, "daemons");
10728
+ return import_node_path47.default.join(base, "daemons");
10409
10729
  }
10410
10730
  function daemonDiscoveryPath(project, home) {
10411
- return import_node_path46.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
10731
+ return import_node_path47.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
10412
10732
  }
10413
10733
  function sanitizeDiscoveryName(project) {
10414
10734
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
10415
10735
  }
10416
10736
  function neatHomeFromEnv() {
10417
10737
  const env = process.env.NEAT_HOME;
10418
- if (env && env.length > 0) return import_node_path46.default.resolve(env);
10738
+ if (env && env.length > 0) return import_node_path47.default.resolve(env);
10419
10739
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
10420
- return import_node_path46.default.join(home, ".neat");
10740
+ return import_node_path47.default.join(home, ".neat");
10421
10741
  }
10422
10742
  function resolveNeatVersion() {
10423
10743
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -10449,7 +10769,7 @@ async function clearDaemonRecord(record, home) {
10449
10769
  } catch {
10450
10770
  }
10451
10771
  try {
10452
- await import_node_fs27.promises.unlink(daemonDiscoveryPath(record.project, home));
10772
+ await import_node_fs28.promises.unlink(daemonDiscoveryPath(record.project, home));
10453
10773
  } catch {
10454
10774
  }
10455
10775
  }
@@ -10458,12 +10778,12 @@ function reconcileDaemonRecordSync(record, home) {
10458
10778
  const stopped = { ...record, status: "stopped" };
10459
10779
  const target = daemonJsonPath(record.projectPath);
10460
10780
  const tmp = `${target}.${process.pid}.tmp`;
10461
- (0, import_node_fs27.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
10462
- (0, import_node_fs27.renameSync)(tmp, target);
10781
+ (0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
10782
+ (0, import_node_fs28.renameSync)(tmp, target);
10463
10783
  } catch {
10464
10784
  }
10465
10785
  try {
10466
- (0, import_node_fs27.unlinkSync)(daemonDiscoveryPath(record.project, home));
10786
+ (0, import_node_fs28.unlinkSync)(daemonDiscoveryPath(record.project, home));
10467
10787
  } catch {
10468
10788
  }
10469
10789
  }
@@ -10486,11 +10806,11 @@ function teardownSlot(slot) {
10486
10806
  }
10487
10807
  }
10488
10808
  function neatHomeFor(opts) {
10489
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path46.default.resolve(opts.neatHome);
10809
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path47.default.resolve(opts.neatHome);
10490
10810
  const env = process.env.NEAT_HOME;
10491
- if (env && env.length > 0) return import_node_path46.default.resolve(env);
10811
+ if (env && env.length > 0) return import_node_path47.default.resolve(env);
10492
10812
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
10493
- return import_node_path46.default.join(home, ".neat");
10813
+ return import_node_path47.default.join(home, ".neat");
10494
10814
  }
10495
10815
  function routeSpanToProject(serviceName, projects) {
10496
10816
  if (!serviceName) return DEFAULT_PROJECT;
@@ -10534,13 +10854,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
10534
10854
  if (!serviceName) return true;
10535
10855
  if (serviceNameMatchesProject(serviceName, project)) return true;
10536
10856
  return graph.someNode(
10537
- (_id, attrs) => attrs.type === import_types41.NodeType.ServiceNode && attrs.name === serviceName
10857
+ (_id, attrs) => attrs.type === import_types42.NodeType.ServiceNode && attrs.name === serviceName
10538
10858
  );
10539
10859
  }
10540
10860
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
10541
- const paths = pathsForProject(entry2.name, import_node_path46.default.join(entry2.path, "neat-out"));
10861
+ const paths = pathsForProject(entry2.name, import_node_path47.default.join(entry2.path, "neat-out"));
10542
10862
  try {
10543
- const stat = await import_node_fs27.promises.stat(entry2.path);
10863
+ const stat = await import_node_fs28.promises.stat(entry2.path);
10544
10864
  if (!stat.isDirectory()) {
10545
10865
  throw new Error(`registered path ${entry2.path} is not a directory`);
10546
10866
  }
@@ -10665,7 +10985,7 @@ async function startDaemon(opts = {}) {
10665
10985
  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;
10666
10986
  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;
10667
10987
  const singleProject = projectArg;
10668
- const singleProjectPath = singleProject && projectPathArg ? import_node_path46.default.resolve(projectPathArg) : null;
10988
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path47.default.resolve(projectPathArg) : null;
10669
10989
  if (singleProject && !singleProjectPath) {
10670
10990
  throw new Error(
10671
10991
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -10673,14 +10993,14 @@ async function startDaemon(opts = {}) {
10673
10993
  }
10674
10994
  if (!singleProject) {
10675
10995
  try {
10676
- await import_node_fs27.promises.access(regPath);
10996
+ await import_node_fs28.promises.access(regPath);
10677
10997
  } catch {
10678
10998
  throw new Error(
10679
10999
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
10680
11000
  );
10681
11001
  }
10682
11002
  }
10683
- const pidPath = import_node_path46.default.join(home, "neatd.pid");
11003
+ const pidPath = import_node_path47.default.join(home, "neatd.pid");
10684
11004
  await writeAtomically(pidPath, `${process.pid}
10685
11005
  `);
10686
11006
  const slots = /* @__PURE__ */ new Map();
@@ -10874,7 +11194,7 @@ async function startDaemon(opts = {}) {
10874
11194
  }
10875
11195
  if (restApp) await restApp.close().catch(() => {
10876
11196
  });
10877
- await import_node_fs27.promises.unlink(pidPath).catch(() => {
11197
+ await import_node_fs28.promises.unlink(pidPath).catch(() => {
10878
11198
  });
10879
11199
  throw new Error(
10880
11200
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -11000,7 +11320,7 @@ async function startDaemon(opts = {}) {
11000
11320
  });
11001
11321
  if (otlpApp) await otlpApp.close().catch(() => {
11002
11322
  });
11003
- await import_node_fs27.promises.unlink(pidPath).catch(() => {
11323
+ await import_node_fs28.promises.unlink(pidPath).catch(() => {
11004
11324
  });
11005
11325
  throw new Error(
11006
11326
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -11035,7 +11355,7 @@ async function startDaemon(opts = {}) {
11035
11355
  });
11036
11356
  if (otlpApp) await otlpApp.close().catch(() => {
11037
11357
  });
11038
- await import_node_fs27.promises.unlink(pidPath).catch(() => {
11358
+ await import_node_fs28.promises.unlink(pidPath).catch(() => {
11039
11359
  });
11040
11360
  throw new Error(
11041
11361
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -11082,9 +11402,9 @@ async function startDaemon(opts = {}) {
11082
11402
  let registryWatcher = null;
11083
11403
  let reloadTimer = null;
11084
11404
  if (!singleProject) try {
11085
- const regDir = import_node_path46.default.dirname(regPath);
11086
- const regBase = import_node_path46.default.basename(regPath);
11087
- registryWatcher = (0, import_node_fs27.watch)(regDir, (_eventType, filename) => {
11405
+ const regDir = import_node_path47.default.dirname(regPath);
11406
+ const regBase = import_node_path47.default.basename(regPath);
11407
+ registryWatcher = (0, import_node_fs28.watch)(regDir, (_eventType, filename) => {
11088
11408
  if (filename !== null && filename !== regBase) return;
11089
11409
  if (reloadTimer) clearTimeout(reloadTimer);
11090
11410
  reloadTimer = setTimeout(() => {
@@ -11133,7 +11453,7 @@ async function startDaemon(opts = {}) {
11133
11453
  if (daemonRecord) {
11134
11454
  await clearDaemonRecord(daemonRecord, home);
11135
11455
  }
11136
- await import_node_fs27.promises.unlink(pidPath).catch(() => {
11456
+ await import_node_fs28.promises.unlink(pidPath).catch(() => {
11137
11457
  });
11138
11458
  };
11139
11459
  return {
@@ -11156,9 +11476,9 @@ init_auth();
11156
11476
  // src/web-spawn.ts
11157
11477
  init_cjs_shims();
11158
11478
  var import_node_child_process2 = require("child_process");
11159
- var import_node_fs28 = require("fs");
11479
+ var import_node_fs29 = require("fs");
11160
11480
  var import_node_net = __toESM(require("net"), 1);
11161
- var import_node_path47 = __toESM(require("path"), 1);
11481
+ var import_node_path48 = __toESM(require("path"), 1);
11162
11482
  var DEFAULT_WEB_PORT = 6328;
11163
11483
  var DEFAULT_REST_PORT = 8080;
11164
11484
  function asValidPort(value) {
@@ -11167,11 +11487,11 @@ function asValidPort(value) {
11167
11487
  }
11168
11488
  function projectRoot() {
11169
11489
  const fromEnv = process.env.NEAT_SCAN_PATH;
11170
- return import_node_path47.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
11490
+ return import_node_path48.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
11171
11491
  }
11172
11492
  async function readDaemonPorts(root) {
11173
11493
  try {
11174
- const raw = await import_node_fs28.promises.readFile(import_node_path47.default.join(root, "neat-out", "daemon.json"), "utf8");
11494
+ const raw = await import_node_fs29.promises.readFile(import_node_path48.default.join(root, "neat-out", "daemon.json"), "utf8");
11175
11495
  const parsed = JSON.parse(raw);
11176
11496
  const ports = parsed?.ports ?? {};
11177
11497
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -11216,10 +11536,10 @@ function resolveWebPackageDir() {
11216
11536
  eval("require")
11217
11537
  );
11218
11538
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
11219
- return import_node_path47.default.dirname(pkgJsonPath);
11539
+ return import_node_path48.default.dirname(pkgJsonPath);
11220
11540
  }
11221
11541
  function resolveStandaloneServerEntry(webDir) {
11222
- return import_node_path47.default.join(webDir, ".next/standalone/packages/web/server.js");
11542
+ return import_node_path48.default.join(webDir, ".next/standalone/packages/web/server.js");
11223
11543
  }
11224
11544
  async function pickInternalPort() {
11225
11545
  return new Promise((resolve, reject) => {
@@ -11272,7 +11592,7 @@ async function spawnWebUI(restPort, opts = {}) {
11272
11592
  NEAT_API_URL: apiUrl
11273
11593
  };
11274
11594
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
11275
- cwd: import_node_path47.default.dirname(serverEntry),
11595
+ cwd: import_node_path48.default.dirname(serverEntry),
11276
11596
  env,
11277
11597
  stdio: ["ignore", "inherit", "inherit"],
11278
11598
  detached: false
@@ -11448,14 +11768,14 @@ function localVersion() {
11448
11768
  }
11449
11769
  function neatHome3() {
11450
11770
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
11451
- return import_node_path48.default.resolve(process.env.NEAT_HOME);
11771
+ return import_node_path49.default.resolve(process.env.NEAT_HOME);
11452
11772
  }
11453
11773
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11454
- return import_node_path48.default.join(home, ".neat");
11774
+ return import_node_path49.default.join(home, ".neat");
11455
11775
  }
11456
11776
  async function readPid() {
11457
11777
  try {
11458
- const raw = await import_node_fs29.promises.readFile(import_node_path48.default.join(neatHome3(), "neatd.pid"), "utf8");
11778
+ const raw = await import_node_fs30.promises.readFile(import_node_path49.default.join(neatHome3(), "neatd.pid"), "utf8");
11459
11779
  const n = Number.parseInt(raw.trim(), 10);
11460
11780
  return Number.isFinite(n) ? n : null;
11461
11781
  } catch {