@neat.is/core 0.10.1 → 0.10.3-dev.20260930

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 path79 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path79) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path79)) {
63
+ const path80 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path80) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path80)) {
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_path53.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
- return import_node_path53.default.resolve(here, "..", "proto");
196
+ const here = import_node_path54.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
+ return import_node_path54.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_path53, import_node_crypto2, grpc, protoLoader;
265
+ var import_node_url, import_node_path54, 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_path53 = __toESM(require("path"), 1);
271
+ import_node_path54 = __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);
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
414
414
  const v = attrs[key];
415
415
  if (typeof v === "string" && v.length > 0) {
416
416
  const q = v.indexOf("?");
417
- const path79 = q === -1 ? v : v.slice(0, q);
418
- if (path79.length > 0) return path79;
417
+ const path80 = q === -1 ? v : v.slice(0, q);
418
+ if (path80.length > 0) return path80;
419
419
  }
420
420
  }
421
421
  return void 0;
@@ -477,10 +477,10 @@ function parseOtlpRequest(body) {
477
477
  return out;
478
478
  }
479
479
  function loadProtoRoot() {
480
- const here = import_node_path54.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
- const protoRoot = import_node_path54.default.resolve(here, "..", "proto");
480
+ const here = import_node_path55.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
+ const protoRoot = import_node_path55.default.resolve(here, "..", "proto");
482
482
  const root = new import_protobufjs.default.Root();
483
- root.resolvePath = (_origin, target) => import_node_path54.default.resolve(protoRoot, target);
483
+ root.resolvePath = (_origin, target) => import_node_path55.default.resolve(protoRoot, target);
484
484
  root.loadSync(
485
485
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
486
486
  { keepCase: true }
@@ -766,12 +766,12 @@ function logSpanHandler(span) {
766
766
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
767
767
  );
768
768
  }
769
- var import_node_path54, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
769
+ var import_node_path55, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
770
770
  var init_otel = __esm({
771
771
  "src/otel.ts"() {
772
772
  "use strict";
773
773
  init_cjs_shims();
774
- import_node_path54 = __toESM(require("path"), 1);
774
+ import_node_path55 = __toESM(require("path"), 1);
775
775
  import_node_url2 = require("url");
776
776
  import_node_zlib = __toESM(require("zlib"), 1);
777
777
  import_fastify = __toESM(require("fastify"), 1);
@@ -868,8 +868,8 @@ init_cjs_shims();
868
868
 
869
869
  // src/ingest.ts
870
870
  init_cjs_shims();
871
- var import_node_fs7 = require("fs");
872
- var import_node_path8 = __toESM(require("path"), 1);
871
+ var import_node_fs8 = require("fs");
872
+ var import_node_path9 = __toESM(require("path"), 1);
873
873
  var sourceMapJs = __toESM(require("source-map-js"), 1);
874
874
 
875
875
  // src/policy.ts
@@ -1374,19 +1374,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1374
1374
  function longestIncomingWalk(graph, start, maxDepth) {
1375
1375
  let best = { path: [start], edges: [] };
1376
1376
  const visited = /* @__PURE__ */ new Set([start]);
1377
- function step(node, path79, edges) {
1378
- if (path79.length > best.path.length) {
1379
- best = { path: [...path79], edges: [...edges] };
1377
+ function step(node, path80, edges) {
1378
+ if (path80.length > best.path.length) {
1379
+ best = { path: [...path80], edges: [...edges] };
1380
1380
  }
1381
- if (path79.length - 1 >= maxDepth) return;
1381
+ if (path80.length - 1 >= maxDepth) return;
1382
1382
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1383
1383
  for (const [srcId, edge] of incoming) {
1384
1384
  if (visited.has(srcId)) continue;
1385
1385
  visited.add(srcId);
1386
- path79.push(srcId);
1386
+ path80.push(srcId);
1387
1387
  edges.push(edge);
1388
- step(srcId, path79, edges);
1389
- path79.pop();
1388
+ step(srcId, path80, edges);
1389
+ path80.pop();
1390
1390
  edges.pop();
1391
1391
  visited.delete(srcId);
1392
1392
  }
@@ -1601,20 +1601,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
1601
1601
  return best;
1602
1602
  }
1603
1603
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1604
- const path79 = [originServiceId];
1604
+ const path80 = [originServiceId];
1605
1605
  const edges = [];
1606
1606
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1607
1607
  let current = originServiceId;
1608
1608
  for (let depth = 0; depth < maxDepth; depth++) {
1609
1609
  const hop = dominantFailingCall(graph, current, visited);
1610
1610
  if (!hop) break;
1611
- path79.push(hop.nextService);
1611
+ path80.push(hop.nextService);
1612
1612
  edges.push(hop.edge);
1613
1613
  visited.add(hop.nextService);
1614
1614
  current = hop.nextService;
1615
1615
  }
1616
1616
  if (edges.length === 0) return null;
1617
- return { path: path79, edges, culprit: current };
1617
+ return { path: path80, edges, culprit: current };
1618
1618
  }
1619
1619
  function isStaleCallEdge(e) {
1620
1620
  return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
@@ -1650,26 +1650,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
1650
1650
  return best;
1651
1651
  }
1652
1652
  function followStaleCallChain(graph, originServiceId, maxDepth) {
1653
- const path79 = [originServiceId];
1653
+ const path80 = [originServiceId];
1654
1654
  const edges = [];
1655
1655
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1656
1656
  let current = originServiceId;
1657
1657
  for (let depth = 0; depth < maxDepth; depth++) {
1658
1658
  const hop = dominantStaleCall(graph, current, visited);
1659
1659
  if (!hop) break;
1660
- path79.push(hop.nextService);
1660
+ path80.push(hop.nextService);
1661
1661
  edges.push(hop.edge);
1662
1662
  visited.add(hop.nextService);
1663
1663
  current = hop.nextService;
1664
1664
  }
1665
1665
  if (edges.length === 0) return null;
1666
- return { path: path79, edges, culprit: current };
1666
+ return { path: path80, edges, culprit: current };
1667
1667
  }
1668
1668
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1669
1669
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1670
1670
  if (!chain) return null;
1671
1671
  const culprit = chain.culprit;
1672
- const path79 = [...chain.path];
1672
+ const path80 = [...chain.path];
1673
1673
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1674
1674
  const baseConfidence = confidenceFromMix(chain.edges);
1675
1675
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1677,14 +1677,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1677
1677
  if (loc) {
1678
1678
  let rootCauseNode = culprit;
1679
1679
  if (loc.fileNode) {
1680
- path79.push(loc.fileNode);
1680
+ path80.push(loc.fileNode);
1681
1681
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1682
1682
  rootCauseNode = loc.fileNode;
1683
1683
  }
1684
1684
  return import_types.RootCauseResultSchema.parse({
1685
1685
  rootCauseNode,
1686
1686
  rootCauseReason: loc.rootCauseReason,
1687
- traversalPath: path79,
1687
+ traversalPath: path80,
1688
1688
  edgeProvenances,
1689
1689
  confidence,
1690
1690
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1696,7 +1696,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1696
1696
  return import_types.RootCauseResultSchema.parse({
1697
1697
  rootCauseNode: culprit,
1698
1698
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1699
- traversalPath: path79,
1699
+ traversalPath: path80,
1700
1700
  edgeProvenances,
1701
1701
  confidence,
1702
1702
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2349,10 +2349,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2349
2349
  traversalPath = staleChain.path;
2350
2350
  edgeProvenances = staleChain.edges.map((e) => e.provenance);
2351
2351
  } else if (top.node !== seedNode) {
2352
- const path79 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2353
- if (path79) {
2354
- traversalPath = path79.nodes;
2355
- edgeProvenances = path79.edges.map((e) => e.provenance);
2352
+ const path80 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2353
+ if (path80) {
2354
+ traversalPath = path80.nodes;
2355
+ edgeProvenances = path80.edges.map((e) => e.provenance);
2356
2356
  } else {
2357
2357
  traversalPath = [errorNodeId, top.node];
2358
2358
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2891,7 +2891,7 @@ var import_types8 = require("@neat.is/types");
2891
2891
 
2892
2892
  // src/extract/routes.ts
2893
2893
  init_cjs_shims();
2894
- var import_node_path7 = __toESM(require("path"), 1);
2894
+ var import_node_path8 = __toESM(require("path"), 1);
2895
2895
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
2896
2896
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
2897
2897
  var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
@@ -2917,6 +2917,17 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
2917
2917
  "dist",
2918
2918
  "build",
2919
2919
  ".next",
2920
+ // `_next` is the *emitted* half of the pair above — `next build`'s output as
2921
+ // it is served or exported. `.next` was listed and `_next` wasn't, so an
2922
+ // exported bundle walked in as first-party source: minified chunks, one file
2923
+ // per line, hundreds of KB each (#1254).
2924
+ "_next",
2925
+ // Agent scratch space. `.claude/worktrees/` holds a full checkout per agent
2926
+ // session, so walking it re-extracts the whole repo once per worktree — and
2927
+ // pulls in whatever build output those checkouts happen to carry. It is
2928
+ // gitignored in practice, but the file walk doesn't read .gitignore, so the
2929
+ // name has to be listed here.
2930
+ ".claude",
2920
2931
  // Python virtualenv shapes (issue #344). Walking into a venv pulls in the
2921
2932
  // entire CPython stdlib + every installed package as if it were first-party
2922
2933
  // service code — 20k+ files, none of which the user wrote. The shape names
@@ -3148,6 +3159,15 @@ function noteExtractedDropped(edge) {
3148
3159
  function drainDroppedExtracted() {
3149
3160
  return droppedSink.splice(0, droppedSink.length);
3150
3161
  }
3162
+ var skippedSink = /* @__PURE__ */ new Map();
3163
+ function noteSkippedFile(file) {
3164
+ if (!skippedSink.has(file.path)) skippedSink.set(file.path, file);
3165
+ }
3166
+ function drainSkippedFiles() {
3167
+ const out = [...skippedSink.values()];
3168
+ skippedSink.clear();
3169
+ return out;
3170
+ }
3151
3171
  function isRejectedLogEnabled() {
3152
3172
  const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
3153
3173
  return raw === "1" || raw === "true";
@@ -3161,14 +3181,91 @@ async function writeRejectedExtracted(drops, rejectedPath) {
3161
3181
 
3162
3182
  // src/extract/calls/shared.ts
3163
3183
  init_cjs_shims();
3184
+ var import_node_fs6 = require("fs");
3185
+ var import_node_path6 = __toESM(require("path"), 1);
3186
+ var import_types4 = require("@neat.is/types");
3187
+
3188
+ // src/extract/gitignore.ts
3189
+ init_cjs_shims();
3164
3190
  var import_node_fs5 = require("fs");
3165
3191
  var import_node_path5 = __toESM(require("path"), 1);
3166
- var import_types4 = require("@neat.is/types");
3192
+ var import_ignore = __toESM(require("ignore"), 1);
3193
+ var LAYER_CACHE = /* @__PURE__ */ new Map();
3194
+ async function layerFor(dir) {
3195
+ const cached = LAYER_CACHE.get(dir);
3196
+ if (cached !== void 0) return cached;
3197
+ let layer = null;
3198
+ try {
3199
+ const raw = await import_node_fs5.promises.readFile(import_node_path5.default.join(dir, ".gitignore"), "utf8");
3200
+ layer = { dir, ig: (0, import_ignore.default)().add(raw) };
3201
+ } catch {
3202
+ layer = null;
3203
+ }
3204
+ LAYER_CACHE.set(dir, layer);
3205
+ return layer;
3206
+ }
3207
+ async function isRepoRoot(dir) {
3208
+ try {
3209
+ await import_node_fs5.promises.stat(import_node_path5.default.join(dir, ".git"));
3210
+ return true;
3211
+ } catch {
3212
+ return false;
3213
+ }
3214
+ }
3215
+ async function loadIgnoreChain(startDir) {
3216
+ const dirs = [];
3217
+ let current = import_node_path5.default.resolve(startDir);
3218
+ for (; ; ) {
3219
+ dirs.push(current);
3220
+ if (await isRepoRoot(current)) break;
3221
+ const parent = import_node_path5.default.dirname(current);
3222
+ if (parent === current) break;
3223
+ current = parent;
3224
+ }
3225
+ dirs.reverse();
3226
+ const layers = [];
3227
+ for (const dir of dirs) {
3228
+ const layer = await layerFor(dir);
3229
+ if (layer) layers.push(layer);
3230
+ }
3231
+ return layers;
3232
+ }
3233
+ async function extendIgnoreChain(chain, dir) {
3234
+ const layer = await layerFor(import_node_path5.default.resolve(dir));
3235
+ return layer ? [...chain, layer] : chain;
3236
+ }
3237
+ function isIgnored(chain, fullPath, isDir) {
3238
+ return decide(chain, fullPath, isDir).ignored;
3239
+ }
3240
+ function decide(chain, fullPath, isDir) {
3241
+ if (chain.length === 0) return { ignored: false, subtree: chain };
3242
+ const abs = import_node_path5.default.resolve(fullPath);
3243
+ let ignored = false;
3244
+ let unignoredAt = -1;
3245
+ for (let i = 0; i < chain.length; i++) {
3246
+ const layer = chain[i];
3247
+ const rel = import_node_path5.default.relative(layer.dir, abs);
3248
+ if (!rel || rel.startsWith("..") || import_node_path5.default.isAbsolute(rel)) continue;
3249
+ const candidate = rel.split(import_node_path5.default.sep).join("/") + (isDir ? "/" : "");
3250
+ const result = layer.ig.test(candidate);
3251
+ if (result.ignored) ignored = true;
3252
+ else if (result.unignored) {
3253
+ ignored = false;
3254
+ unignoredAt = i;
3255
+ }
3256
+ }
3257
+ return {
3258
+ ignored,
3259
+ subtree: unignoredAt >= 0 ? chain.slice(unignoredAt) : chain
3260
+ };
3261
+ }
3262
+
3263
+ // src/extract/calls/shared.ts
3167
3264
  function buildServiceHostIndex(services) {
3168
3265
  const knownHosts = /* @__PURE__ */ new Set();
3169
3266
  const hostToNodeId = /* @__PURE__ */ new Map();
3170
3267
  for (const service of services) {
3171
- const base = import_node_path5.default.basename(service.dir);
3268
+ const base = import_node_path6.default.basename(service.dir);
3172
3269
  knownHosts.add(base);
3173
3270
  knownHosts.add(service.pkg.name);
3174
3271
  hostToNodeId.set(base, service.node.id);
@@ -3177,33 +3274,62 @@ function buildServiceHostIndex(services) {
3177
3274
  return { knownHosts, hostToNodeId };
3178
3275
  }
3179
3276
  async function walkSourceFiles(dir, excludeDirs = []) {
3180
- const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
3277
+ const excluded = new Set(excludeDirs.map((d) => import_node_path6.default.resolve(d)));
3181
3278
  const out = [];
3182
- async function walk11(current) {
3183
- const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3279
+ const rootChain = await loadIgnoreChain(dir);
3280
+ async function walk11(current, inherited) {
3281
+ const entries = await import_node_fs6.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3282
+ const chain = entries.some((e) => e.isFile() && e.name === ".gitignore") ? await extendIgnoreChain(inherited, current) : inherited;
3184
3283
  for (const entry of entries) {
3185
- const full = import_node_path5.default.join(current, entry.name);
3284
+ const full = import_node_path6.default.join(current, entry.name);
3186
3285
  if (entry.isDirectory()) {
3187
3286
  if (IGNORED_DIRS.has(entry.name)) continue;
3188
- if (excluded.has(import_node_path5.default.resolve(full))) continue;
3287
+ if (excluded.has(import_node_path6.default.resolve(full))) continue;
3288
+ const verdict = decide(chain, full, true);
3289
+ if (verdict.ignored) continue;
3189
3290
  if (await isPythonVenvDir(full)) continue;
3190
- await walk11(full);
3191
- } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
3291
+ await walk11(full, verdict.subtree);
3292
+ } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path6.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
3192
3293
  // would attribute our instrumentation imports to the user's service.
3193
- !isNeatAuthoredSourceFile(entry.name)) {
3294
+ !isNeatAuthoredSourceFile(entry.name) && !isIgnored(chain, full, false)) {
3194
3295
  out.push(full);
3195
3296
  }
3196
3297
  }
3197
3298
  }
3198
- await walk11(dir);
3299
+ await walk11(dir, rootChain.filter((l) => import_node_path6.default.resolve(l.dir) !== import_node_path6.default.resolve(dir)));
3199
3300
  return out;
3200
3301
  }
3302
+ var MINIFIED_LINE_CHARS = 5e3;
3303
+ function hasMinifiedName(filePath) {
3304
+ return /\.min\.(js|mjs|cjs|jsx|ts|tsx)$/i.test(filePath);
3305
+ }
3306
+ function isMinifiedSource(filePath, content) {
3307
+ if (hasMinifiedName(filePath)) return { minified: true, detail: "named *.min.js" };
3308
+ let longest = 0;
3309
+ let start = 0;
3310
+ for (; ; ) {
3311
+ const nl = content.indexOf("\n", start);
3312
+ const len = (nl === -1 ? content.length : nl) - start;
3313
+ if (len > longest) longest = len;
3314
+ if (nl === -1) break;
3315
+ start = nl + 1;
3316
+ }
3317
+ if (longest > MINIFIED_LINE_CHARS) {
3318
+ return { minified: true, detail: `longest line ${longest} chars` };
3319
+ }
3320
+ return { minified: false, detail: "" };
3321
+ }
3201
3322
  async function loadSourceFiles(dir, excludeDirs = []) {
3202
3323
  const paths = await walkSourceFiles(dir, excludeDirs);
3203
3324
  const out = [];
3204
3325
  for (const p of paths) {
3205
3326
  try {
3206
- const content = await import_node_fs5.promises.readFile(p, "utf8");
3327
+ const content = await import_node_fs6.promises.readFile(p, "utf8");
3328
+ const verdict = isMinifiedSource(p, content);
3329
+ if (verdict.minified) {
3330
+ noteSkippedFile({ path: p, reason: "minified", detail: verdict.detail });
3331
+ continue;
3332
+ }
3207
3333
  out.push({ path: p, content });
3208
3334
  } catch {
3209
3335
  }
@@ -3215,15 +3341,18 @@ function lineOf(text, needle) {
3215
3341
  if (idx < 0) return 1;
3216
3342
  return text.slice(0, idx).split("\n").length;
3217
3343
  }
3344
+ var SNIPPET_MAX_CHARS = 120;
3218
3345
  function snippet(text, line) {
3219
3346
  const lines = text.split("\n");
3220
- return (lines[line - 1] ?? "").trim();
3347
+ const raw = (lines[line - 1] ?? "").trim();
3348
+ if (raw.length <= SNIPPET_MAX_CHARS) return raw;
3349
+ return `${raw.slice(0, SNIPPET_MAX_CHARS)}\u2026 (+${raw.length - SNIPPET_MAX_CHARS} chars)`;
3221
3350
  }
3222
3351
  function toPosix(p) {
3223
3352
  return p.split("\\").join("/");
3224
3353
  }
3225
3354
  function languageForPath(relPath) {
3226
- switch (import_node_path5.default.extname(relPath).toLowerCase()) {
3355
+ switch (import_node_path6.default.extname(relPath).toLowerCase()) {
3227
3356
  case ".py":
3228
3357
  return "python";
3229
3358
  case ".go":
@@ -3297,8 +3426,8 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
3297
3426
 
3298
3427
  // src/extract/imports.ts
3299
3428
  init_cjs_shims();
3300
- var import_node_path6 = __toESM(require("path"), 1);
3301
- var import_node_fs6 = require("fs");
3429
+ var import_node_path7 = __toESM(require("path"), 1);
3430
+ var import_node_fs7 = require("fs");
3302
3431
  var import_tree_sitter = __toESM(require("tree-sitter"), 1);
3303
3432
  var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
3304
3433
  var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
@@ -3443,15 +3572,15 @@ function collectPyImports(node, out) {
3443
3572
  }
3444
3573
  async function fileExists(p) {
3445
3574
  try {
3446
- await import_node_fs6.promises.access(p);
3575
+ await import_node_fs7.promises.access(p);
3447
3576
  return true;
3448
3577
  } catch {
3449
3578
  return false;
3450
3579
  }
3451
3580
  }
3452
3581
  function isWithinServiceDir(candidate, serviceDir) {
3453
- const rel = import_node_path6.default.relative(serviceDir, candidate);
3454
- return rel !== "" && !rel.startsWith("..") && !import_node_path6.default.isAbsolute(rel);
3582
+ const rel = import_node_path7.default.relative(serviceDir, candidate);
3583
+ return rel !== "" && !rel.startsWith("..") && !import_node_path7.default.isAbsolute(rel);
3455
3584
  }
3456
3585
  var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
3457
3586
  var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
@@ -3459,22 +3588,22 @@ async function firstExistingCandidate(base, serviceDir) {
3459
3588
  for (const ext of JS_EXTENSIONS) {
3460
3589
  const candidate = base + ext;
3461
3590
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3462
- return toPosix(import_node_path6.default.relative(serviceDir, candidate));
3591
+ return toPosix(import_node_path7.default.relative(serviceDir, candidate));
3463
3592
  }
3464
3593
  }
3465
3594
  for (const indexFile of JS_INDEX_FILES) {
3466
- const candidate = import_node_path6.default.join(base, indexFile);
3595
+ const candidate = import_node_path7.default.join(base, indexFile);
3467
3596
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3468
- return toPosix(import_node_path6.default.relative(serviceDir, candidate));
3597
+ return toPosix(import_node_path7.default.relative(serviceDir, candidate));
3469
3598
  }
3470
3599
  }
3471
3600
  return null;
3472
3601
  }
3473
3602
  async function loadTsPathConfig(serviceDir) {
3474
- const tsconfigPath = import_node_path6.default.join(serviceDir, "tsconfig.json");
3603
+ const tsconfigPath = import_node_path7.default.join(serviceDir, "tsconfig.json");
3475
3604
  let raw;
3476
3605
  try {
3477
- raw = await import_node_fs6.promises.readFile(tsconfigPath, "utf8");
3606
+ raw = await import_node_fs7.promises.readFile(tsconfigPath, "utf8");
3478
3607
  } catch {
3479
3608
  return null;
3480
3609
  }
@@ -3483,7 +3612,7 @@ async function loadTsPathConfig(serviceDir) {
3483
3612
  const paths = parsed.compilerOptions?.paths;
3484
3613
  if (!paths || Object.keys(paths).length === 0) return null;
3485
3614
  const baseUrl = parsed.compilerOptions?.baseUrl;
3486
- return { paths, baseDir: baseUrl ? import_node_path6.default.resolve(serviceDir, baseUrl) : serviceDir };
3615
+ return { paths, baseDir: baseUrl ? import_node_path7.default.resolve(serviceDir, baseUrl) : serviceDir };
3487
3616
  } catch (err) {
3488
3617
  recordExtractionError("import alias resolution", tsconfigPath, err);
3489
3618
  return null;
@@ -3501,11 +3630,11 @@ async function resolveTsAlias(specifier, config, serviceDir) {
3501
3630
  if (suffix === null) continue;
3502
3631
  for (const target of targets) {
3503
3632
  const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
3504
- const resolvedBase = import_node_path6.default.resolve(config.baseDir, targetBase, suffix);
3633
+ const resolvedBase = import_node_path7.default.resolve(config.baseDir, targetBase, suffix);
3505
3634
  const hit = await firstExistingCandidate(resolvedBase, serviceDir);
3506
3635
  if (hit) return hit;
3507
3636
  if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists(resolvedBase)) {
3508
- return toPosix(import_node_path6.default.relative(serviceDir, resolvedBase));
3637
+ return toPosix(import_node_path7.default.relative(serviceDir, resolvedBase));
3509
3638
  }
3510
3639
  }
3511
3640
  }
@@ -3514,18 +3643,18 @@ async function resolveTsAlias(specifier, config, serviceDir) {
3514
3643
  async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3515
3644
  if (!specifier) return null;
3516
3645
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
3517
- const base = import_node_path6.default.resolve(importerDir, specifier);
3518
- const ext = import_node_path6.default.extname(specifier);
3646
+ const base = import_node_path7.default.resolve(importerDir, specifier);
3647
+ const ext = import_node_path7.default.extname(specifier);
3519
3648
  if (ext) {
3520
3649
  if (ext === ".js" || ext === ".jsx") {
3521
3650
  const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
3522
3651
  const tsSibling = base.slice(0, -ext.length) + tsExt;
3523
3652
  if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists(tsSibling)) {
3524
- return toPosix(import_node_path6.default.relative(serviceDir, tsSibling));
3653
+ return toPosix(import_node_path7.default.relative(serviceDir, tsSibling));
3525
3654
  }
3526
3655
  }
3527
3656
  if (isWithinServiceDir(base, serviceDir) && await fileExists(base)) {
3528
- return toPosix(import_node_path6.default.relative(serviceDir, base));
3657
+ return toPosix(import_node_path7.default.relative(serviceDir, base));
3529
3658
  }
3530
3659
  if (!JS_EXTENSIONS.includes(ext)) {
3531
3660
  return firstExistingCandidate(base, serviceDir);
@@ -3540,30 +3669,30 @@ async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3540
3669
  async function resolvePyImport(imp, importerPath, serviceDir) {
3541
3670
  let baseDir;
3542
3671
  if (imp.level > 0) {
3543
- baseDir = import_node_path6.default.dirname(importerPath);
3544
- for (let i = 1; i < imp.level; i++) baseDir = import_node_path6.default.dirname(baseDir);
3672
+ baseDir = import_node_path7.default.dirname(importerPath);
3673
+ for (let i = 1; i < imp.level; i++) baseDir = import_node_path7.default.dirname(baseDir);
3545
3674
  } else {
3546
3675
  baseDir = serviceDir;
3547
3676
  }
3548
- const moduleBase = imp.modulePath ? import_node_path6.default.join(baseDir, imp.modulePath.split(".").join("/")) : baseDir;
3677
+ const moduleBase = imp.modulePath ? import_node_path7.default.join(baseDir, imp.modulePath.split(".").join("/")) : baseDir;
3549
3678
  const resolved = /* @__PURE__ */ new Set();
3550
3679
  let needModuleFile = imp.names.length === 0;
3551
3680
  for (const name of imp.names) {
3552
- const submoduleFile = import_node_path6.default.join(moduleBase, `${name}.py`);
3553
- const subpackageInit = import_node_path6.default.join(moduleBase, name, "__init__.py");
3681
+ const submoduleFile = import_node_path7.default.join(moduleBase, `${name}.py`);
3682
+ const subpackageInit = import_node_path7.default.join(moduleBase, name, "__init__.py");
3554
3683
  if (isWithinServiceDir(submoduleFile, serviceDir) && await fileExists(submoduleFile)) {
3555
- resolved.add(toPosix(import_node_path6.default.relative(serviceDir, submoduleFile)));
3684
+ resolved.add(toPosix(import_node_path7.default.relative(serviceDir, submoduleFile)));
3556
3685
  } else if (isWithinServiceDir(subpackageInit, serviceDir) && await fileExists(subpackageInit)) {
3557
- resolved.add(toPosix(import_node_path6.default.relative(serviceDir, subpackageInit)));
3686
+ resolved.add(toPosix(import_node_path7.default.relative(serviceDir, subpackageInit)));
3558
3687
  } else {
3559
3688
  needModuleFile = true;
3560
3689
  }
3561
3690
  }
3562
3691
  if (needModuleFile) {
3563
- const moduleFileCandidates = imp.modulePath ? [`${moduleBase}.py`, import_node_path6.default.join(moduleBase, "__init__.py")] : [import_node_path6.default.join(moduleBase, "__init__.py")];
3692
+ const moduleFileCandidates = imp.modulePath ? [`${moduleBase}.py`, import_node_path7.default.join(moduleBase, "__init__.py")] : [import_node_path7.default.join(moduleBase, "__init__.py")];
3564
3693
  for (const candidate of moduleFileCandidates) {
3565
3694
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3566
- resolved.add(toPosix(import_node_path6.default.relative(serviceDir, candidate)));
3695
+ resolved.add(toPosix(import_node_path7.default.relative(serviceDir, candidate)));
3567
3696
  break;
3568
3697
  }
3569
3698
  }
@@ -3573,11 +3702,11 @@ async function resolvePyImport(imp, importerPath, serviceDir) {
3573
3702
  async function resolveGoImport(specifier, modulePath, serviceDir) {
3574
3703
  if (specifier !== modulePath && !specifier.startsWith(`${modulePath}/`)) return null;
3575
3704
  const suffix = specifier === modulePath ? "" : specifier.slice(modulePath.length + 1);
3576
- const dir = import_node_path6.default.join(serviceDir, suffix);
3577
- const entries = await import_node_fs6.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3578
- const candidates = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".go") && !entry.name.endsWith("_test.go")).map((entry) => import_node_path6.default.join(dir, entry.name));
3705
+ const dir = import_node_path7.default.join(serviceDir, suffix);
3706
+ const entries = await import_node_fs7.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3707
+ const candidates = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".go") && !entry.name.endsWith("_test.go")).map((entry) => import_node_path7.default.join(dir, entry.name));
3579
3708
  if (candidates.length !== 1) return null;
3580
- return toPosix(import_node_path6.default.relative(serviceDir, candidates[0]));
3709
+ return toPosix(import_node_path7.default.relative(serviceDir, candidates[0]));
3581
3710
  }
3582
3711
  function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
3583
3712
  const importeeFileId = (0, import_types5.fileId)(serviceName, importeeRelPath);
@@ -3607,12 +3736,12 @@ async function addImports(graph, services) {
3607
3736
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
3608
3737
  for (const file of files) {
3609
3738
  if (isTestPath(file.path)) continue;
3610
- const relFile = toPosix(import_node_path6.default.relative(service.dir, file.path));
3739
+ const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
3611
3740
  const importerFileId = (0, import_types5.fileId)(service.pkg.name, relFile);
3612
- const isPython = import_node_path6.default.extname(file.path) === ".py";
3613
- const isGo = import_node_path6.default.extname(file.path) === ".go";
3614
- if (import_node_path6.default.extname(file.path) === ".rb") continue;
3615
- if (import_node_path6.default.extname(file.path) === ".php") continue;
3741
+ const isPython = import_node_path7.default.extname(file.path) === ".py";
3742
+ const isGo = import_node_path7.default.extname(file.path) === ".go";
3743
+ if (import_node_path7.default.extname(file.path) === ".rb") continue;
3744
+ if (import_node_path7.default.extname(file.path) === ".php") continue;
3616
3745
  if (isGo) {
3617
3746
  let goImports = [];
3618
3747
  try {
@@ -3622,7 +3751,7 @@ async function addImports(graph, services) {
3622
3751
  recordExtractionError("import extraction", file.path, err);
3623
3752
  continue;
3624
3753
  }
3625
- const goMod = await import_node_fs6.promises.readFile(import_node_path6.default.join(service.dir, "go.mod"), "utf8").catch(() => "");
3754
+ const goMod = await import_node_fs7.promises.readFile(import_node_path7.default.join(service.dir, "go.mod"), "utf8").catch(() => "");
3626
3755
  const modulePath = goMod.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
3627
3756
  if (!modulePath) continue;
3628
3757
  for (const imp of goImports) {
@@ -3666,7 +3795,7 @@ async function addImports(graph, services) {
3666
3795
  continue;
3667
3796
  }
3668
3797
  for (const imp of jsImports) {
3669
- const resolved = await resolveJsImport(imp.specifier, import_node_path6.default.dirname(file.path), service.dir, tsPaths);
3798
+ const resolved = await resolveJsImport(imp.specifier, import_node_path7.default.dirname(file.path), service.dir, tsPaths);
3670
3799
  if (!resolved) continue;
3671
3800
  edgesAdded += emitImportEdge(
3672
3801
  graph,
@@ -3791,8 +3920,8 @@ function chiRoutesFromSource(source, parser) {
3791
3920
  chiWalk(tree.rootNode, "", out);
3792
3921
  return out;
3793
3922
  }
3794
- function stripChiRegex(path79) {
3795
- return path79.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3923
+ function stripChiRegex(path80) {
3924
+ return path80.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3796
3925
  }
3797
3926
  function chiWalk(node, prefix, out) {
3798
3927
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4132,7 +4261,7 @@ function isNextPagesApiFile(relFile) {
4132
4261
  if (pagesIdx === -1 || segs[pagesIdx + 1] !== "api") return false;
4133
4262
  const base = segs[segs.length - 1] ?? "";
4134
4263
  if (/^_(app|document|middleware)\./.test(base)) return false;
4135
- return JS_ROUTE_EXTENSIONS.has(import_node_path7.default.extname(base));
4264
+ return JS_ROUTE_EXTENSIONS.has(import_node_path8.default.extname(base));
4136
4265
  }
4137
4266
  function nextSegment(seg) {
4138
4267
  if (seg.startsWith("(") && seg.endsWith(")")) return null;
@@ -4464,9 +4593,9 @@ function rubyRocketRoute(args) {
4464
4593
  if (!pair || pair.type !== "pair") continue;
4465
4594
  const k = pair.childForFieldName("key");
4466
4595
  if (k?.type !== "string") continue;
4467
- const path79 = rubyLiteral(k);
4468
- if (path79 === null) continue;
4469
- return { path: path79, target: rubyLiteral(pair.childForFieldName("value")) };
4596
+ const path80 = rubyLiteral(k);
4597
+ if (path80 === null) continue;
4598
+ return { path: path80, target: rubyLiteral(pair.childForFieldName("value")) };
4470
4599
  }
4471
4600
  return null;
4472
4601
  }
@@ -5314,12 +5443,12 @@ async function expressMountPrefixes(files, serviceDir, tsPaths) {
5314
5443
  const jsParser = makeJsParser2();
5315
5444
  const fileInfo = /* @__PURE__ */ new Map();
5316
5445
  for (const f of files) {
5317
- if (!JS_ROUTE_EXTENSIONS.has(import_node_path7.default.extname(f.path))) continue;
5446
+ if (!JS_ROUTE_EXTENSIONS.has(import_node_path8.default.extname(f.path))) continue;
5318
5447
  if (isTestPath(f.path)) continue;
5319
- const rel = toPosix(import_node_path7.default.relative(serviceDir, f.path));
5448
+ const rel = toPosix(import_node_path8.default.relative(serviceDir, f.path));
5320
5449
  try {
5321
5450
  const tree = parseSource2(jsParser, f.content);
5322
- fileInfo.set(rel, analyzeExpressFile(tree.rootNode, import_node_path7.default.dirname(f.path)));
5451
+ fileInfo.set(rel, analyzeExpressFile(tree.rootNode, import_node_path8.default.dirname(f.path)));
5323
5452
  } catch {
5324
5453
  }
5325
5454
  }
@@ -5418,14 +5547,14 @@ async function addRoutes(graph, services) {
5418
5547
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
5419
5548
  for (const file of files) {
5420
5549
  if (isTestPath(file.path)) continue;
5421
- const ext = import_node_path7.default.extname(file.path);
5550
+ const ext = import_node_path8.default.extname(file.path);
5422
5551
  const isPy = ext === ".py";
5423
5552
  const isGo = ext === ".go";
5424
5553
  const isRb = ext === ".rb";
5425
5554
  const isPhp = ext === ".php";
5426
5555
  const isRs = ext === ".rs";
5427
5556
  if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
5428
- const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
5557
+ const relFile = toPosix(import_node_path8.default.relative(service.dir, file.path));
5429
5558
  let routes;
5430
5559
  try {
5431
5560
  if (isPhp) {
@@ -5933,7 +6062,7 @@ function languageForExt(relPath) {
5933
6062
  function relPathForRuntimeFile(filepath, serviceNode, scanPath) {
5934
6063
  let p = toPosix2(filepath).replace(/^file:\/\//, "");
5935
6064
  if (scanPath && scanPath.length > 0) {
5936
- const absRoot = toPosix2(import_node_path8.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
6065
+ const absRoot = toPosix2(import_node_path9.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
5937
6066
  const anchor = absRoot.endsWith("/") ? absRoot : `${absRoot}/`;
5938
6067
  if (p.startsWith(anchor)) return p.slice(anchor.length);
5939
6068
  }
@@ -5961,10 +6090,10 @@ function resolveDistToSrc(absFilepath, line) {
5961
6090
  entry = null;
5962
6091
  const mapPath = `${absFilepath}.map`;
5963
6092
  try {
5964
- if ((0, import_node_fs7.existsSync)(mapPath)) {
5965
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(mapPath, "utf8"));
6093
+ if ((0, import_node_fs8.existsSync)(mapPath)) {
6094
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(mapPath, "utf8"));
5966
6095
  const consumer = new sourceMapJs.SourceMapConsumer(raw);
5967
- entry = { consumer, dir: import_node_path8.default.dirname(mapPath) };
6096
+ entry = { consumer, dir: import_node_path9.default.dirname(mapPath) };
5968
6097
  }
5969
6098
  } catch {
5970
6099
  entry = null;
@@ -5984,7 +6113,7 @@ function resolveDistToSrc(absFilepath, line) {
5984
6113
  }
5985
6114
  if (!pos || !pos.source) return null;
5986
6115
  const root = entry.consumer.sourceRoot ?? "";
5987
- const resolved = import_node_path8.default.resolve(entry.dir, root, pos.source);
6116
+ const resolved = import_node_path9.default.resolve(entry.dir, root, pos.source);
5988
6117
  return { filepath: resolved, ...pos.line ? { line: pos.line } : {} };
5989
6118
  } catch {
5990
6119
  return null;
@@ -6528,8 +6657,8 @@ function emitIncidentEvent(project, ev) {
6528
6657
  });
6529
6658
  }
6530
6659
  async function appendErrorEvent(ctx, ev) {
6531
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(ctx.errorsPath), { recursive: true });
6532
- await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
6660
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(ctx.errorsPath), { recursive: true });
6661
+ await import_node_fs8.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
6533
6662
  emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
6534
6663
  }
6535
6664
  async function appendConnectorIncident(errorsPath, input, project) {
@@ -6544,8 +6673,8 @@ async function appendConnectorIncident(errorsPath, input, project) {
6544
6673
  ...input.attributes && Object.keys(input.attributes).length > 0 ? { attributes: input.attributes } : {},
6545
6674
  affectedNode: input.affectedNode
6546
6675
  };
6547
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6548
- await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6676
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(errorsPath), { recursive: true });
6677
+ await import_node_fs8.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6549
6678
  if (project) emitIncidentEvent(project, ev);
6550
6679
  }
6551
6680
  function landIncidentCallSite(span, callSite, trusted, graph) {
@@ -6667,8 +6796,8 @@ function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJ
6667
6796
  return async (span) => {
6668
6797
  const ev = buildErrorEventForReceiver(span, graph, scanPath);
6669
6798
  if (!ev) return;
6670
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6671
- await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6799
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(errorsPath), { recursive: true });
6800
+ await import_node_fs8.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6672
6801
  emitIncidentEvent(project, ev);
6673
6802
  };
6674
6803
  }
@@ -7171,13 +7300,13 @@ async function markStaleEdges(graph, options = {}) {
7171
7300
  return { count: events.length, events };
7172
7301
  }
7173
7302
  async function appendStaleEvents(staleEventsPath, events) {
7174
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(staleEventsPath), { recursive: true });
7303
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(staleEventsPath), { recursive: true });
7175
7304
  const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
7176
- await import_node_fs7.promises.appendFile(staleEventsPath, lines, "utf8");
7305
+ await import_node_fs8.promises.appendFile(staleEventsPath, lines, "utf8");
7177
7306
  }
7178
7307
  async function readStaleEvents(staleEventsPath) {
7179
7308
  try {
7180
- const raw = await import_node_fs7.promises.readFile(staleEventsPath, "utf8");
7309
+ const raw = await import_node_fs8.promises.readFile(staleEventsPath, "utf8");
7181
7310
  return raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
7182
7311
  } catch (err) {
7183
7312
  if (err.code === "ENOENT") return [];
@@ -7213,7 +7342,7 @@ function startStalenessLoop(graph, options = {}) {
7213
7342
  var INCIDENT_READ_MAX_BYTES = 32 * 1024 * 1024;
7214
7343
  var INCIDENT_READ_MAX_EVENTS = 5e3;
7215
7344
  async function readErrorFileTail(errorsPath, maxBytes) {
7216
- const handle = await import_node_fs7.promises.open(errorsPath, "r");
7345
+ const handle = await import_node_fs8.promises.open(errorsPath, "r");
7217
7346
  try {
7218
7347
  const { size } = await handle.stat();
7219
7348
  if (size <= maxBytes) {
@@ -7338,16 +7467,16 @@ function mergeSnapshot(graph, snapshot) {
7338
7467
 
7339
7468
  // src/extract/services.ts
7340
7469
  init_cjs_shims();
7341
- var import_node_fs18 = require("fs");
7342
- var import_node_path20 = __toESM(require("path"), 1);
7343
- var import_ignore = __toESM(require("ignore"), 1);
7470
+ var import_node_fs19 = require("fs");
7471
+ var import_node_path21 = __toESM(require("path"), 1);
7472
+ var import_ignore2 = __toESM(require("ignore"), 1);
7344
7473
  var import_minimatch2 = require("minimatch");
7345
7474
  var import_types18 = require("@neat.is/types");
7346
7475
 
7347
7476
  // src/extract/python.ts
7348
7477
  init_cjs_shims();
7349
- var import_node_fs8 = require("fs");
7350
- var import_node_path9 = __toESM(require("path"), 1);
7478
+ var import_node_fs9 = require("fs");
7479
+ var import_node_path10 = __toESM(require("path"), 1);
7351
7480
  var import_smol_toml = require("smol-toml");
7352
7481
  var REQUIREMENT_LINE = /^\s*([A-Za-z0-9_.-]+)(?:\[[^\]]*\])?\s*(?:(==)\s*([A-Za-z0-9_.+-]+))?/;
7353
7482
  function parseRequirementsTxt(content) {
@@ -7380,25 +7509,25 @@ function depsFromPyProject(pyproject) {
7380
7509
  return out;
7381
7510
  }
7382
7511
  async function discoverPythonService(serviceDir) {
7383
- const pyprojectPath = import_node_path9.default.join(serviceDir, "pyproject.toml");
7384
- const requirementsPath = import_node_path9.default.join(serviceDir, "requirements.txt");
7385
- const setupPath = import_node_path9.default.join(serviceDir, "setup.py");
7512
+ const pyprojectPath = import_node_path10.default.join(serviceDir, "pyproject.toml");
7513
+ const requirementsPath = import_node_path10.default.join(serviceDir, "requirements.txt");
7514
+ const setupPath = import_node_path10.default.join(serviceDir, "setup.py");
7386
7515
  const hasPyproject = await exists(pyprojectPath);
7387
7516
  const hasRequirements = await exists(requirementsPath);
7388
7517
  const hasSetup = await exists(setupPath);
7389
7518
  if (!hasPyproject && !hasRequirements && !hasSetup) return null;
7390
- let name = import_node_path9.default.basename(serviceDir);
7519
+ let name = import_node_path10.default.basename(serviceDir);
7391
7520
  let version;
7392
7521
  const dependencies = {};
7393
7522
  if (hasPyproject) {
7394
- const raw = await import_node_fs8.promises.readFile(pyprojectPath, "utf8");
7523
+ const raw = await import_node_fs9.promises.readFile(pyprojectPath, "utf8");
7395
7524
  const pyproject = (0, import_smol_toml.parse)(raw);
7396
7525
  name = pyproject.project?.name ?? pyproject.tool?.poetry?.name ?? name;
7397
7526
  version = pyproject.project?.version ?? pyproject.tool?.poetry?.version ?? void 0;
7398
7527
  Object.assign(dependencies, depsFromPyProject(pyproject));
7399
7528
  }
7400
7529
  if (hasRequirements) {
7401
- const raw = await import_node_fs8.promises.readFile(requirementsPath, "utf8");
7530
+ const raw = await import_node_fs9.promises.readFile(requirementsPath, "utf8");
7402
7531
  Object.assign(dependencies, parseRequirementsTxt(raw));
7403
7532
  }
7404
7533
  return { name, version, dependencies };
@@ -7413,8 +7542,8 @@ function pythonToPackage(service) {
7413
7542
 
7414
7543
  // src/extract/go.ts
7415
7544
  init_cjs_shims();
7416
- var import_node_fs9 = require("fs");
7417
- var import_node_path10 = __toESM(require("path"), 1);
7545
+ var import_node_fs10 = require("fs");
7546
+ var import_node_path11 = __toESM(require("path"), 1);
7418
7547
  var import_types9 = require("@neat.is/types");
7419
7548
  function parseGoMod(source) {
7420
7549
  const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
@@ -7441,7 +7570,7 @@ function goFramework(deps) {
7441
7570
  async function discoverGoService(scanPath, dir) {
7442
7571
  let raw;
7443
7572
  try {
7444
- raw = await import_node_fs9.promises.readFile(import_node_path10.default.join(dir, "go.mod"), "utf8");
7573
+ raw = await import_node_fs10.promises.readFile(import_node_path11.default.join(dir, "go.mod"), "utf8");
7445
7574
  } catch {
7446
7575
  return null;
7447
7576
  }
@@ -7456,7 +7585,7 @@ async function discoverGoService(scanPath, dir) {
7456
7585
  name,
7457
7586
  language: "go",
7458
7587
  dependencies: mod.dependencies,
7459
- repoPath: import_node_path10.default.relative(scanPath, dir),
7588
+ repoPath: import_node_path11.default.relative(scanPath, dir),
7460
7589
  ...framework ? { framework } : {}
7461
7590
  };
7462
7591
  return { pkg, dir, node };
@@ -7464,8 +7593,8 @@ async function discoverGoService(scanPath, dir) {
7464
7593
 
7465
7594
  // src/extract/ruby.ts
7466
7595
  init_cjs_shims();
7467
- var import_node_fs10 = require("fs");
7468
- var import_node_path11 = __toESM(require("path"), 1);
7596
+ var import_node_fs11 = require("fs");
7597
+ var import_node_path12 = __toESM(require("path"), 1);
7469
7598
  var import_types10 = require("@neat.is/types");
7470
7599
  function parseGemfile(source) {
7471
7600
  const dependencies = {};
@@ -7482,12 +7611,12 @@ function parseGemfile(source) {
7482
7611
  async function discoverRubyService(scanPath, dir) {
7483
7612
  let raw;
7484
7613
  try {
7485
- raw = await import_node_fs10.promises.readFile(import_node_path11.default.join(dir, "Gemfile"), "utf8");
7614
+ raw = await import_node_fs11.promises.readFile(import_node_path12.default.join(dir, "Gemfile"), "utf8");
7486
7615
  } catch {
7487
7616
  return null;
7488
7617
  }
7489
7618
  const gemfile = parseGemfile(raw);
7490
- const name = import_node_path11.default.basename(dir);
7619
+ const name = import_node_path12.default.basename(dir);
7491
7620
  const pkg = { name, dependencies: gemfile.dependencies };
7492
7621
  const node = {
7493
7622
  id: (0, import_types10.serviceId)(name),
@@ -7495,7 +7624,7 @@ async function discoverRubyService(scanPath, dir) {
7495
7624
  name,
7496
7625
  language: "ruby",
7497
7626
  dependencies: gemfile.dependencies,
7498
- repoPath: import_node_path11.default.relative(scanPath, dir),
7627
+ repoPath: import_node_path12.default.relative(scanPath, dir),
7499
7628
  ...gemfile.dependencies["rails"] !== void 0 ? { framework: "rails" } : {}
7500
7629
  };
7501
7630
  return { pkg, dir, node };
@@ -7503,7 +7632,7 @@ async function discoverRubyService(scanPath, dir) {
7503
7632
 
7504
7633
  // src/extract/php.ts
7505
7634
  init_cjs_shims();
7506
- var import_node_path12 = __toESM(require("path"), 1);
7635
+ var import_node_path13 = __toESM(require("path"), 1);
7507
7636
  var import_types11 = require("@neat.is/types");
7508
7637
  function parseComposerJson(composer) {
7509
7638
  return {
@@ -7516,12 +7645,12 @@ function parseComposerJson(composer) {
7516
7645
  async function discoverPhpService(scanPath, dir) {
7517
7646
  let composer;
7518
7647
  try {
7519
- composer = await readJson(import_node_path12.default.join(dir, "composer.json"));
7648
+ composer = await readJson(import_node_path13.default.join(dir, "composer.json"));
7520
7649
  } catch {
7521
7650
  return null;
7522
7651
  }
7523
7652
  const { dependencies } = parseComposerJson(composer);
7524
- const name = import_node_path12.default.basename(dir);
7653
+ const name = import_node_path13.default.basename(dir);
7525
7654
  const pkg = { name, dependencies };
7526
7655
  const node = {
7527
7656
  id: (0, import_types11.serviceId)(name),
@@ -7529,7 +7658,7 @@ async function discoverPhpService(scanPath, dir) {
7529
7658
  name,
7530
7659
  language: "php",
7531
7660
  dependencies,
7532
- repoPath: import_node_path12.default.relative(scanPath, dir),
7661
+ repoPath: import_node_path13.default.relative(scanPath, dir),
7533
7662
  ...dependencies["laravel/framework"] !== void 0 ? { framework: "laravel" } : {}
7534
7663
  };
7535
7664
  return { pkg, dir, node };
@@ -7537,23 +7666,23 @@ async function discoverPhpService(scanPath, dir) {
7537
7666
 
7538
7667
  // src/extract/csharp.ts
7539
7668
  init_cjs_shims();
7540
- var import_node_fs11 = require("fs");
7541
- var import_node_path13 = __toESM(require("path"), 1);
7669
+ var import_node_fs12 = require("fs");
7670
+ var import_node_path14 = __toESM(require("path"), 1);
7542
7671
  var import_types12 = require("@neat.is/types");
7543
7672
  var CSHARP_PROJECT_EXTS = /* @__PURE__ */ new Set([".csproj", ".sln"]);
7544
7673
  async function projectFiles(dir) {
7545
- const entries = await import_node_fs11.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7674
+ const entries = await import_node_fs12.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7546
7675
  const out = [];
7547
7676
  for (const entry of entries) {
7548
7677
  if (!entry.isFile()) continue;
7549
- if (CSHARP_PROJECT_EXTS.has(import_node_path13.default.extname(entry.name).toLowerCase())) out.push(entry.name);
7678
+ if (CSHARP_PROJECT_EXTS.has(import_node_path14.default.extname(entry.name).toLowerCase())) out.push(entry.name);
7550
7679
  }
7551
7680
  return out.sort();
7552
7681
  }
7553
7682
  var NESTED_PROJECT_DIR = "src";
7554
7683
  async function resolveProjectDir(dir) {
7555
7684
  if ((await projectFiles(dir)).length > 0) return dir;
7556
- const nested = import_node_path13.default.join(dir, NESTED_PROJECT_DIR);
7685
+ const nested = import_node_path14.default.join(dir, NESTED_PROJECT_DIR);
7557
7686
  if ((await projectFiles(nested)).length > 0) return nested;
7558
7687
  return null;
7559
7688
  }
@@ -7578,10 +7707,10 @@ async function discoverCsharpService(scanPath, dir) {
7578
7707
  if (projectDir === null) return null;
7579
7708
  const projects = await projectFiles(projectDir);
7580
7709
  const csproj = projects.find((p) => p.toLowerCase().endsWith(".csproj"));
7581
- const name = csproj ? import_node_path13.default.basename(csproj, import_node_path13.default.extname(csproj)) : import_node_path13.default.basename(dir);
7710
+ const name = csproj ? import_node_path14.default.basename(csproj, import_node_path14.default.extname(csproj)) : import_node_path14.default.basename(dir);
7582
7711
  let dependencies = {};
7583
7712
  if (csproj) {
7584
- const raw = await import_node_fs11.promises.readFile(import_node_path13.default.join(projectDir, csproj), "utf8").catch(() => "");
7713
+ const raw = await import_node_fs12.promises.readFile(import_node_path14.default.join(projectDir, csproj), "utf8").catch(() => "");
7585
7714
  dependencies = parseCsproj(raw).dependencies;
7586
7715
  }
7587
7716
  const pkg = { name, dependencies };
@@ -7593,20 +7722,20 @@ async function discoverCsharpService(scanPath, dir) {
7593
7722
  dependencies,
7594
7723
  // Anchor on the directory that holds the project and its `.cs` files, so file
7595
7724
  // attribution reaches the source whether it sits at the root or under `src/`.
7596
- repoPath: import_node_path13.default.relative(scanPath, projectDir)
7725
+ repoPath: import_node_path14.default.relative(scanPath, projectDir)
7597
7726
  };
7598
7727
  return { pkg, dir: projectDir, node };
7599
7728
  }
7600
7729
 
7601
7730
  // src/extract/java.ts
7602
7731
  init_cjs_shims();
7603
- var import_node_fs12 = require("fs");
7604
- var import_node_path14 = __toESM(require("path"), 1);
7732
+ var import_node_fs13 = require("fs");
7733
+ var import_node_path15 = __toESM(require("path"), 1);
7605
7734
  var import_types13 = require("@neat.is/types");
7606
7735
  var JAVA_MANIFESTS = ["pom.xml", "build.gradle", "build.gradle.kts"];
7607
7736
  async function hasJavaManifest(dir) {
7608
7737
  for (const manifest of JAVA_MANIFESTS) {
7609
- if (await exists(import_node_path14.default.join(dir, manifest))) return true;
7738
+ if (await exists(import_node_path15.default.join(dir, manifest))) return true;
7610
7739
  }
7611
7740
  return false;
7612
7741
  }
@@ -7641,19 +7770,19 @@ function parseGradle(source) {
7641
7770
  return { dependencies };
7642
7771
  }
7643
7772
  async function discoverJavaService(scanPath, dir) {
7644
- const pomPath = import_node_path14.default.join(dir, "pom.xml");
7645
- const gradlePath = import_node_path14.default.join(dir, "build.gradle");
7646
- const gradleKtsPath = import_node_path14.default.join(dir, "build.gradle.kts");
7773
+ const pomPath = import_node_path15.default.join(dir, "pom.xml");
7774
+ const gradlePath = import_node_path15.default.join(dir, "build.gradle");
7775
+ const gradleKtsPath = import_node_path15.default.join(dir, "build.gradle.kts");
7647
7776
  let manifest = null;
7648
7777
  if (await exists(pomPath)) {
7649
- manifest = parsePomXml(await import_node_fs12.promises.readFile(pomPath, "utf8").catch(() => ""));
7778
+ manifest = parsePomXml(await import_node_fs13.promises.readFile(pomPath, "utf8").catch(() => ""));
7650
7779
  } else if (await exists(gradlePath)) {
7651
- manifest = parseGradle(await import_node_fs12.promises.readFile(gradlePath, "utf8").catch(() => ""));
7780
+ manifest = parseGradle(await import_node_fs13.promises.readFile(gradlePath, "utf8").catch(() => ""));
7652
7781
  } else if (await exists(gradleKtsPath)) {
7653
- manifest = parseGradle(await import_node_fs12.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7782
+ manifest = parseGradle(await import_node_fs13.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7654
7783
  }
7655
7784
  if (!manifest) return null;
7656
- const name = manifest.name?.trim() || import_node_path14.default.basename(dir);
7785
+ const name = manifest.name?.trim() || import_node_path15.default.basename(dir);
7657
7786
  const dependencies = manifest.dependencies;
7658
7787
  const pkg = { name, dependencies };
7659
7788
  const node = {
@@ -7662,15 +7791,15 @@ async function discoverJavaService(scanPath, dir) {
7662
7791
  name,
7663
7792
  language: "java",
7664
7793
  dependencies,
7665
- repoPath: import_node_path14.default.relative(scanPath, dir)
7794
+ repoPath: import_node_path15.default.relative(scanPath, dir)
7666
7795
  };
7667
7796
  return { pkg, dir, node };
7668
7797
  }
7669
7798
 
7670
7799
  // src/extract/kotlin.ts
7671
7800
  init_cjs_shims();
7672
- var import_node_fs13 = require("fs");
7673
- var import_node_path15 = __toESM(require("path"), 1);
7801
+ var import_node_fs14 = require("fs");
7802
+ var import_node_path16 = __toESM(require("path"), 1);
7674
7803
  var import_types14 = require("@neat.is/types");
7675
7804
  var KOTLIN_SOURCE_EXT = ".kt";
7676
7805
  var JAVA_SOURCE_EXT = ".java";
@@ -7680,15 +7809,15 @@ async function dominantJvmSourceLanguage(dir) {
7680
7809
  let java = 0;
7681
7810
  async function recurse(current, depth) {
7682
7811
  if (depth > SOURCE_PROBE_MAX_DEPTH) return;
7683
- const entries = await import_node_fs13.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7812
+ const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7684
7813
  for (const entry of entries) {
7685
7814
  if (entry.isDirectory()) {
7686
7815
  if (IGNORED_DIRS.has(entry.name)) continue;
7687
- await recurse(import_node_path15.default.join(current, entry.name), depth + 1);
7816
+ await recurse(import_node_path16.default.join(current, entry.name), depth + 1);
7688
7817
  continue;
7689
7818
  }
7690
7819
  if (!entry.isFile()) continue;
7691
- const ext = import_node_path15.default.extname(entry.name).toLowerCase();
7820
+ const ext = import_node_path16.default.extname(entry.name).toLowerCase();
7692
7821
  if (ext === KOTLIN_SOURCE_EXT) kt++;
7693
7822
  else if (ext === JAVA_SOURCE_EXT) java++;
7694
7823
  }
@@ -7699,17 +7828,17 @@ async function dominantJvmSourceLanguage(dir) {
7699
7828
  return void 0;
7700
7829
  }
7701
7830
  async function readJvmManifest(dir) {
7702
- const pomPath = import_node_path15.default.join(dir, JAVA_MANIFESTS[0]);
7703
- const gradlePath = import_node_path15.default.join(dir, JAVA_MANIFESTS[1]);
7704
- const gradleKtsPath = import_node_path15.default.join(dir, JAVA_MANIFESTS[2]);
7831
+ const pomPath = import_node_path16.default.join(dir, JAVA_MANIFESTS[0]);
7832
+ const gradlePath = import_node_path16.default.join(dir, JAVA_MANIFESTS[1]);
7833
+ const gradleKtsPath = import_node_path16.default.join(dir, JAVA_MANIFESTS[2]);
7705
7834
  if (await exists(pomPath)) {
7706
- return parsePomXml(await import_node_fs13.promises.readFile(pomPath, "utf8").catch(() => ""));
7835
+ return parsePomXml(await import_node_fs14.promises.readFile(pomPath, "utf8").catch(() => ""));
7707
7836
  }
7708
7837
  if (await exists(gradlePath)) {
7709
- return parseGradle(await import_node_fs13.promises.readFile(gradlePath, "utf8").catch(() => ""));
7838
+ return parseGradle(await import_node_fs14.promises.readFile(gradlePath, "utf8").catch(() => ""));
7710
7839
  }
7711
7840
  if (await exists(gradleKtsPath)) {
7712
- return parseGradle(await import_node_fs13.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7841
+ return parseGradle(await import_node_fs14.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7713
7842
  }
7714
7843
  return null;
7715
7844
  }
@@ -7718,7 +7847,7 @@ async function discoverKotlinService(scanPath, dir) {
7718
7847
  if (await dominantJvmSourceLanguage(dir) !== "kotlin") return null;
7719
7848
  const manifest = await readJvmManifest(dir);
7720
7849
  if (!manifest) return null;
7721
- const name = manifest.name?.trim() || import_node_path15.default.basename(dir);
7850
+ const name = manifest.name?.trim() || import_node_path16.default.basename(dir);
7722
7851
  const dependencies = manifest.dependencies;
7723
7852
  const pkg = { name, dependencies };
7724
7853
  const node = {
@@ -7727,20 +7856,20 @@ async function discoverKotlinService(scanPath, dir) {
7727
7856
  name,
7728
7857
  language: "kotlin",
7729
7858
  dependencies,
7730
- repoPath: import_node_path15.default.relative(scanPath, dir)
7859
+ repoPath: import_node_path16.default.relative(scanPath, dir)
7731
7860
  };
7732
7861
  return { pkg, dir, node };
7733
7862
  }
7734
7863
 
7735
7864
  // src/extract/rust.ts
7736
7865
  init_cjs_shims();
7737
- var import_node_fs14 = require("fs");
7738
- var import_node_path16 = __toESM(require("path"), 1);
7866
+ var import_node_fs15 = require("fs");
7867
+ var import_node_path17 = __toESM(require("path"), 1);
7739
7868
  var import_smol_toml2 = require("smol-toml");
7740
7869
  var import_types15 = require("@neat.is/types");
7741
7870
  var CARGO_MANIFEST = "Cargo.toml";
7742
7871
  async function hasRustManifest(dir) {
7743
- return exists(import_node_path16.default.join(dir, CARGO_MANIFEST));
7872
+ return exists(import_node_path17.default.join(dir, CARGO_MANIFEST));
7744
7873
  }
7745
7874
  function parseCargoToml(source) {
7746
7875
  const dependencies = {};
@@ -7765,10 +7894,10 @@ function parseCargoToml(source) {
7765
7894
  return { name, dependencies };
7766
7895
  }
7767
7896
  async function discoverRustService(scanPath, dir) {
7768
- const cargoPath = import_node_path16.default.join(dir, CARGO_MANIFEST);
7897
+ const cargoPath = import_node_path17.default.join(dir, CARGO_MANIFEST);
7769
7898
  if (!await exists(cargoPath)) return null;
7770
- const manifest = parseCargoToml(await import_node_fs14.promises.readFile(cargoPath, "utf8").catch(() => ""));
7771
- const name = manifest.name?.trim() || import_node_path16.default.basename(dir);
7899
+ const manifest = parseCargoToml(await import_node_fs15.promises.readFile(cargoPath, "utf8").catch(() => ""));
7900
+ const name = manifest.name?.trim() || import_node_path17.default.basename(dir);
7772
7901
  const dependencies = manifest.dependencies;
7773
7902
  const pkg = { name, dependencies };
7774
7903
  const node = {
@@ -7777,19 +7906,19 @@ async function discoverRustService(scanPath, dir) {
7777
7906
  name,
7778
7907
  language: "rust",
7779
7908
  dependencies,
7780
- repoPath: import_node_path16.default.relative(scanPath, dir)
7909
+ repoPath: import_node_path17.default.relative(scanPath, dir)
7781
7910
  };
7782
7911
  return { pkg, dir, node };
7783
7912
  }
7784
7913
 
7785
7914
  // src/extract/cpp.ts
7786
7915
  init_cjs_shims();
7787
- var import_node_fs15 = require("fs");
7788
- var import_node_path17 = __toESM(require("path"), 1);
7916
+ var import_node_fs16 = require("fs");
7917
+ var import_node_path18 = __toESM(require("path"), 1);
7789
7918
  var import_types16 = require("@neat.is/types");
7790
7919
  var CMAKE_MANIFEST = "CMakeLists.txt";
7791
7920
  async function hasCppManifest(dir) {
7792
- return exists(import_node_path17.default.join(dir, CMAKE_MANIFEST));
7921
+ return exists(import_node_path18.default.join(dir, CMAKE_MANIFEST));
7793
7922
  }
7794
7923
  function parseCMakeProjectName(source) {
7795
7924
  const withoutComments = source.replace(/#.*$/gm, "");
@@ -7797,10 +7926,10 @@ function parseCMakeProjectName(source) {
7797
7926
  return m?.[1];
7798
7927
  }
7799
7928
  async function discoverCppService(scanPath, dir) {
7800
- const cmakePath = import_node_path17.default.join(dir, CMAKE_MANIFEST);
7929
+ const cmakePath = import_node_path18.default.join(dir, CMAKE_MANIFEST);
7801
7930
  if (!await exists(cmakePath)) return null;
7802
- const source = await import_node_fs15.promises.readFile(cmakePath, "utf8").catch(() => "");
7803
- const name = parseCMakeProjectName(source)?.trim() || import_node_path17.default.basename(dir);
7931
+ const source = await import_node_fs16.promises.readFile(cmakePath, "utf8").catch(() => "");
7932
+ const name = parseCMakeProjectName(source)?.trim() || import_node_path18.default.basename(dir);
7804
7933
  const dependencies = {};
7805
7934
  const pkg = { name, dependencies };
7806
7935
  const node = {
@@ -7809,15 +7938,15 @@ async function discoverCppService(scanPath, dir) {
7809
7938
  name,
7810
7939
  language: "cpp",
7811
7940
  dependencies,
7812
- repoPath: import_node_path17.default.relative(scanPath, dir)
7941
+ repoPath: import_node_path18.default.relative(scanPath, dir)
7813
7942
  };
7814
7943
  return { pkg, dir, node };
7815
7944
  }
7816
7945
 
7817
7946
  // src/extract/dockerfile-service.ts
7818
7947
  init_cjs_shims();
7819
- var import_node_fs16 = require("fs");
7820
- var import_node_path18 = __toESM(require("path"), 1);
7948
+ var import_node_fs17 = require("fs");
7949
+ var import_node_path19 = __toESM(require("path"), 1);
7821
7950
  var import_types17 = require("@neat.is/types");
7822
7951
  function languageForSourceExt(ext) {
7823
7952
  switch (ext) {
@@ -7859,12 +7988,12 @@ function languageForSourceExt(ext) {
7859
7988
  }
7860
7989
  var LANGUAGE_PRECEDENCE = ["typescript", "javascript", "python", "go", "ruby", "php", "csharp", "java", "kotlin", "rust", "cpp"];
7861
7990
  async function inferServiceLanguage(dir) {
7862
- const entries = await import_node_fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7991
+ const entries = await import_node_fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7863
7992
  const counts = /* @__PURE__ */ new Map();
7864
7993
  for (const entry of entries) {
7865
7994
  if (!entry.isFile()) continue;
7866
7995
  if (isNeatAuthoredSourceFile(entry.name)) continue;
7867
- const ext = import_node_path18.default.extname(entry.name).toLowerCase();
7996
+ const ext = import_node_path19.default.extname(entry.name).toLowerCase();
7868
7997
  if (!SERVICE_FILE_EXTENSIONS.has(ext)) continue;
7869
7998
  const lang = languageForSourceExt(ext);
7870
7999
  if (!lang) continue;
@@ -7883,7 +8012,7 @@ async function inferServiceLanguage(dir) {
7883
8012
  return best;
7884
8013
  }
7885
8014
  async function hasLanguageManifest(dir) {
7886
- return await exists(import_node_path18.default.join(dir, "package.json")) || await exists(import_node_path18.default.join(dir, "pyproject.toml")) || await exists(import_node_path18.default.join(dir, "requirements.txt")) || await exists(import_node_path18.default.join(dir, "setup.py")) || await exists(import_node_path18.default.join(dir, "go.mod")) || await exists(import_node_path18.default.join(dir, "Gemfile")) || await exists(import_node_path18.default.join(dir, "composer.json")) || // C#'s marker is a glob (`*.csproj` / `*.sln`), so it reads the directory
8015
+ return await exists(import_node_path19.default.join(dir, "package.json")) || await exists(import_node_path19.default.join(dir, "pyproject.toml")) || await exists(import_node_path19.default.join(dir, "requirements.txt")) || await exists(import_node_path19.default.join(dir, "setup.py")) || await exists(import_node_path19.default.join(dir, "go.mod")) || await exists(import_node_path19.default.join(dir, "Gemfile")) || await exists(import_node_path19.default.join(dir, "composer.json")) || // C#'s marker is a glob (`*.csproj` / `*.sln`), so it reads the directory
7887
8016
  // rather than probing a fixed filename (ADR-196).
7888
8017
  await hasCsharpProject(dir) || // Java (and its JVM sibling Kotlin, ADR-199) key on the same Maven / Gradle
7889
8018
  // build manifest (ADR-197), so this one check stands the Dockerfile fallback
@@ -7893,15 +8022,15 @@ async function hasLanguageManifest(dir) {
7893
8022
  await hasCppManifest(dir);
7894
8023
  }
7895
8024
  async function hasDockerfileDeclaredService(dir) {
7896
- if (!await exists(import_node_path18.default.join(dir, "Dockerfile"))) return false;
8025
+ if (!await exists(import_node_path19.default.join(dir, "Dockerfile"))) return false;
7897
8026
  return await inferServiceLanguage(dir) !== null;
7898
8027
  }
7899
8028
  async function discoverDockerfileService(scanPath, dir) {
7900
- if (!await exists(import_node_path18.default.join(dir, "Dockerfile"))) return null;
8029
+ if (!await exists(import_node_path19.default.join(dir, "Dockerfile"))) return null;
7901
8030
  if (await hasLanguageManifest(dir)) return null;
7902
8031
  const language = await inferServiceLanguage(dir);
7903
8032
  if (!language) return null;
7904
- const name = import_node_path18.default.basename(dir);
8033
+ const name = import_node_path19.default.basename(dir);
7905
8034
  const pkg = { name, dependencies: {} };
7906
8035
  const node = {
7907
8036
  id: (0, import_types17.serviceId)(name),
@@ -7909,24 +8038,24 @@ async function discoverDockerfileService(scanPath, dir) {
7909
8038
  name,
7910
8039
  language,
7911
8040
  dependencies: {},
7912
- repoPath: import_node_path18.default.relative(scanPath, dir)
8041
+ repoPath: import_node_path19.default.relative(scanPath, dir)
7913
8042
  };
7914
8043
  return { pkg, dir, node };
7915
8044
  }
7916
8045
 
7917
8046
  // src/extract/owners.ts
7918
8047
  init_cjs_shims();
7919
- var import_node_fs17 = require("fs");
7920
- var import_node_path19 = __toESM(require("path"), 1);
8048
+ var import_node_fs18 = require("fs");
8049
+ var import_node_path20 = __toESM(require("path"), 1);
7921
8050
  var import_minimatch = require("minimatch");
7922
8051
  async function loadCodeowners(scanPath) {
7923
8052
  const candidates = [
7924
- import_node_path19.default.join(scanPath, "CODEOWNERS"),
7925
- import_node_path19.default.join(scanPath, ".github", "CODEOWNERS")
8053
+ import_node_path20.default.join(scanPath, "CODEOWNERS"),
8054
+ import_node_path20.default.join(scanPath, ".github", "CODEOWNERS")
7926
8055
  ];
7927
8056
  for (const file of candidates) {
7928
8057
  if (await exists(file)) {
7929
- const raw = await import_node_fs17.promises.readFile(file, "utf8");
8058
+ const raw = await import_node_fs18.promises.readFile(file, "utf8");
7930
8059
  return parseCodeowners(raw);
7931
8060
  }
7932
8061
  }
@@ -7944,7 +8073,7 @@ function parseCodeowners(raw) {
7944
8073
  return { rules };
7945
8074
  }
7946
8075
  function matchOwner(file, repoPath) {
7947
- const normalized = repoPath.split(import_node_path19.default.sep).join("/");
8076
+ const normalized = repoPath.split(import_node_path20.default.sep).join("/");
7948
8077
  for (const rule of file.rules) {
7949
8078
  if (matchesPattern(rule.pattern, normalized)) return rule.owners;
7950
8079
  }
@@ -7960,7 +8089,7 @@ function matchesPattern(rawPattern, repoPath) {
7960
8089
  return false;
7961
8090
  }
7962
8091
  async function readPackageJsonAuthor(serviceDir) {
7963
- const pkgPath = import_node_path19.default.join(serviceDir, "package.json");
8092
+ const pkgPath = import_node_path20.default.join(serviceDir, "package.json");
7964
8093
  if (!await exists(pkgPath)) return null;
7965
8094
  try {
7966
8095
  const pkg = await readJson(pkgPath);
@@ -7997,36 +8126,36 @@ function workspaceGlobs(pkg) {
7997
8126
  return null;
7998
8127
  }
7999
8128
  async function hasPythonManifest(dir) {
8000
- return await exists(import_node_path20.default.join(dir, "pyproject.toml")) || await exists(import_node_path20.default.join(dir, "requirements.txt")) || await exists(import_node_path20.default.join(dir, "setup.py"));
8129
+ return await exists(import_node_path21.default.join(dir, "pyproject.toml")) || await exists(import_node_path21.default.join(dir, "requirements.txt")) || await exists(import_node_path21.default.join(dir, "setup.py"));
8001
8130
  }
8002
8131
  async function hasGoManifest(dir) {
8003
- return exists(import_node_path20.default.join(dir, "go.mod"));
8132
+ return exists(import_node_path21.default.join(dir, "go.mod"));
8004
8133
  }
8005
8134
  async function hasRubyManifest(dir) {
8006
- return exists(import_node_path20.default.join(dir, "Gemfile"));
8135
+ return exists(import_node_path21.default.join(dir, "Gemfile"));
8007
8136
  }
8008
8137
  async function hasPhpManifest(dir) {
8009
- return exists(import_node_path20.default.join(dir, "composer.json"));
8138
+ return exists(import_node_path21.default.join(dir, "composer.json"));
8010
8139
  }
8011
8140
  async function hasCsharpManifest(dir) {
8012
8141
  return hasCsharpProject(dir);
8013
8142
  }
8014
8143
  async function loadGitignore(scanPath) {
8015
- const gitignorePath = import_node_path20.default.join(scanPath, ".gitignore");
8144
+ const gitignorePath = import_node_path21.default.join(scanPath, ".gitignore");
8016
8145
  if (!await exists(gitignorePath)) return null;
8017
- const raw = await import_node_fs18.promises.readFile(gitignorePath, "utf8");
8018
- return (0, import_ignore.default)().add(raw);
8146
+ const raw = await import_node_fs19.promises.readFile(gitignorePath, "utf8");
8147
+ return (0, import_ignore2.default)().add(raw);
8019
8148
  }
8020
8149
  async function walkDirs(start, scanPath, options, visit) {
8021
8150
  async function recurse(current, depth) {
8022
8151
  if (depth > options.maxDepth) return;
8023
- const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8152
+ const entries = await import_node_fs19.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8024
8153
  for (const entry of entries) {
8025
8154
  if (!entry.isDirectory()) continue;
8026
8155
  if (IGNORED_DIRS.has(entry.name)) continue;
8027
- const child = import_node_path20.default.join(current, entry.name);
8156
+ const child = import_node_path21.default.join(current, entry.name);
8028
8157
  if (options.ig) {
8029
- const rel = import_node_path20.default.relative(scanPath, child).split(import_node_path20.default.sep).join("/");
8158
+ const rel = import_node_path21.default.relative(scanPath, child).split(import_node_path21.default.sep).join("/");
8030
8159
  if (rel && options.ig.ignores(rel + "/")) continue;
8031
8160
  }
8032
8161
  if (await isPythonVenvDir(child)) continue;
@@ -8042,8 +8171,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
8042
8171
  for (const raw of globs) {
8043
8172
  const pattern = raw.replace(/^\.\//, "");
8044
8173
  if (!pattern.includes("*")) {
8045
- const candidate = import_node_path20.default.join(scanPath, pattern);
8046
- if (await exists(import_node_path20.default.join(candidate, "package.json"))) found.add(candidate);
8174
+ const candidate = import_node_path21.default.join(scanPath, pattern);
8175
+ if (await exists(import_node_path21.default.join(candidate, "package.json"))) found.add(candidate);
8047
8176
  continue;
8048
8177
  }
8049
8178
  const segments = pattern.split("/");
@@ -8052,13 +8181,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
8052
8181
  if (seg.includes("*")) break;
8053
8182
  staticSegments.push(seg);
8054
8183
  }
8055
- const start = import_node_path20.default.join(scanPath, ...staticSegments);
8184
+ const start = import_node_path21.default.join(scanPath, ...staticSegments);
8056
8185
  if (!await exists(start)) continue;
8057
8186
  const hasDoubleStar = pattern.includes("**");
8058
8187
  const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
8059
8188
  await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
8060
- const rel = import_node_path20.default.relative(scanPath, dir).split(import_node_path20.default.sep).join("/");
8061
- if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path20.default.join(dir, "package.json"))) {
8189
+ const rel = import_node_path21.default.relative(scanPath, dir).split(import_node_path21.default.sep).join("/");
8190
+ if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path21.default.join(dir, "package.json"))) {
8062
8191
  found.add(dir);
8063
8192
  }
8064
8193
  });
@@ -8081,18 +8210,18 @@ function detectJsFramework(pkg) {
8081
8210
  async function detectJsServiceLanguage(dir, pkg) {
8082
8211
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
8083
8212
  if (deps["typescript"] !== void 0) return "typescript";
8084
- const entries = await import_node_fs18.promises.readdir(dir).catch(() => []);
8213
+ const entries = await import_node_fs19.promises.readdir(dir).catch(() => []);
8085
8214
  if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
8086
8215
  return "javascript";
8087
8216
  }
8088
8217
  async function discoverNodeService(scanPath, dir) {
8089
- const pkgPath = import_node_path20.default.join(dir, "package.json");
8218
+ const pkgPath = import_node_path21.default.join(dir, "package.json");
8090
8219
  if (!await exists(pkgPath)) return null;
8091
8220
  let pkg;
8092
8221
  try {
8093
8222
  pkg = await readJson(pkgPath);
8094
8223
  } catch (err) {
8095
- recordExtractionError("services", import_node_path20.default.relative(scanPath, pkgPath), err);
8224
+ recordExtractionError("services", import_node_path21.default.relative(scanPath, pkgPath), err);
8096
8225
  return null;
8097
8226
  }
8098
8227
  if (!pkg.name) return null;
@@ -8105,7 +8234,7 @@ async function discoverNodeService(scanPath, dir) {
8105
8234
  language,
8106
8235
  version: pkg.version,
8107
8236
  dependencies: pkg.dependencies ?? {},
8108
- repoPath: import_node_path20.default.relative(scanPath, dir),
8237
+ repoPath: import_node_path21.default.relative(scanPath, dir),
8109
8238
  ...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
8110
8239
  ...framework ? { framework } : {}
8111
8240
  };
@@ -8122,12 +8251,12 @@ async function discoverPyService(scanPath, dir) {
8122
8251
  language: "python",
8123
8252
  version: py.version,
8124
8253
  dependencies: py.dependencies,
8125
- repoPath: import_node_path20.default.relative(scanPath, dir)
8254
+ repoPath: import_node_path21.default.relative(scanPath, dir)
8126
8255
  };
8127
8256
  return { pkg, dir, node };
8128
8257
  }
8129
8258
  async function discoverServices(scanPath) {
8130
- const rootPkgPath = import_node_path20.default.join(scanPath, "package.json");
8259
+ const rootPkgPath = import_node_path21.default.join(scanPath, "package.json");
8131
8260
  let rootPkg = null;
8132
8261
  if (await exists(rootPkgPath)) {
8133
8262
  try {
@@ -8135,7 +8264,7 @@ async function discoverServices(scanPath) {
8135
8264
  } catch (err) {
8136
8265
  recordExtractionError(
8137
8266
  "services workspaces",
8138
- import_node_path20.default.relative(scanPath, rootPkgPath),
8267
+ import_node_path21.default.relative(scanPath, rootPkgPath),
8139
8268
  err
8140
8269
  );
8141
8270
  }
@@ -8156,7 +8285,7 @@ async function discoverServices(scanPath) {
8156
8285
  scanPath,
8157
8286
  { maxDepth: parseScanDepth(), ig },
8158
8287
  async (dir) => {
8159
- if (await exists(import_node_path20.default.join(dir, "package.json"))) {
8288
+ if (await exists(import_node_path21.default.join(dir, "package.json"))) {
8160
8289
  candidateDirs.push(dir);
8161
8290
  } else if (await hasPythonManifest(dir) || await hasGoManifest(dir) || await hasRubyManifest(dir) || await hasPhpManifest(dir) || await hasCsharpManifest(dir) || await hasJavaManifest(dir) || await hasRustManifest(dir) || await hasCppManifest(dir)) {
8162
8291
  candidateDirs.push(dir);
@@ -8184,8 +8313,8 @@ async function discoverServices(scanPath) {
8184
8313
  seenDirs.add(service.dir);
8185
8314
  const existingDir = seen.get(service.node.name);
8186
8315
  if (existingDir !== void 0) {
8187
- const a = import_node_path20.default.relative(scanPath, existingDir) || ".";
8188
- const b = import_node_path20.default.relative(scanPath, dir) || ".";
8316
+ const a = import_node_path21.default.relative(scanPath, existingDir) || ".";
8317
+ const b = import_node_path21.default.relative(scanPath, dir) || ".";
8189
8318
  console.warn(
8190
8319
  `[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
8191
8320
  );
@@ -8199,14 +8328,14 @@ async function discoverServices(scanPath) {
8199
8328
  const owner = await computeServiceOwner(codeowners, service.node.repoPath, service.dir);
8200
8329
  if (owner !== void 0) service.node.owner = owner;
8201
8330
  }
8202
- const resolvedDirs = out.map((s) => import_node_path20.default.resolve(s.dir));
8331
+ const resolvedDirs = out.map((s) => import_node_path21.default.resolve(s.dir));
8203
8332
  for (let i = 0; i < out.length; i++) {
8204
8333
  const self = resolvedDirs[i];
8205
8334
  const nested = [];
8206
8335
  for (let j = 0; j < out.length; j++) {
8207
8336
  if (i === j) continue;
8208
8337
  const other = resolvedDirs[j];
8209
- if (other.startsWith(self + import_node_path20.default.sep)) nested.push(other);
8338
+ if (other.startsWith(self + import_node_path21.default.sep)) nested.push(other);
8210
8339
  }
8211
8340
  out[i].excludeDirs = nested;
8212
8341
  }
@@ -8233,8 +8362,8 @@ function addServiceNodes(graph, services) {
8233
8362
 
8234
8363
  // src/extract/aliases.ts
8235
8364
  init_cjs_shims();
8236
- var import_node_path21 = __toESM(require("path"), 1);
8237
- var import_node_fs19 = require("fs");
8365
+ var import_node_path22 = __toESM(require("path"), 1);
8366
+ var import_node_fs20 = require("fs");
8238
8367
  var import_yaml2 = require("yaml");
8239
8368
  var import_types19 = require("@neat.is/types");
8240
8369
  var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
@@ -8261,14 +8390,14 @@ function indexServicesByName(services) {
8261
8390
  const map = /* @__PURE__ */ new Map();
8262
8391
  for (const s of services) {
8263
8392
  map.set(s.node.name, s.node.id);
8264
- map.set(import_node_path21.default.basename(s.dir), s.node.id);
8393
+ map.set(import_node_path22.default.basename(s.dir), s.node.id);
8265
8394
  }
8266
8395
  return map;
8267
8396
  }
8268
8397
  async function collectComposeAliases(graph, scanPath, serviceIndex) {
8269
8398
  let composePath = null;
8270
8399
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
8271
- const abs = import_node_path21.default.join(scanPath, name);
8400
+ const abs = import_node_path22.default.join(scanPath, name);
8272
8401
  if (await exists(abs)) {
8273
8402
  composePath = abs;
8274
8403
  break;
@@ -8281,7 +8410,7 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
8281
8410
  } catch (err) {
8282
8411
  recordExtractionError(
8283
8412
  "aliases compose",
8284
- import_node_path21.default.relative(scanPath, composePath),
8413
+ import_node_path22.default.relative(scanPath, composePath),
8285
8414
  err
8286
8415
  );
8287
8416
  return;
@@ -8324,11 +8453,11 @@ function parseDockerfileLabels(content) {
8324
8453
  }
8325
8454
  async function collectDockerfileAliases(graph, services) {
8326
8455
  for (const service of services) {
8327
- const dockerfilePath = import_node_path21.default.join(service.dir, "Dockerfile");
8456
+ const dockerfilePath = import_node_path22.default.join(service.dir, "Dockerfile");
8328
8457
  if (!await exists(dockerfilePath)) continue;
8329
8458
  let content;
8330
8459
  try {
8331
- content = await import_node_fs19.promises.readFile(dockerfilePath, "utf8");
8460
+ content = await import_node_fs20.promises.readFile(dockerfilePath, "utf8");
8332
8461
  } catch (err) {
8333
8462
  recordExtractionError("aliases dockerfile", dockerfilePath, err);
8334
8463
  continue;
@@ -8340,15 +8469,15 @@ async function collectDockerfileAliases(graph, services) {
8340
8469
  async function walkYamlFiles(start, depth = 0, max = 5) {
8341
8470
  if (depth > max) return [];
8342
8471
  const out = [];
8343
- const entries = await import_node_fs19.promises.readdir(start, { withFileTypes: true }).catch(() => []);
8472
+ const entries = await import_node_fs20.promises.readdir(start, { withFileTypes: true }).catch(() => []);
8344
8473
  for (const entry of entries) {
8345
8474
  if (entry.isDirectory()) {
8346
8475
  if (IGNORED_DIRS.has(entry.name)) continue;
8347
- const child = import_node_path21.default.join(start, entry.name);
8476
+ const child = import_node_path22.default.join(start, entry.name);
8348
8477
  if (await isPythonVenvDir(child)) continue;
8349
8478
  out.push(...await walkYamlFiles(child, depth + 1, max));
8350
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path21.default.extname(entry.name))) {
8351
- out.push(import_node_path21.default.join(start, entry.name));
8479
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path22.default.extname(entry.name))) {
8480
+ out.push(import_node_path22.default.join(start, entry.name));
8352
8481
  }
8353
8482
  }
8354
8483
  return out;
@@ -8375,7 +8504,7 @@ function k8sServiceTarget(doc, byName) {
8375
8504
  async function collectK8sAliases(graph, scanPath, serviceIndex) {
8376
8505
  const files = await walkYamlFiles(scanPath);
8377
8506
  for (const file of files) {
8378
- const content = await import_node_fs19.promises.readFile(file, "utf8");
8507
+ const content = await import_node_fs20.promises.readFile(file, "utf8");
8379
8508
  let docs;
8380
8509
  try {
8381
8510
  docs = (0, import_yaml2.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -8400,14 +8529,14 @@ async function addServiceAliases(graph, scanPath, services) {
8400
8529
 
8401
8530
  // src/extract/files.ts
8402
8531
  init_cjs_shims();
8403
- var import_node_path22 = __toESM(require("path"), 1);
8532
+ var import_node_path23 = __toESM(require("path"), 1);
8404
8533
  async function addFiles(graph, services) {
8405
8534
  let nodesAdded = 0;
8406
8535
  let edgesAdded = 0;
8407
8536
  for (const service of services) {
8408
8537
  const filePaths = await walkSourceFiles(service.dir, service.excludeDirs);
8409
8538
  for (const filePath of filePaths) {
8410
- const relPath = toPosix(import_node_path22.default.relative(service.dir, filePath));
8539
+ const relPath = toPosix(import_node_path23.default.relative(service.dir, filePath));
8411
8540
  const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
8412
8541
  graph,
8413
8542
  service.pkg.name,
@@ -8423,7 +8552,7 @@ async function addFiles(graph, services) {
8423
8552
 
8424
8553
  // src/extract/symbols.ts
8425
8554
  init_cjs_shims();
8426
- var import_node_path23 = __toESM(require("path"), 1);
8555
+ var import_node_path24 = __toESM(require("path"), 1);
8427
8556
  var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
8428
8557
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
8429
8558
  var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
@@ -9175,10 +9304,10 @@ async function addSymbols(graph, services) {
9175
9304
  for (const service of services) {
9176
9305
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
9177
9306
  for (const file of files) {
9178
- const ext = import_node_path23.default.extname(file.path);
9307
+ const ext = import_node_path24.default.extname(file.path);
9179
9308
  const parser = parserForExt5(ext);
9180
9309
  if (!parser) continue;
9181
- const relPath = toPosix(import_node_path23.default.relative(service.dir, file.path));
9310
+ const relPath = toPosix(import_node_path24.default.relative(service.dir, file.path));
9182
9311
  let defs;
9183
9312
  try {
9184
9313
  const tree = parseSource3(parser, file.content);
@@ -9238,7 +9367,7 @@ async function addSymbols(graph, services) {
9238
9367
 
9239
9368
  // src/extract/symbol-edges.ts
9240
9369
  init_cjs_shims();
9241
- var import_node_path24 = __toESM(require("path"), 1);
9370
+ var import_node_path25 = __toESM(require("path"), 1);
9242
9371
  var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
9243
9372
  var import_types21 = require("@neat.is/types");
9244
9373
  function extendsInfo(classHeritage) {
@@ -9345,10 +9474,10 @@ async function addSymbolEdges(graph, services) {
9345
9474
  const tsPaths = await loadTsPathConfig(service.dir);
9346
9475
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
9347
9476
  for (const file of files) {
9348
- const parser = parserForExt5(import_node_path24.default.extname(file.path));
9477
+ const parser = parserForExt5(import_node_path25.default.extname(file.path));
9349
9478
  if (!parser) continue;
9350
- const relPath = toPosix(import_node_path24.default.relative(service.dir, file.path));
9351
- const fileDir = import_node_path24.default.dirname(file.path);
9479
+ const relPath = toPosix(import_node_path25.default.relative(service.dir, file.path));
9480
+ const fileDir = import_node_path25.default.dirname(file.path);
9352
9481
  let root;
9353
9482
  try {
9354
9483
  root = parseSource3(parser, file.content).rootNode;
@@ -9483,7 +9612,7 @@ async function addSymbolEdges(graph, services) {
9483
9612
 
9484
9613
  // src/extract/actions.ts
9485
9614
  init_cjs_shims();
9486
- var import_node_path25 = __toESM(require("path"), 1);
9615
+ var import_node_path26 = __toESM(require("path"), 1);
9487
9616
  var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
9488
9617
  var import_types22 = require("@neat.is/types");
9489
9618
  function stringLiteralText2(node) {
@@ -9630,9 +9759,9 @@ async function addServerActions(graph, services) {
9630
9759
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
9631
9760
  for (const file of files) {
9632
9761
  if (isTestPath(file.path)) continue;
9633
- const parser = parserForExt5(import_node_path25.default.extname(file.path));
9762
+ const parser = parserForExt5(import_node_path26.default.extname(file.path));
9634
9763
  if (!parser) continue;
9635
- const relPath = toPosix(import_node_path25.default.relative(service.dir, file.path));
9764
+ const relPath = toPosix(import_node_path26.default.relative(service.dir, file.path));
9636
9765
  let root;
9637
9766
  try {
9638
9767
  root = parseSource3(parser, file.content).rootNode;
@@ -9693,10 +9822,10 @@ async function addServerActions(graph, services) {
9693
9822
  }
9694
9823
  for (const file of files) {
9695
9824
  if (isTestPath(file.path)) continue;
9696
- const parser = parserForExt5(import_node_path25.default.extname(file.path));
9825
+ const parser = parserForExt5(import_node_path26.default.extname(file.path));
9697
9826
  if (!parser) continue;
9698
- const relPath = toPosix(import_node_path25.default.relative(service.dir, file.path));
9699
- const fileDir = import_node_path25.default.dirname(file.path);
9827
+ const relPath = toPosix(import_node_path26.default.relative(service.dir, file.path));
9828
+ const fileDir = import_node_path26.default.dirname(file.path);
9700
9829
  let root;
9701
9830
  try {
9702
9831
  root = parseSource3(parser, file.content).rootNode;
@@ -9749,14 +9878,14 @@ async function addServerActions(graph, services) {
9749
9878
 
9750
9879
  // src/extract/databases/index.ts
9751
9880
  init_cjs_shims();
9752
- var import_node_path34 = __toESM(require("path"), 1);
9881
+ var import_node_path35 = __toESM(require("path"), 1);
9753
9882
  var import_types23 = require("@neat.is/types");
9754
9883
 
9755
9884
  // src/extract/databases/db-config-yaml.ts
9756
9885
  init_cjs_shims();
9757
- var import_node_path26 = __toESM(require("path"), 1);
9886
+ var import_node_path27 = __toESM(require("path"), 1);
9758
9887
  async function parse(serviceDir) {
9759
- const yamlPath = import_node_path26.default.join(serviceDir, "db-config.yaml");
9888
+ const yamlPath = import_node_path27.default.join(serviceDir, "db-config.yaml");
9760
9889
  if (!await exists(yamlPath)) return [];
9761
9890
  const raw = await readYaml(yamlPath);
9762
9891
  return [
@@ -9774,13 +9903,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
9774
9903
 
9775
9904
  // src/extract/databases/dotenv.ts
9776
9905
  init_cjs_shims();
9777
- var import_node_fs21 = require("fs");
9778
- var import_node_path28 = __toESM(require("path"), 1);
9906
+ var import_node_fs22 = require("fs");
9907
+ var import_node_path29 = __toESM(require("path"), 1);
9779
9908
 
9780
9909
  // src/extract/databases/shared.ts
9781
9910
  init_cjs_shims();
9782
- var import_node_fs20 = require("fs");
9783
- var import_node_path27 = __toESM(require("path"), 1);
9911
+ var import_node_fs21 = require("fs");
9912
+ var import_node_path28 = __toESM(require("path"), 1);
9784
9913
  function schemeToEngine(scheme) {
9785
9914
  const s = scheme.toLowerCase().split("+")[0];
9786
9915
  switch (s) {
@@ -9819,18 +9948,18 @@ function parseConnectionString(url) {
9819
9948
  }
9820
9949
  async function readIfExists(filePath) {
9821
9950
  try {
9822
- return await import_node_fs20.promises.readFile(filePath, "utf8");
9951
+ return await import_node_fs21.promises.readFile(filePath, "utf8");
9823
9952
  } catch {
9824
9953
  return null;
9825
9954
  }
9826
9955
  }
9827
9956
  async function resolveEnvVar(serviceDir, name) {
9828
- const entries = await import_node_fs20.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9957
+ const entries = await import_node_fs21.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9829
9958
  const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
9830
9959
  const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
9831
9960
  envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
9832
9961
  for (const fileName of envNames) {
9833
- const content = await readIfExists(import_node_path27.default.join(serviceDir, fileName));
9962
+ const content = await readIfExists(import_node_path28.default.join(serviceDir, fileName));
9834
9963
  if (!content) continue;
9835
9964
  for (const line of content.split("\n")) {
9836
9965
  const trimmed = line.trim();
@@ -9849,7 +9978,7 @@ async function resolveEnvVar(serviceDir, name) {
9849
9978
  }
9850
9979
  async function findFirst(serviceDir, candidates) {
9851
9980
  for (const rel of candidates) {
9852
- const abs = import_node_path27.default.join(serviceDir, rel);
9981
+ const abs = import_node_path28.default.join(serviceDir, rel);
9853
9982
  const content = await readIfExists(abs);
9854
9983
  if (content !== null) return abs;
9855
9984
  }
@@ -9907,15 +10036,15 @@ function parseDotenvLine(line) {
9907
10036
  return { key, value };
9908
10037
  }
9909
10038
  async function parse2(serviceDir) {
9910
- const entries = await import_node_fs21.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
10039
+ const entries = await import_node_fs22.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9911
10040
  const configs = [];
9912
10041
  const seen = /* @__PURE__ */ new Set();
9913
10042
  for (const entry of entries) {
9914
10043
  if (!entry.isFile()) continue;
9915
10044
  const match = isConfigFile(entry.name);
9916
10045
  if (!match.match || match.fileType !== "env") continue;
9917
- const filePath = import_node_path28.default.join(serviceDir, entry.name);
9918
- const content = await import_node_fs21.promises.readFile(filePath, "utf8");
10046
+ const filePath = import_node_path29.default.join(serviceDir, entry.name);
10047
+ const content = await import_node_fs22.promises.readFile(filePath, "utf8");
9919
10048
  for (const line of content.split("\n")) {
9920
10049
  const parsed = parseDotenvLine(line);
9921
10050
  if (!parsed) continue;
@@ -9934,9 +10063,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
9934
10063
 
9935
10064
  // src/extract/databases/prisma.ts
9936
10065
  init_cjs_shims();
9937
- var import_node_path29 = __toESM(require("path"), 1);
10066
+ var import_node_path30 = __toESM(require("path"), 1);
9938
10067
  async function parse3(serviceDir) {
9939
- const schemaPath = import_node_path29.default.join(serviceDir, "prisma", "schema.prisma");
10068
+ const schemaPath = import_node_path30.default.join(serviceDir, "prisma", "schema.prisma");
9940
10069
  const content = await readIfExists(schemaPath);
9941
10070
  if (!content) return [];
9942
10071
  const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
@@ -10094,10 +10223,10 @@ var knexParser = { name: "knex", parse: parse5 };
10094
10223
 
10095
10224
  // src/extract/databases/ormconfig.ts
10096
10225
  init_cjs_shims();
10097
- var import_node_path30 = __toESM(require("path"), 1);
10226
+ var import_node_path31 = __toESM(require("path"), 1);
10098
10227
  async function parse6(serviceDir) {
10099
10228
  for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
10100
- const abs = import_node_path30.default.join(serviceDir, candidate);
10229
+ const abs = import_node_path31.default.join(serviceDir, candidate);
10101
10230
  if (!await exists(abs)) continue;
10102
10231
  const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
10103
10232
  const entries = Array.isArray(raw) ? raw : [raw];
@@ -10157,9 +10286,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
10157
10286
 
10158
10287
  // src/extract/databases/sequelize.ts
10159
10288
  init_cjs_shims();
10160
- var import_node_path31 = __toESM(require("path"), 1);
10289
+ var import_node_path32 = __toESM(require("path"), 1);
10161
10290
  async function parse8(serviceDir) {
10162
- const configPath = import_node_path31.default.join(serviceDir, "config", "config.json");
10291
+ const configPath = import_node_path32.default.join(serviceDir, "config", "config.json");
10163
10292
  if (!await exists(configPath)) return [];
10164
10293
  const raw = await readJson(configPath);
10165
10294
  const out = [];
@@ -10186,8 +10315,8 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
10186
10315
 
10187
10316
  // src/extract/databases/csharp.ts
10188
10317
  init_cjs_shims();
10189
- var import_node_fs22 = require("fs");
10190
- var import_node_path32 = __toESM(require("path"), 1);
10318
+ var import_node_fs23 = require("fs");
10319
+ var import_node_path33 = __toESM(require("path"), 1);
10191
10320
  var CS_EXT = ".cs";
10192
10321
  var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
10193
10322
  var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
@@ -10234,12 +10363,12 @@ function parseRedisEndpoint(raw) {
10234
10363
  return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
10235
10364
  }
10236
10365
  async function resolveEnvUpTree(startDir, name) {
10237
- let dir = import_node_path32.default.resolve(startDir);
10366
+ let dir = import_node_path33.default.resolve(startDir);
10238
10367
  for (let depth = 0; depth < 12; depth++) {
10239
10368
  const value = await resolveEnvVar(dir, name);
10240
10369
  if (value !== null) return value;
10241
- const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
10242
- const parent = import_node_path32.default.dirname(dir);
10370
+ const atRepoRoot = await import_node_fs23.promises.access(import_node_path33.default.join(dir, ".git")).then(() => true).catch(() => false);
10371
+ const parent = import_node_path33.default.dirname(dir);
10243
10372
  if (atRepoRoot || parent === dir) break;
10244
10373
  dir = parent;
10245
10374
  }
@@ -10294,12 +10423,12 @@ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
10294
10423
  }
10295
10424
  async function parse9(serviceDir) {
10296
10425
  const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
10297
- (f) => import_node_path32.default.extname(f) === CS_EXT
10426
+ (f) => import_node_path33.default.extname(f) === CS_EXT
10298
10427
  );
10299
10428
  if (files.length === 0) return [];
10300
10429
  const sources = [];
10301
10430
  for (const file of files) {
10302
- const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
10431
+ const content = await import_node_fs23.promises.readFile(file, "utf8").catch(() => null);
10303
10432
  if (content !== null) sources.push({ file, content });
10304
10433
  }
10305
10434
  let pgGateFile = null;
@@ -10340,7 +10469,7 @@ var csharpParser = { name: "csharp", parse: parse9 };
10340
10469
 
10341
10470
  // src/extract/databases/docker-compose.ts
10342
10471
  init_cjs_shims();
10343
- var import_node_path33 = __toESM(require("path"), 1);
10472
+ var import_node_path34 = __toESM(require("path"), 1);
10344
10473
  function portFromService(svc) {
10345
10474
  for (const raw of svc.ports ?? []) {
10346
10475
  const str = String(raw);
@@ -10367,7 +10496,7 @@ function databaseFromEnv(svc) {
10367
10496
  }
10368
10497
  async function parse10(serviceDir) {
10369
10498
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
10370
- const abs = import_node_path33.default.join(serviceDir, name);
10499
+ const abs = import_node_path34.default.join(serviceDir, name);
10371
10500
  if (!await exists(abs)) continue;
10372
10501
  const raw = await readYaml(abs);
10373
10502
  if (!raw?.services) return [];
@@ -10540,7 +10669,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10540
10669
  discoveredVia: mergedDiscoveredVia
10541
10670
  });
10542
10671
  }
10543
- const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
10672
+ const relConfigFile = toPosix(import_node_path35.default.relative(service.dir, config.sourceFile));
10544
10673
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10545
10674
  graph,
10546
10675
  service.pkg.name,
@@ -10549,7 +10678,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10549
10678
  );
10550
10679
  nodesAdded += fn;
10551
10680
  edgesAdded += fe;
10552
- const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
10681
+ const evidenceFile = toPosix(import_node_path35.default.relative(scanPath, config.sourceFile));
10553
10682
  const edge = {
10554
10683
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
10555
10684
  source: fileNodeId,
@@ -10570,15 +10699,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10570
10699
  if (allConfigs.length === 1) {
10571
10700
  const primary = allConfigs[0];
10572
10701
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
10573
- const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
10702
+ const relPath = import_node_path35.default.relative(scanPath, primary.sourceFile);
10574
10703
  const cfgId = (0, import_types23.configId)(relPath);
10575
10704
  if (!graph.hasNode(cfgId)) {
10576
10705
  const cfgNode = {
10577
10706
  id: cfgId,
10578
10707
  type: import_types23.NodeType.ConfigNode,
10579
- name: import_node_path34.default.basename(primary.sourceFile),
10708
+ name: import_node_path35.default.basename(primary.sourceFile),
10580
10709
  path: relPath,
10581
- fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
10710
+ fileType: isConfigFile(import_node_path35.default.basename(primary.sourceFile)).fileType || "config"
10582
10711
  };
10583
10712
  graph.addNode(cfgId, cfgNode);
10584
10713
  nodesAdded++;
@@ -10619,19 +10748,19 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10619
10748
 
10620
10749
  // src/extract/configs.ts
10621
10750
  init_cjs_shims();
10622
- var import_node_fs23 = require("fs");
10623
- var import_node_path35 = __toESM(require("path"), 1);
10751
+ var import_node_fs24 = require("fs");
10752
+ var import_node_path36 = __toESM(require("path"), 1);
10624
10753
  var import_types24 = require("@neat.is/types");
10625
10754
  async function walkConfigFiles(dir, excludeDirs = []) {
10626
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10755
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10627
10756
  const out = [];
10628
10757
  async function walk11(current) {
10629
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
10758
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true });
10630
10759
  for (const entry of entries) {
10631
- const full = import_node_path35.default.join(current, entry.name);
10760
+ const full = import_node_path36.default.join(current, entry.name);
10632
10761
  if (entry.isDirectory()) {
10633
10762
  if (IGNORED_DIRS.has(entry.name)) continue;
10634
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10763
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
10635
10764
  if (await isPythonVenvDir(full)) continue;
10636
10765
  await walk11(full);
10637
10766
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
@@ -10648,19 +10777,19 @@ async function addConfigNodes(graph, services, scanPath) {
10648
10777
  for (const service of services) {
10649
10778
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
10650
10779
  for (const file of configFiles) {
10651
- const relPath = import_node_path35.default.relative(scanPath, file);
10780
+ const relPath = import_node_path36.default.relative(scanPath, file);
10652
10781
  const node = {
10653
10782
  id: (0, import_types24.configId)(relPath),
10654
10783
  type: import_types24.NodeType.ConfigNode,
10655
- name: import_node_path35.default.basename(file),
10784
+ name: import_node_path36.default.basename(file),
10656
10785
  path: relPath,
10657
- fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
10786
+ fileType: isConfigFile(import_node_path36.default.basename(file)).fileType
10658
10787
  };
10659
10788
  if (!graph.hasNode(node.id)) {
10660
10789
  graph.addNode(node.id, node);
10661
10790
  nodesAdded++;
10662
10791
  }
10663
- const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
10792
+ const relToService = toPosix(import_node_path36.default.relative(service.dir, file));
10664
10793
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10665
10794
  graph,
10666
10795
  service.pkg.name,
@@ -10676,7 +10805,7 @@ async function addConfigNodes(graph, services, scanPath) {
10676
10805
  type: import_types24.EdgeType.CONFIGURED_BY,
10677
10806
  provenance: import_types24.Provenance.EXTRACTED,
10678
10807
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
10679
- evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
10808
+ evidence: { file: relPath.split(import_node_path36.default.sep).join("/") }
10680
10809
  };
10681
10810
  if (!graph.hasEdge(edge.id)) {
10682
10811
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -10689,8 +10818,8 @@ async function addConfigNodes(graph, services, scanPath) {
10689
10818
 
10690
10819
  // src/extract/proto.ts
10691
10820
  init_cjs_shims();
10692
- var import_node_fs24 = require("fs");
10693
- var import_node_path36 = __toESM(require("path"), 1);
10821
+ var import_node_fs25 = require("fs");
10822
+ var import_node_path37 = __toESM(require("path"), 1);
10694
10823
  var import_types25 = require("@neat.is/types");
10695
10824
  var PROTO_EXTENSION = ".proto";
10696
10825
  function packageOf(content) {
@@ -10728,18 +10857,18 @@ function grpcMethodsFromProto(content, fqPackage) {
10728
10857
  return out;
10729
10858
  }
10730
10859
  async function walkProtoFiles(dir, excludeDirs = []) {
10731
- const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10860
+ const excluded = new Set(excludeDirs.map((d) => import_node_path37.default.resolve(d)));
10732
10861
  const out = [];
10733
10862
  async function walk11(current) {
10734
- const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10863
+ const entries = await import_node_fs25.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10735
10864
  for (const entry of entries) {
10736
- const full = import_node_path36.default.join(current, entry.name);
10865
+ const full = import_node_path37.default.join(current, entry.name);
10737
10866
  if (entry.isDirectory()) {
10738
10867
  if (IGNORED_DIRS.has(entry.name)) continue;
10739
- if (excluded.has(import_node_path36.default.resolve(full))) continue;
10868
+ if (excluded.has(import_node_path37.default.resolve(full))) continue;
10740
10869
  if (await isPythonVenvDir(full)) continue;
10741
10870
  await walk11(full);
10742
- } else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
10871
+ } else if (entry.isFile() && import_node_path37.default.extname(entry.name) === PROTO_EXTENSION) {
10743
10872
  out.push(full);
10744
10873
  }
10745
10874
  }
@@ -10754,10 +10883,10 @@ async function addGrpcMethods(graph, services) {
10754
10883
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
10755
10884
  for (const protoPath of protoPaths) {
10756
10885
  if (isTestPath(protoPath)) continue;
10757
- const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
10886
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, protoPath));
10758
10887
  let content;
10759
10888
  try {
10760
- content = await import_node_fs24.promises.readFile(protoPath, "utf8");
10889
+ content = await import_node_fs25.promises.readFile(protoPath, "utf8");
10761
10890
  } catch (err) {
10762
10891
  recordExtractionError("proto extraction", protoPath, err);
10763
10892
  continue;
@@ -10816,7 +10945,7 @@ var import_types45 = require("@neat.is/types");
10816
10945
 
10817
10946
  // src/extract/calls/http.ts
10818
10947
  init_cjs_shims();
10819
- var import_node_path37 = __toESM(require("path"), 1);
10948
+ var import_node_path38 = __toESM(require("path"), 1);
10820
10949
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
10821
10950
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
10822
10951
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -10899,7 +11028,7 @@ async function addHttpCallEdges(graph, services) {
10899
11028
  const seen = /* @__PURE__ */ new Set();
10900
11029
  for (const file of files) {
10901
11030
  if (isTestPath(file.path)) continue;
10902
- const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
11031
+ const parser = parserForExt(import_node_path38.default.extname(file.path), parserCache);
10903
11032
  let sites;
10904
11033
  try {
10905
11034
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -10908,7 +11037,7 @@ async function addHttpCallEdges(graph, services) {
10908
11037
  continue;
10909
11038
  }
10910
11039
  if (sites.length === 0) continue;
10911
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
11040
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10912
11041
  for (const site of sites) {
10913
11042
  const targetId = hostToNodeId.get(site.host);
10914
11043
  if (!targetId || targetId === service.node.id) continue;
@@ -10962,7 +11091,7 @@ async function addHttpCallEdges(graph, services) {
10962
11091
 
10963
11092
  // src/extract/calls/route-match.ts
10964
11093
  init_cjs_shims();
10965
- var import_node_path38 = __toESM(require("path"), 1);
11094
+ var import_node_path39 = __toESM(require("path"), 1);
10966
11095
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
10967
11096
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
10968
11097
  var import_types27 = require("@neat.is/types");
@@ -11240,7 +11369,7 @@ async function addRouteCallEdges(graph, services) {
11240
11369
  const seen = /* @__PURE__ */ new Set();
11241
11370
  for (const file of files) {
11242
11371
  if (isTestPath(file.path)) continue;
11243
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
11372
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path39.default.extname(file.path))) continue;
11244
11373
  let sites;
11245
11374
  try {
11246
11375
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -11249,7 +11378,7 @@ async function addRouteCallEdges(graph, services) {
11249
11378
  continue;
11250
11379
  }
11251
11380
  if (sites.length === 0) continue;
11252
- const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
11381
+ const relFile = toPosix(import_node_path39.default.relative(service.dir, file.path));
11253
11382
  for (const site of sites) {
11254
11383
  if (site.host === null) {
11255
11384
  const dedupKey2 = `${relFile}|unresolved-host`;
@@ -11342,7 +11471,7 @@ async function addRouteCallEdges(graph, services) {
11342
11471
 
11343
11472
  // src/extract/calls/kafka.ts
11344
11473
  init_cjs_shims();
11345
- var import_node_path39 = __toESM(require("path"), 1);
11474
+ var import_node_path40 = __toESM(require("path"), 1);
11346
11475
  var import_types28 = require("@neat.is/types");
11347
11476
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
11348
11477
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
@@ -11430,13 +11559,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
11430
11559
  // call sites — verified-call-site tier (ADR-066).
11431
11560
  confidenceKind: "verified-call-site",
11432
11561
  evidence: {
11433
- file: import_node_path39.default.relative(serviceDir, file.path),
11562
+ file: import_node_path40.default.relative(serviceDir, file.path),
11434
11563
  line,
11435
11564
  snippet: snippet(file.content, line)
11436
11565
  }
11437
11566
  });
11438
11567
  };
11439
- if (import_node_path39.default.extname(file.path) === ".go") {
11568
+ if (import_node_path40.default.extname(file.path) === ".go") {
11440
11569
  goSaramaEndpoints(file.content, make);
11441
11570
  } else {
11442
11571
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -11447,7 +11576,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
11447
11576
 
11448
11577
  // src/extract/calls/redis.ts
11449
11578
  init_cjs_shims();
11450
- var import_node_path40 = __toESM(require("path"), 1);
11579
+ var import_node_path41 = __toESM(require("path"), 1);
11451
11580
  var import_types29 = require("@neat.is/types");
11452
11581
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
11453
11582
  function redisEndpointsFromFile(file, serviceDir) {
@@ -11470,7 +11599,7 @@ function redisEndpointsFromFile(file, serviceDir) {
11470
11599
  // support tier (ADR-066).
11471
11600
  confidenceKind: "url-with-structural-support",
11472
11601
  evidence: {
11473
- file: import_node_path40.default.relative(serviceDir, file.path),
11602
+ file: import_node_path41.default.relative(serviceDir, file.path),
11474
11603
  line,
11475
11604
  snippet: snippet(file.content, line)
11476
11605
  }
@@ -11481,7 +11610,7 @@ function redisEndpointsFromFile(file, serviceDir) {
11481
11610
 
11482
11611
  // src/extract/calls/aws.ts
11483
11612
  init_cjs_shims();
11484
- var import_node_path41 = __toESM(require("path"), 1);
11613
+ var import_node_path42 = __toESM(require("path"), 1);
11485
11614
  var import_types30 = require("@neat.is/types");
11486
11615
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
11487
11616
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -11515,7 +11644,7 @@ function awsEndpointsFromFile(file, serviceDir) {
11515
11644
  // (ADR-066).
11516
11645
  confidenceKind: "verified-call-site",
11517
11646
  evidence: {
11518
- file: import_node_path41.default.relative(serviceDir, file.path),
11647
+ file: import_node_path42.default.relative(serviceDir, file.path),
11519
11648
  line,
11520
11649
  snippet: snippet(file.content, line)
11521
11650
  }
@@ -11540,7 +11669,7 @@ function awsEndpointsFromFile(file, serviceDir) {
11540
11669
 
11541
11670
  // src/extract/calls/grpc.ts
11542
11671
  init_cjs_shims();
11543
- var import_node_path42 = __toESM(require("path"), 1);
11672
+ var import_node_path43 = __toESM(require("path"), 1);
11544
11673
  var import_types31 = require("@neat.is/types");
11545
11674
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
11546
11675
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -11599,7 +11728,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11599
11728
  // tier (ADR-066).
11600
11729
  confidenceKind: "verified-call-site",
11601
11730
  evidence: {
11602
- file: import_node_path42.default.relative(serviceDir, file.path),
11731
+ file: import_node_path43.default.relative(serviceDir, file.path),
11603
11732
  line,
11604
11733
  snippet: snippet(file.content, line)
11605
11734
  }
@@ -11610,7 +11739,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11610
11739
 
11611
11740
  // src/extract/calls/supabase.ts
11612
11741
  init_cjs_shims();
11613
- var import_node_path43 = __toESM(require("path"), 1);
11742
+ var import_node_path44 = __toESM(require("path"), 1);
11614
11743
  var import_types32 = require("@neat.is/types");
11615
11744
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
11616
11745
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -11669,7 +11798,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11669
11798
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
11670
11799
  confidenceKind: "verified-call-site",
11671
11800
  evidence: {
11672
- file: import_node_path43.default.relative(serviceDir, file.path),
11801
+ file: import_node_path44.default.relative(serviceDir, file.path),
11673
11802
  line,
11674
11803
  snippet: snippet(file.content, line)
11675
11804
  }
@@ -11696,7 +11825,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11696
11825
  edgeType: "CALLS",
11697
11826
  confidenceKind: "verified-call-site",
11698
11827
  evidence: {
11699
- file: import_node_path43.default.relative(serviceDir, file.path),
11828
+ file: import_node_path44.default.relative(serviceDir, file.path),
11700
11829
  line,
11701
11830
  snippet: snippet(file.content, line)
11702
11831
  }
@@ -11708,7 +11837,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11708
11837
 
11709
11838
  // src/extract/calls/firestore.ts
11710
11839
  init_cjs_shims();
11711
- var import_node_path44 = __toESM(require("path"), 1);
11840
+ var import_node_path45 = __toESM(require("path"), 1);
11712
11841
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
11713
11842
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
11714
11843
  var import_types33 = require("@neat.is/types");
@@ -11881,7 +12010,7 @@ function firestoreEndpointsFromFile(file, serviceDir, imported) {
11881
12010
  if (!hasClient && !hasAdmin && importedNames.size === 0) return [];
11882
12011
  const ownSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11883
12012
  const fileSdk = !hasClient && !hasAdmin ? imported?.sdk ?? null : ownSdk;
11884
- const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
12013
+ const tree = parseSource3(parserForExt2(import_node_path45.default.extname(file.path)), file.content);
11885
12014
  const clientVars = firestoreClientVars(tree.rootNode);
11886
12015
  for (const n of importedNames) clientVars.add(n);
11887
12016
  const collLine = /* @__PURE__ */ new Map();
@@ -11986,7 +12115,7 @@ function firestoreEndpointsFromFile(file, serviceDir, imported) {
11986
12115
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
11987
12116
  ...sdkWrites ? { sdkWrites } : {},
11988
12117
  evidence: {
11989
- file: import_node_path44.default.relative(serviceDir, file.path),
12118
+ file: import_node_path45.default.relative(serviceDir, file.path),
11990
12119
  line,
11991
12120
  snippet: snippet(file.content, line)
11992
12121
  }
@@ -12046,7 +12175,7 @@ function sdkOf(content) {
12046
12175
  return hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
12047
12176
  }
12048
12177
  function parseOrNull(file) {
12049
- const ext = import_node_path44.default.extname(file.path);
12178
+ const ext = import_node_path45.default.extname(file.path);
12050
12179
  if (!(ext in GRAMMAR_BY_EXT)) return null;
12051
12180
  try {
12052
12181
  return parseSource3(parserForExt2(ext), file.content).rootNode;
@@ -12062,7 +12191,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
12062
12191
  if (!root) continue;
12063
12192
  const names = exportedFirestoreClients(root);
12064
12193
  if (names.size === 0) continue;
12065
- registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), { names, sdk: sdkOf(f.content) });
12194
+ registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), { names, sdk: sdkOf(f.content) });
12066
12195
  }
12067
12196
  if (registry.size === 0) return [];
12068
12197
  const out = [];
@@ -12075,7 +12204,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
12075
12204
  const names = /* @__PURE__ */ new Set();
12076
12205
  let sdk = null;
12077
12206
  for (const b of bindings) {
12078
- const rel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
12207
+ const rel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
12079
12208
  if (!rel) continue;
12080
12209
  const entry = registry.get(rel);
12081
12210
  if (!entry || !entry.names.has(b.imported)) continue;
@@ -12090,7 +12219,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
12090
12219
 
12091
12220
  // src/extract/calls/mongoose.ts
12092
12221
  init_cjs_shims();
12093
- var import_node_path45 = __toESM(require("path"), 1);
12222
+ var import_node_path46 = __toESM(require("path"), 1);
12094
12223
  var import_types34 = require("@neat.is/types");
12095
12224
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
12096
12225
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -12241,7 +12370,7 @@ function endpoint(r, file, serviceDir, matchText) {
12241
12370
  kind: r.kind,
12242
12371
  edgeType: "CALLS",
12243
12372
  confidenceKind: "verified-call-site",
12244
- evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12373
+ evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12245
12374
  };
12246
12375
  }
12247
12376
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -12331,7 +12460,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
12331
12460
  const registry = /* @__PURE__ */ new Map();
12332
12461
  for (const f of mongooseFiles) {
12333
12462
  const fx = fileExportsOf(f.content, pluralizeOn);
12334
- if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
12463
+ if (fx) registry.set(toPosix(import_node_path46.default.relative(serviceDir, f.path)), fx);
12335
12464
  }
12336
12465
  if (registry.size === 0) return [];
12337
12466
  const out = [];
@@ -12342,7 +12471,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
12342
12471
  const directColl = /* @__PURE__ */ new Map();
12343
12472
  const nsExports = /* @__PURE__ */ new Map();
12344
12473
  for (const b of bindings) {
12345
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
12474
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path46.default.dirname(f.path), serviceDir, null);
12346
12475
  if (!resolvedRel) continue;
12347
12476
  const fx = registry.get(resolvedRel);
12348
12477
  if (!fx) continue;
@@ -12386,7 +12515,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
12386
12515
 
12387
12516
  // src/extract/calls/sqlalchemy.ts
12388
12517
  init_cjs_shims();
12389
- var import_node_path46 = __toESM(require("path"), 1);
12518
+ var import_node_path47 = __toESM(require("path"), 1);
12390
12519
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
12391
12520
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
12392
12521
  var import_types35 = require("@neat.is/types");
@@ -12525,7 +12654,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
12525
12654
  childTable,
12526
12655
  parentTable,
12527
12656
  evidence: {
12528
- file: import_node_path46.default.relative(serviceDir, file.path),
12657
+ file: import_node_path47.default.relative(serviceDir, file.path),
12529
12658
  line,
12530
12659
  snippet: snippet(file.content, line)
12531
12660
  }
@@ -12550,7 +12679,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
12550
12679
  confidenceKind: "verified-call-site",
12551
12680
  ...columns && columns.length > 0 ? { columns } : {},
12552
12681
  evidence: {
12553
- file: import_node_path46.default.relative(serviceDir, file.path),
12682
+ file: import_node_path47.default.relative(serviceDir, file.path),
12554
12683
  line,
12555
12684
  snippet: snippet(file.content, line)
12556
12685
  }
@@ -12657,7 +12786,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12657
12786
  edgeType: "CALLS",
12658
12787
  confidenceKind: "verified-call-site",
12659
12788
  evidence: {
12660
- file: import_node_path46.default.relative(serviceDir, file.path),
12789
+ file: import_node_path47.default.relative(serviceDir, file.path),
12661
12790
  line,
12662
12791
  snippet: snippet(file.content, line)
12663
12792
  }
@@ -12669,7 +12798,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12669
12798
 
12670
12799
  // src/extract/calls/socket.ts
12671
12800
  init_cjs_shims();
12672
- var import_node_path47 = __toESM(require("path"), 1);
12801
+ var import_node_path48 = __toESM(require("path"), 1);
12673
12802
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
12674
12803
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
12675
12804
  var import_types36 = require("@neat.is/types");
@@ -12745,7 +12874,7 @@ function socketEndpointsFromFile(file, serviceDir) {
12745
12874
  // A verified `socket.socket(...)` + `.connect((host, port))` call site.
12746
12875
  confidenceKind: "verified-call-site",
12747
12876
  evidence: {
12748
- file: import_node_path47.default.relative(serviceDir, file.path),
12877
+ file: import_node_path48.default.relative(serviceDir, file.path),
12749
12878
  line,
12750
12879
  snippet: snippet(file.content, line)
12751
12880
  }
@@ -12756,7 +12885,7 @@ function socketEndpointsFromFile(file, serviceDir) {
12756
12885
 
12757
12886
  // src/extract/calls/django-orm.ts
12758
12887
  init_cjs_shims();
12759
- var import_node_path48 = __toESM(require("path"), 1);
12888
+ var import_node_path49 = __toESM(require("path"), 1);
12760
12889
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12761
12890
  var import_tree_sitter_python7 = __toESM(require("tree-sitter-python"), 1);
12762
12891
  var import_types37 = require("@neat.is/types");
@@ -12826,7 +12955,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12826
12955
  const tree = parseSource8(makePyParser5(), file.content);
12827
12956
  const out = [];
12828
12957
  const seen = /* @__PURE__ */ new Set();
12829
- const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
12958
+ const defaultAppLabel = import_node_path49.default.basename(import_node_path49.default.dirname(file.path));
12830
12959
  walk5(tree.rootNode, (node) => {
12831
12960
  if (node.type !== "class_definition") return;
12832
12961
  if (!extendsDjangoModel(node)) return;
@@ -12844,7 +12973,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12844
12973
  kind: "sql-table",
12845
12974
  edgeType: "CALLS",
12846
12975
  confidenceKind: "verified-call-site",
12847
- evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12976
+ evidence: { file: import_node_path49.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12848
12977
  });
12849
12978
  });
12850
12979
  return out;
@@ -12852,7 +12981,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12852
12981
 
12853
12982
  // src/extract/calls/drizzle.ts
12854
12983
  init_cjs_shims();
12855
- var import_node_path49 = __toESM(require("path"), 1);
12984
+ var import_node_path50 = __toESM(require("path"), 1);
12856
12985
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12857
12986
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12858
12987
  var import_types38 = require("@neat.is/types");
@@ -12930,7 +13059,7 @@ function columnsFromObject(obj) {
12930
13059
  }
12931
13060
  function drizzleEndpointsFromFile(file, serviceDir) {
12932
13061
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12933
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
13062
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12934
13063
  const out = [];
12935
13064
  const seen = /* @__PURE__ */ new Set();
12936
13065
  const walk11 = (node) => {
@@ -12953,7 +13082,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12953
13082
  confidenceKind: "structural",
12954
13083
  columns,
12955
13084
  evidence: {
12956
- file: import_node_path49.default.relative(serviceDir, file.path),
13085
+ file: import_node_path50.default.relative(serviceDir, file.path),
12957
13086
  line,
12958
13087
  snippet: snippet(file.content, line)
12959
13088
  }
@@ -13019,7 +13148,7 @@ function referencesTargetVar(call) {
13019
13148
  }
13020
13149
  function drizzleForeignKeys(file, serviceDir) {
13021
13150
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
13022
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
13151
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
13023
13152
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
13024
13153
  const out = [];
13025
13154
  const seen = /* @__PURE__ */ new Set();
@@ -13038,7 +13167,7 @@ function drizzleForeignKeys(file, serviceDir) {
13038
13167
  childTable: table.tableName,
13039
13168
  parentTable,
13040
13169
  evidence: {
13041
- file: import_node_path49.default.relative(serviceDir, file.path),
13170
+ file: import_node_path50.default.relative(serviceDir, file.path),
13042
13171
  line,
13043
13172
  snippet: snippet(file.content, line)
13044
13173
  }
@@ -13055,7 +13184,7 @@ function drizzleForeignKeys(file, serviceDir) {
13055
13184
 
13056
13185
  // src/extract/calls/prisma.ts
13057
13186
  init_cjs_shims();
13058
- var import_node_path50 = __toESM(require("path"), 1);
13187
+ var import_node_path51 = __toESM(require("path"), 1);
13059
13188
  var import_types39 = require("@neat.is/types");
13060
13189
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
13061
13190
  "Int",
@@ -13111,7 +13240,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
13111
13240
  confidenceKind: "structural",
13112
13241
  columns: b.columns,
13113
13242
  evidence: {
13114
- file: import_node_path50.default.relative(serviceDir, file.path),
13243
+ file: import_node_path51.default.relative(serviceDir, file.path),
13115
13244
  line: b.startLine,
13116
13245
  snippet: snippet(content, b.startLine)
13117
13246
  }
@@ -13172,7 +13301,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
13172
13301
  }
13173
13302
  async function prismaColumnEndpoints(serviceDir) {
13174
13303
  const schemaPath = await findFirst(serviceDir, [
13175
- import_node_path50.default.join("prisma", "schema.prisma"),
13304
+ import_node_path51.default.join("prisma", "schema.prisma"),
13176
13305
  "schema.prisma"
13177
13306
  ]);
13178
13307
  if (!schemaPath) return [];
@@ -13247,7 +13376,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
13247
13376
  childTable: current.table,
13248
13377
  parentTable,
13249
13378
  evidence: {
13250
- file: import_node_path50.default.relative(serviceDir, file.path),
13379
+ file: import_node_path51.default.relative(serviceDir, file.path),
13251
13380
  line: lineNo,
13252
13381
  snippet: snippet(content, lineNo)
13253
13382
  }
@@ -13262,7 +13391,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
13262
13391
  }
13263
13392
  async function prismaForeignKeys(serviceDir) {
13264
13393
  const schemaPath = await findFirst(serviceDir, [
13265
- import_node_path50.default.join("prisma", "schema.prisma"),
13394
+ import_node_path51.default.join("prisma", "schema.prisma"),
13266
13395
  "schema.prisma"
13267
13396
  ]);
13268
13397
  if (!schemaPath) return [];
@@ -13273,7 +13402,7 @@ async function prismaForeignKeys(serviceDir) {
13273
13402
 
13274
13403
  // src/extract/calls/activerecord.ts
13275
13404
  init_cjs_shims();
13276
- var import_node_path51 = __toESM(require("path"), 1);
13405
+ var import_node_path52 = __toESM(require("path"), 1);
13277
13406
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
13278
13407
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
13279
13408
  var import_types40 = require("@neat.is/types");
@@ -13481,7 +13610,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
13481
13610
  confidenceKind: "structural",
13482
13611
  ...table.columns.length > 0 ? { columns: table.columns } : {},
13483
13612
  evidence: {
13484
- file: import_node_path51.default.relative(serviceDir, file.path),
13613
+ file: import_node_path52.default.relative(serviceDir, file.path),
13485
13614
  line: table.line,
13486
13615
  snippet: snippet(file.content, table.line)
13487
13616
  }
@@ -13503,7 +13632,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
13503
13632
  childTable,
13504
13633
  parentTable,
13505
13634
  evidence: {
13506
- file: import_node_path51.default.relative(serviceDir, file.path),
13635
+ file: import_node_path52.default.relative(serviceDir, file.path),
13507
13636
  line,
13508
13637
  snippet: snippet(file.content, line)
13509
13638
  }
@@ -13586,7 +13715,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
13586
13715
  edgeType: "CALLS",
13587
13716
  confidenceKind: "verified-call-site",
13588
13717
  evidence: {
13589
- file: import_node_path51.default.relative(serviceDir, file.path),
13718
+ file: import_node_path52.default.relative(serviceDir, file.path),
13590
13719
  line,
13591
13720
  snippet: snippet(file.content, line)
13592
13721
  }
@@ -13623,7 +13752,7 @@ function railsModelForeignKeys(file, serviceDir) {
13623
13752
  childTable,
13624
13753
  parentTable,
13625
13754
  evidence: {
13626
- file: import_node_path51.default.relative(serviceDir, file.path),
13755
+ file: import_node_path52.default.relative(serviceDir, file.path),
13627
13756
  line,
13628
13757
  snippet: snippet(file.content, line)
13629
13758
  }
@@ -13635,7 +13764,7 @@ function railsModelForeignKeys(file, serviceDir) {
13635
13764
 
13636
13765
  // src/extract/calls/eloquent.ts
13637
13766
  init_cjs_shims();
13638
- var import_node_path52 = __toESM(require("path"), 1);
13767
+ var import_node_path53 = __toESM(require("path"), 1);
13639
13768
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13640
13769
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
13641
13770
  var import_types41 = require("@neat.is/types");
@@ -13892,7 +14021,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13892
14021
  confidenceKind: "structural",
13893
14022
  ...columns.length > 0 ? { columns } : {},
13894
14023
  evidence: {
13895
- file: import_node_path52.default.relative(serviceDir, file.path),
14024
+ file: import_node_path53.default.relative(serviceDir, file.path),
13896
14025
  line: bp.line,
13897
14026
  snippet: snippet(file.content, bp.line)
13898
14027
  }
@@ -13914,7 +14043,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13914
14043
  childTable,
13915
14044
  parentTable,
13916
14045
  evidence: {
13917
- file: import_node_path52.default.relative(serviceDir, file.path),
14046
+ file: import_node_path53.default.relative(serviceDir, file.path),
13918
14047
  line,
13919
14048
  snippet: snippet(file.content, line)
13920
14049
  }
@@ -14034,7 +14163,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
14034
14163
  edgeType: "CALLS",
14035
14164
  confidenceKind: "verified-call-site",
14036
14165
  evidence: {
14037
- file: import_node_path52.default.relative(serviceDir, file.path),
14166
+ file: import_node_path53.default.relative(serviceDir, file.path),
14038
14167
  line,
14039
14168
  snippet: snippet(file.content, line)
14040
14169
  }
@@ -14070,7 +14199,7 @@ function laravelModelForeignKeys(file, serviceDir) {
14070
14199
  childTable,
14071
14200
  parentTable,
14072
14201
  evidence: {
14073
- file: import_node_path52.default.relative(serviceDir, file.path),
14202
+ file: import_node_path53.default.relative(serviceDir, file.path),
14074
14203
  line,
14075
14204
  snippet: snippet(file.content, line)
14076
14205
  }
@@ -14082,7 +14211,7 @@ function laravelModelForeignKeys(file, serviceDir) {
14082
14211
 
14083
14212
  // src/extract/calls/go.ts
14084
14213
  init_cjs_shims();
14085
- var import_node_path55 = __toESM(require("path"), 1);
14214
+ var import_node_path56 = __toESM(require("path"), 1);
14086
14215
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
14087
14216
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
14088
14217
  var import_types42 = require("@neat.is/types");
@@ -14157,7 +14286,7 @@ function firstStringLiteralArg(argsNode) {
14157
14286
  return null;
14158
14287
  }
14159
14288
  function goSqlEndpointsFromFile(file, serviceDir) {
14160
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14289
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
14161
14290
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
14162
14291
  const tree = parseSource11(makeGoParser3(), file.content);
14163
14292
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -14186,7 +14315,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
14186
14315
  confidenceKind: "verified-call-site",
14187
14316
  ...columns.length > 0 ? { columns } : {},
14188
14317
  evidence: {
14189
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14318
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14190
14319
  line,
14191
14320
  snippet: snippet(file.content, line)
14192
14321
  }
@@ -14197,7 +14326,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
14197
14326
 
14198
14327
  // src/extract/calls/gorm.ts
14199
14328
  init_cjs_shims();
14200
- var import_node_path56 = __toESM(require("path"), 1);
14329
+ var import_node_path57 = __toESM(require("path"), 1);
14201
14330
  var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14202
14331
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
14203
14332
  var import_types43 = require("@neat.is/types");
@@ -14630,7 +14759,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
14630
14759
  seen.delete(struct.name);
14631
14760
  }
14632
14761
  function gormEndpointsFromFile(file, serviceDir) {
14633
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14762
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
14634
14763
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14635
14764
  const tree = parseSource12(makeGoParser4(), file.content);
14636
14765
  const { structs, models, tableFor } = analyze(tree);
@@ -14652,7 +14781,7 @@ function gormEndpointsFromFile(file, serviceDir) {
14652
14781
  confidenceKind: "structural",
14653
14782
  ...columns.length > 0 ? { columns } : {},
14654
14783
  evidence: {
14655
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14784
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14656
14785
  line: struct.line,
14657
14786
  snippet: snippet(file.content, struct.line)
14658
14787
  }
@@ -14661,7 +14790,7 @@ function gormEndpointsFromFile(file, serviceDir) {
14661
14790
  return out;
14662
14791
  }
14663
14792
  function gormForeignKeys(file, serviceDir) {
14664
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14793
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
14665
14794
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14666
14795
  const tree = parseSource12(makeGoParser4(), file.content);
14667
14796
  const { structs, models, tableFor } = analyze(tree);
@@ -14676,7 +14805,7 @@ function gormForeignKeys(file, serviceDir) {
14676
14805
  childTable,
14677
14806
  parentTable,
14678
14807
  evidence: {
14679
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14808
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14680
14809
  line,
14681
14810
  snippet: snippet(file.content, line)
14682
14811
  }
@@ -14713,7 +14842,7 @@ function gormForeignKeys(file, serviceDir) {
14713
14842
 
14714
14843
  // src/extract/calls/efcore.ts
14715
14844
  init_cjs_shims();
14716
- var import_node_path57 = __toESM(require("path"), 1);
14845
+ var import_node_path58 = __toESM(require("path"), 1);
14717
14846
  var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14718
14847
  var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14719
14848
  var import_types44 = require("@neat.is/types");
@@ -14792,7 +14921,7 @@ function tableFromToTable(call) {
14792
14921
  return value ? csStringLiteral(value) : null;
14793
14922
  }
14794
14923
  function efcoreEndpointsFromFile(file, serviceDir) {
14795
- if (import_node_path57.default.extname(file.path) !== ".cs") return [];
14924
+ if (import_node_path58.default.extname(file.path) !== ".cs") return [];
14796
14925
  if (!EFCORE_GATE.test(file.content)) return [];
14797
14926
  const tree = parseSource13(makeCsParser(), file.content);
14798
14927
  const out = [];
@@ -14807,7 +14936,7 @@ function efcoreEndpointsFromFile(file, serviceDir) {
14807
14936
  edgeType: "CALLS",
14808
14937
  confidenceKind: "structural",
14809
14938
  evidence: {
14810
- file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14939
+ file: toPosix(import_node_path58.default.relative(serviceDir, file.path)),
14811
14940
  line,
14812
14941
  snippet: snippet(file.content, line)
14813
14942
  }
@@ -15062,7 +15191,7 @@ init_cjs_shims();
15062
15191
 
15063
15192
  // src/extract/infra/docker-compose.ts
15064
15193
  init_cjs_shims();
15065
- var import_node_path58 = __toESM(require("path"), 1);
15194
+ var import_node_path59 = __toESM(require("path"), 1);
15066
15195
  var import_types48 = require("@neat.is/types");
15067
15196
 
15068
15197
  // src/extract/infra/shared.ts
@@ -15133,7 +15262,7 @@ function dependsOnList(value) {
15133
15262
  }
15134
15263
  function serviceNameToServiceNode(name, services) {
15135
15264
  for (const s of services) {
15136
- if (s.node.name === name || import_node_path58.default.basename(s.dir) === name) return s.node.id;
15265
+ if (s.node.name === name || import_node_path59.default.basename(s.dir) === name) return s.node.id;
15137
15266
  }
15138
15267
  return null;
15139
15268
  }
@@ -15142,7 +15271,7 @@ async function addComposeInfra(graph, scanPath, services) {
15142
15271
  let edgesAdded = 0;
15143
15272
  let composePath = null;
15144
15273
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
15145
- const abs = import_node_path58.default.join(scanPath, name);
15274
+ const abs = import_node_path59.default.join(scanPath, name);
15146
15275
  if (await exists(abs)) {
15147
15276
  composePath = abs;
15148
15277
  break;
@@ -15155,13 +15284,13 @@ async function addComposeInfra(graph, scanPath, services) {
15155
15284
  } catch (err) {
15156
15285
  recordExtractionError(
15157
15286
  "infra docker-compose",
15158
- import_node_path58.default.relative(scanPath, composePath),
15287
+ import_node_path59.default.relative(scanPath, composePath),
15159
15288
  err
15160
15289
  );
15161
15290
  return { nodesAdded, edgesAdded };
15162
15291
  }
15163
15292
  if (!compose?.services) return { nodesAdded, edgesAdded };
15164
- const evidenceFile = import_node_path58.default.relative(scanPath, composePath).split(import_node_path58.default.sep).join("/");
15293
+ const evidenceFile = import_node_path59.default.relative(scanPath, composePath).split(import_node_path59.default.sep).join("/");
15165
15294
  const composeNameToNodeId = /* @__PURE__ */ new Map();
15166
15295
  for (const [composeName, svc] of Object.entries(compose.services)) {
15167
15296
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -15203,8 +15332,8 @@ async function addComposeInfra(graph, scanPath, services) {
15203
15332
 
15204
15333
  // src/extract/infra/dockerfile.ts
15205
15334
  init_cjs_shims();
15206
- var import_node_path59 = __toESM(require("path"), 1);
15207
- var import_node_fs25 = require("fs");
15335
+ var import_node_path60 = __toESM(require("path"), 1);
15336
+ var import_node_fs26 = require("fs");
15208
15337
  var import_types49 = require("@neat.is/types");
15209
15338
  function readDockerfile(content) {
15210
15339
  let image = null;
@@ -15234,15 +15363,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15234
15363
  let nodesAdded = 0;
15235
15364
  let edgesAdded = 0;
15236
15365
  for (const service of services) {
15237
- const dockerfilePath = import_node_path59.default.join(service.dir, "Dockerfile");
15366
+ const dockerfilePath = import_node_path60.default.join(service.dir, "Dockerfile");
15238
15367
  if (!await exists(dockerfilePath)) continue;
15239
15368
  let content;
15240
15369
  try {
15241
- content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
15370
+ content = await import_node_fs26.promises.readFile(dockerfilePath, "utf8");
15242
15371
  } catch (err) {
15243
15372
  recordExtractionError(
15244
15373
  "infra dockerfile",
15245
- import_node_path59.default.relative(scanPath, dockerfilePath),
15374
+ import_node_path60.default.relative(scanPath, dockerfilePath),
15246
15375
  err
15247
15376
  );
15248
15377
  continue;
@@ -15254,8 +15383,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15254
15383
  graph.addNode(node.id, node);
15255
15384
  nodesAdded++;
15256
15385
  }
15257
- const relDockerfile = toPosix(import_node_path59.default.relative(service.dir, dockerfilePath));
15258
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, dockerfilePath));
15386
+ const relDockerfile = toPosix(import_node_path60.default.relative(service.dir, dockerfilePath));
15387
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, dockerfilePath));
15259
15388
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
15260
15389
  graph,
15261
15390
  service.pkg.name,
@@ -15307,23 +15436,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15307
15436
 
15308
15437
  // src/extract/infra/terraform.ts
15309
15438
  init_cjs_shims();
15310
- var import_node_fs26 = require("fs");
15311
- var import_node_path60 = __toESM(require("path"), 1);
15439
+ var import_node_fs27 = require("fs");
15440
+ var import_node_path61 = __toESM(require("path"), 1);
15312
15441
  var import_types50 = require("@neat.is/types");
15313
15442
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
15314
15443
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
15315
15444
  async function walkTfFiles(start, depth = 0, max = 5) {
15316
15445
  if (depth > max) return [];
15317
15446
  const out = [];
15318
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15447
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15319
15448
  for (const entry of entries) {
15320
15449
  if (entry.isDirectory()) {
15321
15450
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
15322
- const child = import_node_path60.default.join(start, entry.name);
15451
+ const child = import_node_path61.default.join(start, entry.name);
15323
15452
  if (await isPythonVenvDir(child)) continue;
15324
15453
  out.push(...await walkTfFiles(child, depth + 1, max));
15325
15454
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
15326
- out.push(import_node_path60.default.join(start, entry.name));
15455
+ out.push(import_node_path61.default.join(start, entry.name));
15327
15456
  }
15328
15457
  }
15329
15458
  return out;
@@ -15354,8 +15483,8 @@ async function addTerraformResources(graph, scanPath) {
15354
15483
  let edgesAdded = 0;
15355
15484
  const files = await walkTfFiles(scanPath);
15356
15485
  for (const file of files) {
15357
- const content = await import_node_fs26.promises.readFile(file, "utf8");
15358
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, file));
15486
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
15487
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, file));
15359
15488
  const resources = [];
15360
15489
  const byKey = /* @__PURE__ */ new Map();
15361
15490
  RESOURCE_RE.lastIndex = 0;
@@ -15412,8 +15541,8 @@ async function addTerraformResources(graph, scanPath) {
15412
15541
 
15413
15542
  // src/extract/infra/k8s.ts
15414
15543
  init_cjs_shims();
15415
- var import_node_fs27 = require("fs");
15416
- var import_node_path61 = __toESM(require("path"), 1);
15544
+ var import_node_fs28 = require("fs");
15545
+ var import_node_path62 = __toESM(require("path"), 1);
15417
15546
  var import_yaml3 = require("yaml");
15418
15547
  var import_types51 = require("@neat.is/types");
15419
15548
  var K8S_KIND_TO_INFRA_KIND = {
@@ -15429,15 +15558,15 @@ var WORKLOAD_KINDS = /* @__PURE__ */ new Set(["Deployment", "StatefulSet", "Daem
15429
15558
  async function walkYamlFiles2(start, depth = 0, max = 5) {
15430
15559
  if (depth > max) return [];
15431
15560
  const out = [];
15432
- const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15561
+ const entries = await import_node_fs28.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15433
15562
  for (const entry of entries) {
15434
15563
  if (entry.isDirectory()) {
15435
15564
  if (IGNORED_DIRS.has(entry.name)) continue;
15436
- const child = import_node_path61.default.join(start, entry.name);
15565
+ const child = import_node_path62.default.join(start, entry.name);
15437
15566
  if (await isPythonVenvDir(child)) continue;
15438
15567
  out.push(...await walkYamlFiles2(child, depth + 1, max));
15439
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path61.default.extname(entry.name))) {
15440
- out.push(import_node_path61.default.join(start, entry.name));
15568
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path62.default.extname(entry.name))) {
15569
+ out.push(import_node_path62.default.join(start, entry.name));
15441
15570
  }
15442
15571
  }
15443
15572
  return out;
@@ -15447,14 +15576,14 @@ async function addK8sResources(graph, scanPath) {
15447
15576
  let edgesAdded = 0;
15448
15577
  const files = await walkYamlFiles2(scanPath);
15449
15578
  for (const file of files) {
15450
- const content = await import_node_fs27.promises.readFile(file, "utf8");
15579
+ const content = await import_node_fs28.promises.readFile(file, "utf8");
15451
15580
  let docs;
15452
15581
  try {
15453
15582
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
15454
15583
  } catch {
15455
15584
  continue;
15456
15585
  }
15457
- const relFile = toPosix(import_node_path61.default.relative(scanPath, file));
15586
+ const relFile = toPosix(import_node_path62.default.relative(scanPath, file));
15458
15587
  for (const doc of docs) {
15459
15588
  if (!doc?.kind || !doc.metadata?.name) continue;
15460
15589
  const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind];
@@ -15503,16 +15632,16 @@ async function addK8sResources(graph, scanPath) {
15503
15632
 
15504
15633
  // src/extract/infra/cloudflare.ts
15505
15634
  init_cjs_shims();
15506
- var import_node_fs28 = require("fs");
15507
- var import_node_path62 = __toESM(require("path"), 1);
15635
+ var import_node_fs29 = require("fs");
15636
+ var import_node_path63 = __toESM(require("path"), 1);
15508
15637
  var import_smol_toml3 = require("smol-toml");
15509
15638
  var import_types52 = require("@neat.is/types");
15510
15639
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
15511
15640
  async function readWranglerConfig(dir) {
15512
15641
  for (const filename of WRANGLER_FILENAMES) {
15513
- const abs = import_node_path62.default.join(dir, filename);
15642
+ const abs = import_node_path63.default.join(dir, filename);
15514
15643
  if (!await exists(abs)) continue;
15515
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
15644
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15516
15645
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
15517
15646
  return { config, relFile: filename, raw };
15518
15647
  }
@@ -15573,11 +15702,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15573
15702
  try {
15574
15703
  read = await readWranglerConfig(service.dir);
15575
15704
  } catch (err) {
15576
- recordExtractionError("infra cloudflare", import_node_path62.default.relative(scanPath, service.dir), err);
15705
+ recordExtractionError("infra cloudflare", import_node_path63.default.relative(scanPath, service.dir), err);
15577
15706
  continue;
15578
15707
  }
15579
15708
  if (!read || !read.config.name) continue;
15580
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, read.relFile)));
15709
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, read.relFile)));
15581
15710
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
15582
15711
  }
15583
15712
  for (const worker of discovered) {
@@ -15589,7 +15718,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15589
15718
  }
15590
15719
  let anchorId = service.node.id;
15591
15720
  if (config.main) {
15592
- const entryRelPath = toPosix(import_node_path62.default.normalize(config.main));
15721
+ const entryRelPath = toPosix(import_node_path63.default.normalize(config.main));
15593
15722
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
15594
15723
  graph,
15595
15724
  service.pkg.name,
@@ -15736,24 +15865,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15736
15865
 
15737
15866
  // src/extract/infra/vercel.ts
15738
15867
  init_cjs_shims();
15739
- var import_node_fs29 = require("fs");
15740
- var import_node_path63 = __toESM(require("path"), 1);
15868
+ var import_node_fs30 = require("fs");
15869
+ var import_node_path64 = __toESM(require("path"), 1);
15741
15870
  var import_types53 = require("@neat.is/types");
15742
15871
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
15743
15872
  async function readVercelConfig(dir) {
15744
15873
  for (const filename of VERCEL_CONFIG_FILENAMES) {
15745
- const abs = import_node_path63.default.join(dir, filename);
15874
+ const abs = import_node_path64.default.join(dir, filename);
15746
15875
  if (!await exists(abs)) continue;
15747
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15876
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15748
15877
  const config = JSON.parse(maskCommentsInSource(raw));
15749
15878
  return { config, relFile: filename, raw };
15750
15879
  }
15751
15880
  return null;
15752
15881
  }
15753
15882
  async function readLinkedProjectName(dir) {
15754
- const abs = import_node_path63.default.join(dir, ".vercel", "project.json");
15883
+ const abs = import_node_path64.default.join(dir, ".vercel", "project.json");
15755
15884
  if (!await exists(abs)) return void 0;
15756
- const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
15885
+ const parsed = JSON.parse(await import_node_fs30.promises.readFile(abs, "utf8"));
15757
15886
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
15758
15887
  }
15759
15888
  function routeSource(route) {
@@ -15769,7 +15898,7 @@ async function addVercelServices(graph, services, scanPath) {
15769
15898
  read = await readVercelConfig(service.dir);
15770
15899
  projectName = await readLinkedProjectName(service.dir);
15771
15900
  } catch (err) {
15772
- recordExtractionError("infra vercel", import_node_path63.default.relative(scanPath, service.dir), err);
15901
+ recordExtractionError("infra vercel", import_node_path64.default.relative(scanPath, service.dir), err);
15773
15902
  continue;
15774
15903
  }
15775
15904
  if (!read && !projectName) continue;
@@ -15785,7 +15914,7 @@ async function addVercelServices(graph, services, scanPath) {
15785
15914
  const anchorId = service.node.id;
15786
15915
  if (!read) continue;
15787
15916
  const { config, relFile, raw } = read;
15788
- const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15917
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15789
15918
  const add = (edgeType, kind, name) => {
15790
15919
  if (!name) return;
15791
15920
  const result = emitPlatformResourceEdge(
@@ -15814,16 +15943,16 @@ async function addVercelServices(graph, services, scanPath) {
15814
15943
 
15815
15944
  // src/extract/infra/railway.ts
15816
15945
  init_cjs_shims();
15817
- var import_node_fs30 = require("fs");
15818
- var import_node_path64 = __toESM(require("path"), 1);
15946
+ var import_node_fs31 = require("fs");
15947
+ var import_node_path65 = __toESM(require("path"), 1);
15819
15948
  var import_smol_toml4 = require("smol-toml");
15820
15949
  var import_types54 = require("@neat.is/types");
15821
15950
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
15822
15951
  async function readRailwayConfig(dir) {
15823
15952
  for (const filename of RAILWAY_FILENAMES) {
15824
- const abs = import_node_path64.default.join(dir, filename);
15953
+ const abs = import_node_path65.default.join(dir, filename);
15825
15954
  if (!await exists(abs)) continue;
15826
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15955
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15827
15956
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
15828
15957
  return { config, relFile: filename, raw };
15829
15958
  }
@@ -15837,7 +15966,7 @@ async function addRailwayServices(graph, services, scanPath) {
15837
15966
  try {
15838
15967
  read = await readRailwayConfig(service.dir);
15839
15968
  } catch (err) {
15840
- recordExtractionError("infra railway", import_node_path64.default.relative(scanPath, service.dir), err);
15969
+ recordExtractionError("infra railway", import_node_path65.default.relative(scanPath, service.dir), err);
15841
15970
  continue;
15842
15971
  }
15843
15972
  if (!read) continue;
@@ -15847,7 +15976,7 @@ async function addRailwayServices(graph, services, scanPath) {
15847
15976
  }
15848
15977
  const anchorId = service.node.id;
15849
15978
  const { config, relFile, raw } = read;
15850
- const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15979
+ const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
15851
15980
  const add = (edgeType, kind, name) => {
15852
15981
  if (!name) return;
15853
15982
  const result = emitPlatformResourceEdge(
@@ -15872,15 +16001,15 @@ async function addRailwayServices(graph, services, scanPath) {
15872
16001
 
15873
16002
  // src/extract/infra/supabase.ts
15874
16003
  init_cjs_shims();
15875
- var import_node_fs31 = require("fs");
15876
- var import_node_path65 = __toESM(require("path"), 1);
16004
+ var import_node_fs32 = require("fs");
16005
+ var import_node_path66 = __toESM(require("path"), 1);
15877
16006
  var import_smol_toml5 = require("smol-toml");
15878
16007
  var import_types55 = require("@neat.is/types");
15879
16008
  async function readSupabaseConfig(dir) {
15880
- const relFile = import_node_path65.default.join("supabase", "config.toml");
15881
- const abs = import_node_path65.default.join(dir, relFile);
16009
+ const relFile = import_node_path66.default.join("supabase", "config.toml");
16010
+ const abs = import_node_path66.default.join(dir, relFile);
15882
16011
  if (!await exists(abs)) return null;
15883
- const raw = await import_node_fs31.promises.readFile(abs, "utf8");
16012
+ const raw = await import_node_fs32.promises.readFile(abs, "utf8");
15884
16013
  const config = (0, import_smol_toml5.parse)(raw);
15885
16014
  return { config, relFile, raw };
15886
16015
  }
@@ -15892,7 +16021,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15892
16021
  try {
15893
16022
  read = await readSupabaseConfig(service.dir);
15894
16023
  } catch (err) {
15895
- recordExtractionError("infra supabase", import_node_path65.default.relative(scanPath, service.dir), err);
16024
+ recordExtractionError("infra supabase", import_node_path66.default.relative(scanPath, service.dir), err);
15896
16025
  continue;
15897
16026
  }
15898
16027
  if (!read) continue;
@@ -15907,7 +16036,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15907
16036
  });
15908
16037
  }
15909
16038
  const anchorId = service.node.id;
15910
- const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
16039
+ const evidenceFile = toPosix(import_node_path66.default.relative(scanPath, import_node_path66.default.join(service.dir, relFile)));
15911
16040
  const add = (edgeType, kind, name) => {
15912
16041
  if (!name) return;
15913
16042
  const result = emitPlatformResourceEdge(
@@ -15949,7 +16078,7 @@ async function addInfra(graph, scanPath, services) {
15949
16078
 
15950
16079
  // src/extract/zod-shapes.ts
15951
16080
  init_cjs_shims();
15952
- var import_node_path66 = __toESM(require("path"), 1);
16081
+ var import_node_path67 = __toESM(require("path"), 1);
15953
16082
  var import_tree_sitter18 = __toESM(require("tree-sitter"), 1);
15954
16083
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15955
16084
  var import_types56 = require("@neat.is/types");
@@ -16044,7 +16173,7 @@ function topLevelSchemas(root) {
16044
16173
  }
16045
16174
  function zodShapesFromFile(file, serviceDir) {
16046
16175
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
16047
- const tree = parseSource3(parserForExt4(import_node_path66.default.extname(file.path)), file.content);
16176
+ const tree = parseSource3(parserForExt4(import_node_path67.default.extname(file.path)), file.content);
16048
16177
  const out = [];
16049
16178
  const seen = /* @__PURE__ */ new Set();
16050
16179
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -16062,7 +16191,7 @@ function zodShapesFromFile(file, serviceDir) {
16062
16191
  name,
16063
16192
  fields,
16064
16193
  evidence: {
16065
- file: import_node_path66.default.relative(serviceDir, file.path),
16194
+ file: import_node_path67.default.relative(serviceDir, file.path),
16066
16195
  line,
16067
16196
  snippet: snippet(file.content, line)
16068
16197
  }
@@ -16330,12 +16459,12 @@ async function addFirestoreRules(graph, services) {
16330
16459
  }
16331
16460
 
16332
16461
  // src/extract/index.ts
16333
- var import_node_path68 = __toESM(require("path"), 1);
16462
+ var import_node_path69 = __toESM(require("path"), 1);
16334
16463
 
16335
16464
  // src/extract/retire.ts
16336
16465
  init_cjs_shims();
16337
- var import_node_fs32 = require("fs");
16338
- var import_node_path67 = __toESM(require("path"), 1);
16466
+ var import_node_fs33 = require("fs");
16467
+ var import_node_path68 = __toESM(require("path"), 1);
16339
16468
  var import_types58 = require("@neat.is/types");
16340
16469
  function dropOrphanedFileNodes(graph) {
16341
16470
  const orphans = [];
@@ -16356,11 +16485,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
16356
16485
  if (edge.provenance !== import_types58.Provenance.EXTRACTED) return;
16357
16486
  const evidenceFile = edge.evidence?.file;
16358
16487
  if (!evidenceFile) return;
16359
- if (import_node_path67.default.isAbsolute(evidenceFile)) {
16360
- if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
16488
+ if (import_node_path68.default.isAbsolute(evidenceFile)) {
16489
+ if (!(0, import_node_fs33.existsSync)(evidenceFile)) toDrop.push(id);
16361
16490
  return;
16362
16491
  }
16363
- const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path67.default.join(base, evidenceFile)));
16492
+ const found = bases.some((base) => (0, import_node_fs33.existsSync)(import_node_path68.default.join(base, evidenceFile)));
16364
16493
  if (!found) toDrop.push(id);
16365
16494
  });
16366
16495
  for (const id of toDrop) graph.dropEdge(id);
@@ -16372,6 +16501,7 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
16372
16501
  async function extractFromDirectory(graph, scanPath, opts = {}) {
16373
16502
  await ensureCompatLoaded();
16374
16503
  drainExtractionErrors();
16504
+ drainSkippedFiles();
16375
16505
  const services = await discoverServices(scanPath);
16376
16506
  const phase1Nodes = addServiceNodes(graph, services);
16377
16507
  await addServiceAliases(graph, scanPath, services);
@@ -16416,8 +16546,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16416
16546
  }
16417
16547
  }
16418
16548
  const droppedEntries = drainDroppedExtracted();
16549
+ const skippedEntries = drainSkippedFiles();
16419
16550
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
16420
- const rejectedPath = import_node_path68.default.join(import_node_path68.default.dirname(opts.errorsPath), "rejected.ndjson");
16551
+ const rejectedPath = import_node_path69.default.join(import_node_path69.default.dirname(opts.errorsPath), "rejected.ndjson");
16421
16552
  try {
16422
16553
  await writeRejectedExtracted(droppedEntries, rejectedPath);
16423
16554
  } catch (err) {
@@ -16434,7 +16565,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16434
16565
  errorEntries,
16435
16566
  ghostsRetired,
16436
16567
  extractedDropped: droppedEntries.length,
16437
- droppedEntries
16568
+ droppedEntries,
16569
+ skippedFiles: skippedEntries.length,
16570
+ skippedEntries
16438
16571
  };
16439
16572
  emitNeatEvent({
16440
16573
  type: "extraction-complete",
@@ -16451,8 +16584,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16451
16584
 
16452
16585
  // src/persist.ts
16453
16586
  init_cjs_shims();
16454
- var import_node_fs33 = require("fs");
16455
- var import_node_path69 = __toESM(require("path"), 1);
16587
+ var import_node_fs34 = require("fs");
16588
+ var import_node_path70 = __toESM(require("path"), 1);
16456
16589
  var import_types59 = require("@neat.is/types");
16457
16590
  var SCHEMA_VERSION = 7;
16458
16591
  function migrateV1ToV2(payload) {
@@ -16507,7 +16640,43 @@ function migrateV2ToV3(payload) {
16507
16640
  return { ...payload, schemaVersion: 3 };
16508
16641
  }
16509
16642
  async function ensureDir(filePath) {
16510
- await import_node_fs33.promises.mkdir(import_node_path69.default.dirname(filePath), { recursive: true });
16643
+ await import_node_fs34.promises.mkdir(import_node_path70.default.dirname(filePath), { recursive: true });
16644
+ }
16645
+ var WRITE_FLUSH_CHARS = 4 * 1024 * 1024;
16646
+ function jsonOrUndefined(value) {
16647
+ return JSON.stringify(value);
16648
+ }
16649
+ function* serializeArray(items) {
16650
+ yield "[";
16651
+ for (let i = 0; i < items.length; i++) {
16652
+ if (i > 0) yield ",";
16653
+ yield jsonOrUndefined(items[i]) ?? "null";
16654
+ }
16655
+ yield "]";
16656
+ }
16657
+ function isPlainObject(value) {
16658
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
16659
+ const proto = Object.getPrototypeOf(value);
16660
+ return proto === Object.prototype || proto === null;
16661
+ }
16662
+ function* serializeObject(obj) {
16663
+ yield "{";
16664
+ let first = true;
16665
+ for (const key of Object.keys(obj)) {
16666
+ const value = obj[key];
16667
+ const descend = Array.isArray(value) || isPlainObject(value);
16668
+ if (!descend && jsonOrUndefined(value) === void 0) continue;
16669
+ if (!first) yield ",";
16670
+ first = false;
16671
+ yield `${JSON.stringify(key)}:`;
16672
+ if (Array.isArray(value)) yield* serializeArray(value);
16673
+ else if (isPlainObject(value)) yield* serializeObject(value);
16674
+ else yield jsonOrUndefined(value);
16675
+ }
16676
+ yield "}";
16677
+ }
16678
+ function* serializeGraphChunks(payload) {
16679
+ yield* serializeObject(payload);
16511
16680
  }
16512
16681
  async function saveGraphToDisk(graph, outPath) {
16513
16682
  await ensureDir(outPath);
@@ -16517,13 +16686,26 @@ async function saveGraphToDisk(graph, outPath) {
16517
16686
  graph: graph.export()
16518
16687
  };
16519
16688
  const tmp = `${outPath}.tmp`;
16520
- await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
16521
- await import_node_fs33.promises.rename(tmp, outPath);
16689
+ const handle = await import_node_fs34.promises.open(tmp, "w");
16690
+ try {
16691
+ let buffered = "";
16692
+ for (const chunk of serializeGraphChunks(payload)) {
16693
+ buffered += chunk;
16694
+ if (buffered.length >= WRITE_FLUSH_CHARS) {
16695
+ await handle.write(buffered, null, "utf8");
16696
+ buffered = "";
16697
+ }
16698
+ }
16699
+ if (buffered.length > 0) await handle.write(buffered, null, "utf8");
16700
+ } finally {
16701
+ await handle.close();
16702
+ }
16703
+ await import_node_fs34.promises.rename(tmp, outPath);
16522
16704
  }
16523
16705
  async function loadGraphFromDisk(graph, outPath) {
16524
16706
  let raw;
16525
16707
  try {
16526
- raw = await import_node_fs33.promises.readFile(outPath, "utf8");
16708
+ raw = await import_node_fs34.promises.readFile(outPath, "utf8");
16527
16709
  } catch (err) {
16528
16710
  if (err.code === "ENOENT") return;
16529
16711
  throw err;
@@ -16603,15 +16785,15 @@ var import_types101 = require("@neat.is/types");
16603
16785
 
16604
16786
  // src/extend/index.ts
16605
16787
  init_cjs_shims();
16606
- var import_node_fs35 = require("fs");
16607
- var import_node_path71 = __toESM(require("path"), 1);
16788
+ var import_node_fs36 = require("fs");
16789
+ var import_node_path72 = __toESM(require("path"), 1);
16608
16790
  var import_node_os2 = __toESM(require("os"), 1);
16609
16791
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
16610
16792
 
16611
16793
  // src/installers/package-manager.ts
16612
16794
  init_cjs_shims();
16613
- var import_node_fs34 = require("fs");
16614
- var import_node_path70 = __toESM(require("path"), 1);
16795
+ var import_node_fs35 = require("fs");
16796
+ var import_node_path71 = __toESM(require("path"), 1);
16615
16797
  var import_node_child_process = require("child_process");
16616
16798
  var LOCKFILE_PRIORITY = [
16617
16799
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -16626,29 +16808,29 @@ var LOCKFILE_PRIORITY = [
16626
16808
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
16627
16809
  async function exists2(p) {
16628
16810
  try {
16629
- await import_node_fs34.promises.access(p);
16811
+ await import_node_fs35.promises.access(p);
16630
16812
  return true;
16631
16813
  } catch {
16632
16814
  return false;
16633
16815
  }
16634
16816
  }
16635
16817
  async function detectPackageManager(serviceDir) {
16636
- let dir = import_node_path70.default.resolve(serviceDir);
16818
+ let dir = import_node_path71.default.resolve(serviceDir);
16637
16819
  const stops = /* @__PURE__ */ new Set();
16638
16820
  for (let i = 0; i < 64; i++) {
16639
16821
  if (stops.has(dir)) break;
16640
16822
  stops.add(dir);
16641
16823
  for (const candidate of LOCKFILE_PRIORITY) {
16642
- const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
16824
+ const lockPath = import_node_path71.default.join(dir, candidate.lockfile);
16643
16825
  if (await exists2(lockPath)) {
16644
16826
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
16645
16827
  }
16646
16828
  }
16647
- const parent = import_node_path70.default.dirname(dir);
16829
+ const parent = import_node_path71.default.dirname(dir);
16648
16830
  if (parent === dir) break;
16649
16831
  dir = parent;
16650
16832
  }
16651
- return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16833
+ return { pm: "npm", cwd: import_node_path71.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16652
16834
  }
16653
16835
  async function runPackageManagerInstall(cmd) {
16654
16836
  return new Promise((resolve) => {
@@ -16690,15 +16872,15 @@ ${err.message}`
16690
16872
  // src/extend/index.ts
16691
16873
  async function fileExists2(p) {
16692
16874
  try {
16693
- await import_node_fs35.promises.access(p);
16875
+ await import_node_fs36.promises.access(p);
16694
16876
  return true;
16695
16877
  } catch {
16696
16878
  return false;
16697
16879
  }
16698
16880
  }
16699
16881
  async function readPackageJson(scanPath) {
16700
- const pkgPath = import_node_path71.default.join(scanPath, "package.json");
16701
- const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
16882
+ const pkgPath = import_node_path72.default.join(scanPath, "package.json");
16883
+ const raw = await import_node_fs36.promises.readFile(pkgPath, "utf8");
16702
16884
  return JSON.parse(raw);
16703
16885
  }
16704
16886
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -16712,15 +16894,15 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
16712
16894
  async function findHookFiles(scanPath) {
16713
16895
  const found = [];
16714
16896
  const walk11 = async (dir) => {
16715
- const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
16897
+ const entries = await import_node_fs36.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
16716
16898
  for (const entry of entries) {
16717
16899
  if (entry.isDirectory()) {
16718
16900
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
16719
- await walk11(import_node_path71.default.join(dir, entry.name));
16901
+ await walk11(import_node_path72.default.join(dir, entry.name));
16720
16902
  } else if (entry.isFile()) {
16721
16903
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
16722
- const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry.name));
16723
- found.push(rel.split(import_node_path71.default.sep).join("/"));
16904
+ const rel = import_node_path72.default.relative(scanPath, import_node_path72.default.join(dir, entry.name));
16905
+ found.push(rel.split(import_node_path72.default.sep).join("/"));
16724
16906
  }
16725
16907
  }
16726
16908
  }
@@ -16731,7 +16913,7 @@ async function findHookFiles(scanPath) {
16731
16913
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16732
16914
  let fallback = null;
16733
16915
  for (const file of hookFiles) {
16734
- const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
16916
+ const content = await import_node_fs36.promises.readFile(import_node_path72.default.join(scanPath, file), "utf8");
16735
16917
  const patched = splicedContent(content, snippet2);
16736
16918
  if (patched !== null) return { file, content, patched };
16737
16919
  if (fallback === null) fallback = { file, content };
@@ -16739,12 +16921,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16739
16921
  return { file: fallback.file, content: fallback.content, patched: null };
16740
16922
  }
16741
16923
  function extendLogPath() {
16742
- return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16924
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path72.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16743
16925
  }
16744
16926
  async function appendExtendLog(entry) {
16745
16927
  const logPath = extendLogPath();
16746
- await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
16747
- await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
16928
+ await import_node_fs36.promises.mkdir(import_node_path72.default.dirname(logPath), { recursive: true });
16929
+ await import_node_fs36.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
16748
16930
  }
16749
16931
  function splicedContent(fileContent, snippet2) {
16750
16932
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -16802,7 +16984,7 @@ function lookupInstrumentation(library, installedVersion) {
16802
16984
  }
16803
16985
  async function describeProjectInstrumentation(ctx) {
16804
16986
  const hookFiles = await findHookFiles(ctx.scanPath);
16805
- const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
16987
+ const envNeat = await fileExists2(import_node_path72.default.join(ctx.scanPath, ".env.neat"));
16806
16988
  const registryInstrPackages = new Set(
16807
16989
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
16808
16990
  );
@@ -16824,7 +17006,7 @@ async function applyExtension(ctx, args, options) {
16824
17006
  );
16825
17007
  }
16826
17008
  for (const file of hookFiles) {
16827
- const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
17009
+ const content = await import_node_fs36.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
16828
17010
  if (content.includes(args.registration_snippet)) {
16829
17011
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
16830
17012
  }
@@ -16836,18 +17018,18 @@ async function applyExtension(ctx, args, options) {
16836
17018
  );
16837
17019
  }
16838
17020
  const primaryFile = primary.file;
16839
- const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
17021
+ const primaryPath = import_node_path72.default.join(ctx.scanPath, primaryFile);
16840
17022
  const filesTouched = [];
16841
17023
  const depsAdded = [];
16842
- const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
17024
+ const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
16843
17025
  const pkg = await readPackageJson(ctx.scanPath);
16844
17026
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
16845
17027
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
16846
- await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
17028
+ await import_node_fs36.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16847
17029
  filesTouched.push("package.json");
16848
17030
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
16849
17031
  }
16850
- await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
17032
+ await import_node_fs36.promises.writeFile(primaryPath, primary.patched, "utf8");
16851
17033
  filesTouched.push(primaryFile);
16852
17034
  const cmd = await detectPackageManager(ctx.scanPath);
16853
17035
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -16878,7 +17060,7 @@ async function dryRunExtension(ctx, args) {
16878
17060
  };
16879
17061
  }
16880
17062
  for (const file of hookFiles) {
16881
- const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
17063
+ const content = await import_node_fs36.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
16882
17064
  if (content.includes(args.registration_snippet)) {
16883
17065
  return {
16884
17066
  library: args.library,
@@ -16913,28 +17095,28 @@ async function rollbackExtension(ctx, args) {
16913
17095
  if (!await fileExists2(logPath)) {
16914
17096
  return { undone: false, message: "no apply found for library" };
16915
17097
  }
16916
- const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
17098
+ const raw = await import_node_fs36.promises.readFile(logPath, "utf8");
16917
17099
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
16918
17100
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
16919
17101
  if (!match) {
16920
17102
  return { undone: false, message: "no apply found for library" };
16921
17103
  }
16922
- const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
17104
+ const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
16923
17105
  if (await fileExists2(pkgPath)) {
16924
17106
  const pkg = await readPackageJson(ctx.scanPath);
16925
17107
  if (pkg.dependencies?.[match.instrumentation_package]) {
16926
17108
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
16927
17109
  pkg.dependencies = rest;
16928
- await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
17110
+ await import_node_fs36.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16929
17111
  }
16930
17112
  }
16931
17113
  const hookFiles = await findHookFiles(ctx.scanPath);
16932
17114
  for (const file of hookFiles) {
16933
- const filePath = import_node_path71.default.join(ctx.scanPath, file);
16934
- const content = await import_node_fs35.promises.readFile(filePath, "utf8");
17115
+ const filePath = import_node_path72.default.join(ctx.scanPath, file);
17116
+ const content = await import_node_fs36.promises.readFile(filePath, "utf8");
16935
17117
  if (content.includes(match.registration_snippet)) {
16936
17118
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
16937
- await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
17119
+ await import_node_fs36.promises.writeFile(filePath, filtered, "utf8");
16938
17120
  break;
16939
17121
  }
16940
17122
  }
@@ -18571,7 +18753,7 @@ async function askGraph(graph, question, opts = {}) {
18571
18753
 
18572
18754
  // src/diff.ts
18573
18755
  init_cjs_shims();
18574
- var import_node_fs36 = require("fs");
18756
+ var import_node_fs37 = require("fs");
18575
18757
  async function loadSnapshotForDiff(target) {
18576
18758
  if (/^https?:\/\//i.test(target)) {
18577
18759
  const res = await fetch(target);
@@ -18580,7 +18762,7 @@ async function loadSnapshotForDiff(target) {
18580
18762
  }
18581
18763
  return await res.json();
18582
18764
  }
18583
- const raw = await import_node_fs36.promises.readFile(target, "utf8");
18765
+ const raw = await import_node_fs37.promises.readFile(target, "utf8");
18584
18766
  return JSON.parse(raw);
18585
18767
  }
18586
18768
  function indexEntries(entries) {
@@ -18648,23 +18830,23 @@ function canonicalJson(value) {
18648
18830
 
18649
18831
  // src/projects.ts
18650
18832
  init_cjs_shims();
18651
- var import_node_path72 = __toESM(require("path"), 1);
18833
+ var import_node_path73 = __toESM(require("path"), 1);
18652
18834
  function pathsForProject(project, baseDir) {
18653
18835
  if (project === DEFAULT_PROJECT) {
18654
18836
  return {
18655
- snapshotPath: import_node_path72.default.join(baseDir, "graph.json"),
18656
- errorsPath: import_node_path72.default.join(baseDir, "errors.ndjson"),
18657
- staleEventsPath: import_node_path72.default.join(baseDir, "stale-events.ndjson"),
18658
- embeddingsCachePath: import_node_path72.default.join(baseDir, "embeddings.json"),
18659
- policyViolationsPath: import_node_path72.default.join(baseDir, "policy-violations.ndjson")
18837
+ snapshotPath: import_node_path73.default.join(baseDir, "graph.json"),
18838
+ errorsPath: import_node_path73.default.join(baseDir, "errors.ndjson"),
18839
+ staleEventsPath: import_node_path73.default.join(baseDir, "stale-events.ndjson"),
18840
+ embeddingsCachePath: import_node_path73.default.join(baseDir, "embeddings.json"),
18841
+ policyViolationsPath: import_node_path73.default.join(baseDir, "policy-violations.ndjson")
18660
18842
  };
18661
18843
  }
18662
18844
  return {
18663
- snapshotPath: import_node_path72.default.join(baseDir, `${project}.json`),
18664
- errorsPath: import_node_path72.default.join(baseDir, `errors.${project}.ndjson`),
18665
- staleEventsPath: import_node_path72.default.join(baseDir, `stale-events.${project}.ndjson`),
18666
- embeddingsCachePath: import_node_path72.default.join(baseDir, `embeddings.${project}.json`),
18667
- policyViolationsPath: import_node_path72.default.join(baseDir, `policy-violations.${project}.ndjson`)
18845
+ snapshotPath: import_node_path73.default.join(baseDir, `${project}.json`),
18846
+ errorsPath: import_node_path73.default.join(baseDir, `errors.${project}.ndjson`),
18847
+ staleEventsPath: import_node_path73.default.join(baseDir, `stale-events.${project}.ndjson`),
18848
+ embeddingsCachePath: import_node_path73.default.join(baseDir, `embeddings.${project}.json`),
18849
+ policyViolationsPath: import_node_path73.default.join(baseDir, `policy-violations.${project}.ndjson`)
18668
18850
  };
18669
18851
  }
18670
18852
  var Projects = class {
@@ -18700,28 +18882,28 @@ var Projects = class {
18700
18882
 
18701
18883
  // src/registry.ts
18702
18884
  init_cjs_shims();
18703
- var import_node_fs37 = require("fs");
18885
+ var import_node_fs38 = require("fs");
18704
18886
  var import_node_os3 = __toESM(require("os"), 1);
18705
- var import_node_path73 = __toESM(require("path"), 1);
18887
+ var import_node_path74 = __toESM(require("path"), 1);
18706
18888
  var import_types63 = require("@neat.is/types");
18707
18889
  var LOCK_TIMEOUT_MS = 5e3;
18708
18890
  var LOCK_RETRY_MS = 50;
18709
18891
  function neatHome() {
18710
18892
  const override = process.env.NEAT_HOME;
18711
- if (override && override.length > 0) return import_node_path73.default.resolve(override);
18712
- return import_node_path73.default.join(import_node_os3.default.homedir(), ".neat");
18893
+ if (override && override.length > 0) return import_node_path74.default.resolve(override);
18894
+ return import_node_path74.default.join(import_node_os3.default.homedir(), ".neat");
18713
18895
  }
18714
18896
  function registryPath() {
18715
- return import_node_path73.default.join(neatHome(), "projects.json");
18897
+ return import_node_path74.default.join(neatHome(), "projects.json");
18716
18898
  }
18717
18899
  function registryLockPath() {
18718
- return import_node_path73.default.join(neatHome(), "projects.json.lock");
18900
+ return import_node_path74.default.join(neatHome(), "projects.json.lock");
18719
18901
  }
18720
18902
  function daemonPidPath() {
18721
- return import_node_path73.default.join(neatHome(), "neatd.pid");
18903
+ return import_node_path74.default.join(neatHome(), "neatd.pid");
18722
18904
  }
18723
18905
  function daemonsDir() {
18724
- return import_node_path73.default.join(neatHome(), "daemons");
18906
+ return import_node_path74.default.join(neatHome(), "daemons");
18725
18907
  }
18726
18908
  function isFiniteInt(v) {
18727
18909
  return typeof v === "number" && Number.isFinite(v);
@@ -18754,7 +18936,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
18754
18936
  const dir = daemonsDir();
18755
18937
  let names;
18756
18938
  try {
18757
- names = await import_node_fs37.promises.readdir(dir);
18939
+ names = await import_node_fs38.promises.readdir(dir);
18758
18940
  } catch (err) {
18759
18941
  if (err.code === "ENOENT") return [];
18760
18942
  throw err;
@@ -18762,10 +18944,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
18762
18944
  const out = [];
18763
18945
  for (const name of names) {
18764
18946
  if (!name.endsWith(".json")) continue;
18765
- const file = import_node_path73.default.join(dir, name);
18947
+ const file = import_node_path74.default.join(dir, name);
18766
18948
  let raw;
18767
18949
  try {
18768
- raw = await import_node_fs37.promises.readFile(file, "utf8");
18950
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
18769
18951
  } catch {
18770
18952
  continue;
18771
18953
  }
@@ -18791,7 +18973,7 @@ function isPidAliveDefault(pid) {
18791
18973
  }
18792
18974
  async function readPidFile(file) {
18793
18975
  try {
18794
- const raw = await import_node_fs37.promises.readFile(file, "utf8");
18976
+ const raw = await import_node_fs38.promises.readFile(file, "utf8");
18795
18977
  const pid = Number.parseInt(raw.trim(), 10);
18796
18978
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
18797
18979
  } catch {
@@ -18817,14 +18999,12 @@ async function readLockPid(lockPath) {
18817
18999
  async function classifyLockHolder(lockPath, probe = defaultLockHolderProbe) {
18818
19000
  const lockPid = await readLockPid(lockPath);
18819
19001
  const daemonPid = await probe.daemonPidFromFile();
18820
- if (daemonPid !== void 0 && daemonPid !== process.pid && probe.isPidAlive(daemonPid) && // The lock already names the daemon, or the daemon answers on its port.
18821
- // Either confirms a live daemon is in the picture (the second guards
18822
- // against a stale pidfile whose PID got reused).
18823
- (daemonPid === lockPid || await probe.daemonResponds())) {
18824
- return { kind: "daemon", pid: daemonPid };
18825
- }
18826
19002
  if (lockPid !== void 0 && lockPid !== process.pid && probe.isPidAlive(lockPid)) {
18827
- return { kind: "command", pid: lockPid };
19003
+ return { kind: lockPid === daemonPid ? "daemon" : "command", pid: lockPid };
19004
+ }
19005
+ if (daemonPid !== void 0 && daemonPid !== process.pid && probe.isPidAlive(daemonPid) && // Guards against a stale pidfile whose PID got reused.
19006
+ await probe.daemonResponds()) {
19007
+ return { kind: "daemon", pid: daemonPid };
18828
19008
  }
18829
19009
  return { kind: "stale" };
18830
19010
  }
@@ -18835,36 +19015,36 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
18835
19015
  case "command":
18836
19016
  return `Another neat command (pid ${holder.pid}) is holding the registry lock. Wait for it to finish, or check \`ps\` if you're not sure what's running.`;
18837
19017
  case "stale":
18838
- return `neat registry: timed out after ${timeoutMs}ms waiting for ${lockPath}. Another neat process is holding the lock; if no such process exists, remove the file by hand.`;
19018
+ return `timed out after ${timeoutMs}ms waiting for ${lockPath}. Another neat process is holding the lock; if no such process exists, remove the file by hand.`;
18839
19019
  }
18840
19020
  }
18841
19021
  async function normalizeProjectPath(input) {
18842
- const resolved = import_node_path73.default.resolve(input);
19022
+ const resolved = import_node_path74.default.resolve(input);
18843
19023
  try {
18844
- return await import_node_fs37.promises.realpath(resolved);
19024
+ return await import_node_fs38.promises.realpath(resolved);
18845
19025
  } catch {
18846
19026
  return resolved;
18847
19027
  }
18848
19028
  }
18849
19029
  async function writeAtomically(target, contents) {
18850
- await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(target), { recursive: true });
19030
+ await import_node_fs38.promises.mkdir(import_node_path74.default.dirname(target), { recursive: true });
18851
19031
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
18852
- const fd = await import_node_fs37.promises.open(tmp, "w");
19032
+ const fd = await import_node_fs38.promises.open(tmp, "w");
18853
19033
  try {
18854
19034
  await fd.writeFile(contents, "utf8");
18855
19035
  await fd.sync();
18856
19036
  } finally {
18857
19037
  await fd.close();
18858
19038
  }
18859
- await import_node_fs37.promises.rename(tmp, target);
19039
+ await import_node_fs38.promises.rename(tmp, target);
18860
19040
  }
18861
19041
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
18862
19042
  const deadline = Date.now() + timeoutMs;
18863
- await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(lockPath), { recursive: true });
19043
+ await import_node_fs38.promises.mkdir(import_node_path74.default.dirname(lockPath), { recursive: true });
18864
19044
  let probedHolder = false;
18865
19045
  while (true) {
18866
19046
  try {
18867
- const fd = await import_node_fs37.promises.open(lockPath, "wx");
19047
+ const fd = await import_node_fs38.promises.open(lockPath, "wx");
18868
19048
  try {
18869
19049
  await fd.writeFile(`${process.pid}
18870
19050
  `, "utf8");
@@ -18878,18 +19058,36 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
18878
19058
  if (!probedHolder) {
18879
19059
  probedHolder = true;
18880
19060
  const holder = await classifyLockHolder(lockPath, probe);
18881
- if (holder.kind === "daemon") throw new Error(lockHolderMessage(holder, lockPath, timeoutMs));
19061
+ if (holder.kind === "daemon") {
19062
+ throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
19063
+ }
19064
+ if (holder.kind === "stale" && await isReclaimableLock(lockPath)) {
19065
+ await import_node_fs38.promises.unlink(lockPath).catch(() => {
19066
+ });
19067
+ continue;
19068
+ }
18882
19069
  }
18883
19070
  if (Date.now() >= deadline) {
18884
19071
  const holder = await classifyLockHolder(lockPath, probe);
18885
- throw new Error(lockHolderMessage(holder, lockPath, timeoutMs));
19072
+ throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
18886
19073
  }
18887
19074
  await new Promise((r) => setTimeout(r, LOCK_RETRY_MS));
18888
19075
  }
18889
19076
  }
18890
19077
  }
19078
+ var LOCK_UNSTAMPED_GRACE_MS = 5e3;
19079
+ async function isReclaimableLock(lockPath) {
19080
+ const pid = await readLockPid(lockPath);
19081
+ if (pid !== void 0) return true;
19082
+ try {
19083
+ const stat = await import_node_fs38.promises.stat(lockPath);
19084
+ return Date.now() - stat.mtimeMs > LOCK_UNSTAMPED_GRACE_MS;
19085
+ } catch {
19086
+ return false;
19087
+ }
19088
+ }
18891
19089
  async function releaseLock(lockPath) {
18892
- await import_node_fs37.promises.unlink(lockPath).catch(() => {
19090
+ await import_node_fs38.promises.unlink(lockPath).catch(() => {
18893
19091
  });
18894
19092
  }
18895
19093
  async function withLock(fn) {
@@ -18905,7 +19103,7 @@ async function readRegistry() {
18905
19103
  const file = registryPath();
18906
19104
  let raw;
18907
19105
  try {
18908
- raw = await import_node_fs37.promises.readFile(file, "utf8");
19106
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
18909
19107
  } catch (err) {
18910
19108
  if (err.code === "ENOENT") {
18911
19109
  return { version: 1, projects: [] };
@@ -18919,10 +19117,22 @@ async function writeRegistry(reg) {
18919
19117
  const validated = import_types63.RegistryFileSchema.parse(reg);
18920
19118
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
18921
19119
  }
18922
- var ProjectNameCollisionError = class extends Error {
19120
+ var RegistryError = class extends Error {
19121
+ constructor(message) {
19122
+ super(message);
19123
+ this.name = "RegistryError";
19124
+ }
19125
+ };
19126
+ var RegistryLockError = class extends RegistryError {
19127
+ constructor(message) {
19128
+ super(message);
19129
+ this.name = "RegistryLockError";
19130
+ }
19131
+ };
19132
+ var ProjectNameCollisionError = class extends RegistryError {
18923
19133
  projectName;
18924
19134
  constructor(name) {
18925
- super(`neat registry: a project named "${name}" is already registered`);
19135
+ super(`a project named "${name}" is already registered`);
18926
19136
  this.name = "ProjectNameCollisionError";
18927
19137
  this.projectName = name;
18928
19138
  }
@@ -18971,7 +19181,7 @@ async function setStatus(name, status2) {
18971
19181
  return withLock(async () => {
18972
19182
  const reg = await readRegistry();
18973
19183
  const entry = reg.projects.find((p) => p.name === name);
18974
- if (!entry) throw new Error(`neat registry: no project named "${name}"`);
19184
+ if (!entry) throw new RegistryError(`no project named "${name}"`);
18975
19185
  entry.status = status2;
18976
19186
  await writeRegistry(reg);
18977
19187
  return entry;
@@ -19010,7 +19220,7 @@ function pruneTtlMs() {
19010
19220
  }
19011
19221
  async function statPathStatus(p) {
19012
19222
  try {
19013
- const stat = await import_node_fs37.promises.stat(p);
19223
+ const stat = await import_node_fs38.promises.stat(p);
19014
19224
  return stat.isDirectory() ? "present" : "unknown";
19015
19225
  } catch (err) {
19016
19226
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -19111,8 +19321,8 @@ init_auth();
19111
19321
  // src/connectors-config.ts
19112
19322
  init_cjs_shims();
19113
19323
  var import_node_os4 = __toESM(require("os"), 1);
19114
- var import_node_path74 = __toESM(require("path"), 1);
19115
- var import_node_fs38 = require("fs");
19324
+ var import_node_path75 = __toESM(require("path"), 1);
19325
+ var import_node_fs39 = require("fs");
19116
19326
  var CONNECTORS_CONFIG_VERSION = 1;
19117
19327
  var EnvRefUnsetError = class extends Error {
19118
19328
  ref;
@@ -19126,17 +19336,17 @@ var EnvRefUnsetError = class extends Error {
19126
19336
  };
19127
19337
  function neatHome2() {
19128
19338
  const override = process.env.NEAT_HOME;
19129
- if (override && override.length > 0) return import_node_path74.default.resolve(override);
19130
- return import_node_path74.default.join(import_node_os4.default.homedir(), ".neat");
19339
+ if (override && override.length > 0) return import_node_path75.default.resolve(override);
19340
+ return import_node_path75.default.join(import_node_os4.default.homedir(), ".neat");
19131
19341
  }
19132
19342
  function connectorsConfigPath(home = neatHome2()) {
19133
- return import_node_path74.default.join(home, "connectors.json");
19343
+ return import_node_path75.default.join(home, "connectors.json");
19134
19344
  }
19135
19345
  var MODE_MASK_LOOSER_THAN_0600 = 63;
19136
19346
  async function warnIfModeLooserThan0600(file) {
19137
19347
  if (process.platform === "win32") return;
19138
19348
  try {
19139
- const stat = await import_node_fs38.promises.stat(file);
19349
+ const stat = await import_node_fs39.promises.stat(file);
19140
19350
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
19141
19351
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
19142
19352
  console.warn(
@@ -19150,7 +19360,7 @@ async function readConnectorsConfig(home = neatHome2()) {
19150
19360
  const file = connectorsConfigPath(home);
19151
19361
  let raw;
19152
19362
  try {
19153
- raw = await import_node_fs38.promises.readFile(file, "utf8");
19363
+ raw = await import_node_fs39.promises.readFile(file, "utf8");
19154
19364
  } catch (err) {
19155
19365
  if (err.code === "ENOENT") {
19156
19366
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -19946,10 +20156,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
19946
20156
  // src/connectors/supabase/map.ts
19947
20157
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
19948
20158
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
19949
- function targetFromRestPath(path79) {
19950
- const rpcMatch = REST_RPC_PATH_RE.exec(path79);
20159
+ function targetFromRestPath(path80) {
20160
+ const rpcMatch = REST_RPC_PATH_RE.exec(path80);
19951
20161
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
19952
- const tableMatch = REST_TABLE_PATH_RE.exec(path79);
20162
+ const tableMatch = REST_TABLE_PATH_RE.exec(path80);
19953
20163
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
19954
20164
  return null;
19955
20165
  }
@@ -20553,9 +20763,9 @@ function parseFirebaseTargetName(targetName) {
20553
20763
  const secondSep = rest.indexOf(FIELD_SEP);
20554
20764
  if (secondSep === -1) return null;
20555
20765
  const method = rest.slice(0, secondSep);
20556
- const path79 = rest.slice(secondSep + 1);
20557
- if (!resourceName || !method || !path79) return null;
20558
- return { resourceName, method, path: path79 };
20766
+ const path80 = rest.slice(secondSep + 1);
20767
+ if (!resourceName || !method || !path80) return null;
20768
+ return { resourceName, method, path: path80 };
20559
20769
  }
20560
20770
  function resourceNameFor(type, labels) {
20561
20771
  if (!labels) return null;
@@ -20593,14 +20803,14 @@ function mapLogEntryToSignal(entry) {
20593
20803
  if (!req) return null;
20594
20804
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
20595
20805
  const method = req.requestMethod.toUpperCase();
20596
- const path79 = pathFromRequestUrl(req.requestUrl);
20597
- if (path79 === null) return null;
20806
+ const path80 = pathFromRequestUrl(req.requestUrl);
20807
+ if (path80 === null) return null;
20598
20808
  const timestamp = entry.timestamp;
20599
20809
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
20600
20810
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
20601
20811
  return {
20602
20812
  targetKind: resourceType,
20603
- targetName: packFirebaseTargetName({ resourceName, method, path: path79 }),
20813
+ targetName: packFirebaseTargetName({ resourceName, method, path: path80 }),
20604
20814
  callCount: 1,
20605
20815
  errorCount: isError ? 1 : 0,
20606
20816
  lastObservedIso: timestamp
@@ -20625,9 +20835,9 @@ var import_types71 = require("@neat.is/types");
20625
20835
  function normalizeName(name) {
20626
20836
  return name.toLowerCase().replace(/[^a-z0-9]/g, "");
20627
20837
  }
20628
- function inferServiceName(graph, resourceName, method, path79) {
20838
+ function inferServiceName(graph, resourceName, method, path80) {
20629
20839
  const wantedMethod = method.toUpperCase();
20630
- const wantedPath = normalizePathTemplate(path79);
20840
+ const wantedPath = normalizePathTemplate(path80);
20631
20841
  const services = [];
20632
20842
  const owners = /* @__PURE__ */ new Set();
20633
20843
  graph.forEachNode((_id, attrs) => {
@@ -20844,7 +21054,7 @@ function mapEventToSignal(event) {
20844
21054
  if (Number.isNaN(observedAt.getTime())) return null;
20845
21055
  const statusCode = metadata?.statusCode;
20846
21056
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
20847
- const path79 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
21057
+ const path80 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20848
21058
  return {
20849
21059
  targetKind: CLOUDFLARE_TARGET_KIND,
20850
21060
  targetName: scriptName,
@@ -20852,7 +21062,7 @@ function mapEventToSignal(event) {
20852
21062
  errorCount: isError ? 1 : 0,
20853
21063
  lastObservedIso: observedAt.toISOString(),
20854
21064
  method,
20855
- ...path79 ? { path: path79 } : {},
21065
+ ...path80 ? { path: path80 } : {},
20856
21066
  ...typeof statusCode === "number" ? { statusCode } : {},
20857
21067
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
20858
21068
  };
@@ -20898,8 +21108,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
20898
21108
  });
20899
21109
  return found;
20900
21110
  }
20901
- function findMatchingRouteNode(graph, serviceName, method, path79) {
20902
- const normalizedPath = normalizePathTemplate(path79);
21111
+ function findMatchingRouteNode(graph, serviceName, method, path80) {
21112
+ const normalizedPath = normalizePathTemplate(path80);
20903
21113
  let found = null;
20904
21114
  graph.forEachNode((id, attrs) => {
20905
21115
  if (found) return;
@@ -20916,10 +21126,10 @@ function createCloudflareResolveTarget(config, graph) {
20916
21126
  return (signal) => {
20917
21127
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
20918
21128
  const scriptName = signal.targetName;
20919
- const { method, path: path79 } = signal;
21129
+ const { method, path: path80 } = signal;
20920
21130
  const resolveRouteGrain = (serviceName, wholeFileId) => {
20921
- if (!method || !path79) return wholeFileId;
20922
- return findMatchingRouteNode(graph, serviceName, method, path79) ?? wholeFileId;
21131
+ if (!method || !path80) return wholeFileId;
21132
+ return findMatchingRouteNode(graph, serviceName, method, path80) ?? wholeFileId;
20923
21133
  };
20924
21134
  const mapping = config.workers?.[scriptName];
20925
21135
  if (mapping) {
@@ -21271,9 +21481,9 @@ function parseCloudRunTargetName(targetName) {
21271
21481
  const secondSep = rest.indexOf(FIELD_SEP2);
21272
21482
  if (secondSep === -1) return null;
21273
21483
  const method = rest.slice(0, secondSep);
21274
- const path79 = rest.slice(secondSep + 1);
21275
- if (!serviceName || !method || !path79) return null;
21276
- return { serviceName, method, path: path79 };
21484
+ const path80 = rest.slice(secondSep + 1);
21485
+ if (!serviceName || !method || !path80) return null;
21486
+ return { serviceName, method, path: path80 };
21277
21487
  }
21278
21488
 
21279
21489
  // src/connectors/cloud-run/map.ts
@@ -21302,14 +21512,14 @@ function mapLogEntryToSignal2(entry) {
21302
21512
  if (!req) return null;
21303
21513
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
21304
21514
  const method = req.requestMethod.toUpperCase();
21305
- const path79 = pathFromRequestUrl2(req.requestUrl);
21306
- if (path79 === null) return null;
21515
+ const path80 = pathFromRequestUrl2(req.requestUrl);
21516
+ if (path80 === null) return null;
21307
21517
  const timestamp = entry.timestamp;
21308
21518
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
21309
21519
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
21310
21520
  return {
21311
21521
  targetKind: CLOUD_RUN_TARGET_KIND,
21312
- targetName: packCloudRunTargetName({ serviceName, method, path: path79 }),
21522
+ targetName: packCloudRunTargetName({ serviceName, method, path: path80 }),
21313
21523
  callCount: 1,
21314
21524
  errorCount: isError ? 1 : 0,
21315
21525
  lastObservedIso: timestamp
@@ -21348,14 +21558,14 @@ function createCloudRunResolveTarget(graph, config) {
21348
21558
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
21349
21559
  const identity = parseCloudRunTargetName(signal.targetName);
21350
21560
  if (!identity) return null;
21351
- const { serviceName: gcpServiceName, method, path: path79 } = identity;
21352
- const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path79) ?? void 0 : void 0);
21561
+ const { serviceName: gcpServiceName, method, path: path80 } = identity;
21562
+ const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path80) ?? void 0 : void 0);
21353
21563
  if (mappedService) {
21354
21564
  const routeNodeId = findMatchingRouteNode2(
21355
21565
  graph,
21356
21566
  mappedService,
21357
21567
  method,
21358
- normalizePathTemplate(path79)
21568
+ normalizePathTemplate(path80)
21359
21569
  );
21360
21570
  if (routeNodeId) {
21361
21571
  return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types82.EdgeType.CALLS };
@@ -21491,9 +21701,9 @@ function parseGcpLbTargetName(targetName) {
21491
21701
  const secondSep = rest.indexOf(FIELD_SEP3);
21492
21702
  if (secondSep === -1) return null;
21493
21703
  const method = rest.slice(0, secondSep);
21494
- const path79 = rest.slice(secondSep + 1);
21495
- if (!backendServiceName || !method || !path79) return null;
21496
- return { backendServiceName, method, path: path79 };
21704
+ const path80 = rest.slice(secondSep + 1);
21705
+ if (!backendServiceName || !method || !path80) return null;
21706
+ return { backendServiceName, method, path: path80 };
21497
21707
  }
21498
21708
 
21499
21709
  // src/connectors/gcp-lb/map.ts
@@ -21522,14 +21732,14 @@ function mapLogEntryToSignal3(entry) {
21522
21732
  if (!req) return null;
21523
21733
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
21524
21734
  const method = req.requestMethod.toUpperCase();
21525
- const path79 = pathFromRequestUrl3(req.requestUrl);
21526
- if (path79 === null) return null;
21735
+ const path80 = pathFromRequestUrl3(req.requestUrl);
21736
+ if (path80 === null) return null;
21527
21737
  const timestamp = entry.timestamp;
21528
21738
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
21529
21739
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
21530
21740
  return {
21531
21741
  targetKind: GCP_LB_TARGET_KIND,
21532
- targetName: packGcpLbTargetName({ backendServiceName, method, path: path79 }),
21742
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path80 }),
21533
21743
  callCount: 1,
21534
21744
  errorCount: isError ? 1 : 0,
21535
21745
  lastObservedIso: timestamp
@@ -21568,14 +21778,14 @@ function createGcpLbResolveTarget(graph, config) {
21568
21778
  if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
21569
21779
  const identity = parseGcpLbTargetName(signal.targetName);
21570
21780
  if (!identity) return null;
21571
- const { backendServiceName, method, path: path79 } = identity;
21572
- const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path79) ?? void 0 : void 0);
21781
+ const { backendServiceName, method, path: path80 } = identity;
21782
+ const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path80) ?? void 0 : void 0);
21573
21783
  if (mappedService) {
21574
21784
  const routeNodeId = findMatchingRouteNode3(
21575
21785
  graph,
21576
21786
  mappedService,
21577
21787
  method,
21578
- normalizePathTemplate(path79)
21788
+ normalizePathTemplate(path80)
21579
21789
  );
21580
21790
  if (routeNodeId) {
21581
21791
  return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types86.EdgeType.CALLS };
@@ -22893,16 +23103,18 @@ function serializeGraph(graph) {
22893
23103
  });
22894
23104
  return { nodes, edges };
22895
23105
  }
22896
- function projectFromReq(req, singleProject) {
23106
+ function projectFromReq(req, singleProject, registry) {
22897
23107
  const params = req.params;
22898
23108
  const named2 = params.project;
22899
23109
  if (singleProject) {
22900
23110
  return named2 === void 0 || named2 === DEFAULT_PROJECT ? singleProject : named2;
22901
23111
  }
22902
- return named2 ?? DEFAULT_PROJECT;
23112
+ if (named2 !== void 0) return named2;
23113
+ const hosted = registry?.list() ?? [];
23114
+ return hosted.length === 1 ? hosted[0] : DEFAULT_PROJECT;
22903
23115
  }
22904
23116
  function resolveProject(registry, req, reply, bootstrap, singleProject) {
22905
- const name = projectFromReq(req, singleProject);
23117
+ const name = projectFromReq(req, singleProject, registry);
22906
23118
  const ctx = registry.get(name);
22907
23119
  if (!ctx) {
22908
23120
  const phase = bootstrap?.status(name);
@@ -22914,10 +23126,13 @@ function resolveProject(registry, req, reply, bootstrap, singleProject) {
22914
23126
  void reply.code(503).send({ ready: false, project: name, status: "broken" });
22915
23127
  return null;
22916
23128
  }
23129
+ const unnamed = req.params.project === void 0;
23130
+ const hosted = registry.list();
23131
+ const hint = unnamed && name === DEFAULT_PROJECT && hosted.length > 1 ? `This daemon hosts ${hosted.length} projects and none is named \`default\`: ${hosted.join(", ")}. Name one as /projects/<name>/\u2026 , or reach that project's own daemon.` : "This daemon does not host that project. GET /projects lists what it serves (see hostedHere).";
22917
23132
  void reply.code(404).send({
22918
23133
  error: "project not found",
22919
23134
  project: name,
22920
- hint: "This daemon does not host that project. GET /projects lists what it serves (see hostedHere)."
23135
+ hint
22921
23136
  });
22922
23137
  return null;
22923
23138
  }
@@ -23800,8 +24015,8 @@ init_otel_grpc();
23800
24015
 
23801
24016
  // src/daemon.ts
23802
24017
  init_cjs_shims();
23803
- var import_node_fs41 = require("fs");
23804
- var import_node_path79 = __toESM(require("path"), 1);
24018
+ var import_node_fs42 = require("fs");
24019
+ var import_node_path80 = __toESM(require("path"), 1);
23805
24020
  var import_node_module = require("module");
23806
24021
  init_otel();
23807
24022
 
@@ -23838,7 +24053,7 @@ async function reconcileFrontierSurfaces(graph, errorsPath) {
23838
24053
  // src/connectors/hosted.ts
23839
24054
  init_cjs_shims();
23840
24055
  var import_promises = require("fs/promises");
23841
- var import_node_path75 = require("path");
24056
+ var import_node_path76 = require("path");
23842
24057
 
23843
24058
  // src/connectors/railway/target-ref.ts
23844
24059
  init_cjs_shims();
@@ -23873,13 +24088,13 @@ function decodeRailwayTargetRef(ref) {
23873
24088
  var CREDENTIAL_REFRESH_SKEW_MS = 6e4;
23874
24089
  var CP_REQUEST_TIMEOUT_MS = 1e4;
23875
24090
  var DEFAULT_RELIST_INTERVAL_MS = 6e4;
23876
- async function cpGet(path79, deps) {
24091
+ async function cpGet(path80, deps) {
23877
24092
  const f = deps.fetchImpl ?? fetch;
23878
- const res = await f(`${deps.cpUrl.replace(/\/+$/, "")}${path79}`, {
24093
+ const res = await f(`${deps.cpUrl.replace(/\/+$/, "")}${path80}`, {
23879
24094
  headers: { authorization: `Bearer ${deps.daemonToken}`, accept: "application/json" },
23880
24095
  signal: AbortSignal.timeout(CP_REQUEST_TIMEOUT_MS)
23881
24096
  });
23882
- if (!res.ok) throw new Error(`control plane ${path79} \u2192 HTTP ${res.status}`);
24097
+ if (!res.ok) throw new Error(`control plane ${path80} \u2192 HTTP ${res.status}`);
23883
24098
  return await res.json();
23884
24099
  }
23885
24100
  function credentialRecord(provider, cred) {
@@ -23940,7 +24155,7 @@ function parseFirebaseServiceMap(raw) {
23940
24155
  }
23941
24156
  }
23942
24157
  function pushHandlesPath(projectDir) {
23943
- return (0, import_node_path75.join)(projectDir, "neat-out", "connectors-hosted.json");
24158
+ return (0, import_node_path76.join)(projectDir, "neat-out", "connectors-hosted.json");
23944
24159
  }
23945
24160
  async function readPushHandles(projectDir) {
23946
24161
  try {
@@ -23951,14 +24166,14 @@ async function readPushHandles(projectDir) {
23951
24166
  }
23952
24167
  }
23953
24168
  async function writePushHandle(projectDir, provider, handle) {
23954
- const path79 = pushHandlesPath(projectDir);
24169
+ const path80 = pushHandlesPath(projectDir);
23955
24170
  const handles = await readPushHandles(projectDir);
23956
24171
  handles[provider] = handle;
23957
- await (0, import_promises.mkdir)((0, import_node_path75.dirname)(path79), { recursive: true });
23958
- const tmp = `${path79}.${process.pid}.tmp`;
24172
+ await (0, import_promises.mkdir)((0, import_node_path76.dirname)(path80), { recursive: true });
24173
+ const tmp = `${path80}.${process.pid}.tmp`;
23959
24174
  await (0, import_promises.writeFile)(tmp, `${JSON.stringify(handles, null, 2)}
23960
24175
  `, "utf8");
23961
- await (0, import_promises.rename)(tmp, path79);
24176
+ await (0, import_promises.rename)(tmp, path80);
23962
24177
  }
23963
24178
  function pushCredentialRecord(provider, cred, deps) {
23964
24179
  switch (provider) {
@@ -24179,7 +24394,7 @@ async function maybeStartHostedConnectors(input) {
24179
24394
  // src/connectors/hosted-repos.ts
24180
24395
  init_cjs_shims();
24181
24396
  var import_node_os5 = __toESM(require("os"), 1);
24182
- var import_node_path76 = __toESM(require("path"), 1);
24397
+ var import_node_path77 = __toESM(require("path"), 1);
24183
24398
  var import_promises2 = require("fs/promises");
24184
24399
  var CP_REQUEST_TIMEOUT_MS2 = 1e4;
24185
24400
  var DEFAULT_SYNC_INTERVAL_MS = 5 * 6e4;
@@ -24208,7 +24423,7 @@ async function cpPostStatus(deps, owner, name, body) {
24208
24423
  if (!res.ok && res.status !== 404) throw new Error(`control plane status POST \u2192 HTTP ${res.status}`);
24209
24424
  }
24210
24425
  var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
24211
- const [{ default: git }, httpMod, fs40] = await Promise.all([
24426
+ const [{ default: git }, httpMod, fs41] = await Promise.all([
24212
24427
  import("isomorphic-git"),
24213
24428
  import("isomorphic-git/http/node"),
24214
24429
  import("fs")
@@ -24219,7 +24434,7 @@ var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
24219
24434
  const username = parsed.password ? parsed.username : "x-access-token";
24220
24435
  const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
24221
24436
  const clone = git.clone({
24222
- fs: fs40,
24437
+ fs: fs41,
24223
24438
  http,
24224
24439
  dir: destDir,
24225
24440
  url: cleanUrl,
@@ -24250,7 +24465,7 @@ async function syncOneRepo(r, input) {
24250
24465
  const tmpRoot = input.tmpRoot ?? import_node_os5.default.tmpdir();
24251
24466
  let dir;
24252
24467
  try {
24253
- dir = await (0, import_promises2.mkdtemp)(import_node_path76.default.join(tmpRoot, "neat-repo-"));
24468
+ dir = await (0, import_promises2.mkdtemp)(import_node_path77.default.join(tmpRoot, "neat-repo-"));
24254
24469
  await cpPostStatus(deps, r.owner, r.name, { syncStatus: "syncing", detail: "cloning" }).catch(() => {
24255
24470
  });
24256
24471
  await cloneRepo(r.cloneUrl, r.defaultBranch, dir);
@@ -24387,9 +24602,9 @@ function makeK8sFetchImpl(transport) {
24387
24602
  req.end();
24388
24603
  }));
24389
24604
  }
24390
- async function listResource(transport, namespace, path79, opts = {}) {
24605
+ async function listResource(transport, namespace, path80, opts = {}) {
24391
24606
  const base = opts.apiUrl ?? transport.server;
24392
- const url = `${base.replace(/\/$/, "")}${path79}`;
24607
+ const url = `${base.replace(/\/$/, "")}${path80}`;
24393
24608
  const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
24394
24609
  const res = await junctionFetch(
24395
24610
  url,
@@ -24399,7 +24614,7 @@ async function listResource(transport, namespace, path79, opts = {}) {
24399
24614
  { provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
24400
24615
  );
24401
24616
  if (!res.ok) {
24402
- throw new Error(`kubernetes ${path79} failed: ${res.status} ${res.statusText}`);
24617
+ throw new Error(`kubernetes ${path80} failed: ${res.status} ${res.statusText}`);
24403
24618
  }
24404
24619
  const json = await res.json();
24405
24620
  return Array.isArray(json.items) ? json.items : [];
@@ -24420,14 +24635,14 @@ async function fetchPods(transport, namespace, opts = {}) {
24420
24635
 
24421
24636
  // src/connectors/kubernetes/kubeconfig.ts
24422
24637
  init_cjs_shims();
24423
- var import_node_fs39 = require("fs");
24638
+ var import_node_fs40 = require("fs");
24424
24639
  var import_yaml4 = require("yaml");
24425
24640
  function pemFrom(dataField, pathField) {
24426
24641
  if (typeof dataField === "string" && dataField.length > 0) {
24427
24642
  return Buffer.from(dataField, "base64").toString("utf8");
24428
24643
  }
24429
24644
  if (typeof pathField === "string" && pathField.length > 0) {
24430
- return (0, import_node_fs39.readFileSync)(pathField, "utf8");
24645
+ return (0, import_node_fs40.readFileSync)(pathField, "utf8");
24431
24646
  }
24432
24647
  return void 0;
24433
24648
  }
@@ -24438,7 +24653,7 @@ function named(list, name) {
24438
24653
  }
24439
24654
  function parseKubeconfig(kubeconfig) {
24440
24655
  const looksInline = /\n/.test(kubeconfig) || /(^|\s)clusters\s*:/.test(kubeconfig);
24441
- const text = looksInline ? kubeconfig : (0, import_node_fs39.readFileSync)(kubeconfig, "utf8");
24656
+ const text = looksInline ? kubeconfig : (0, import_node_fs40.readFileSync)(kubeconfig, "utf8");
24442
24657
  let doc;
24443
24658
  try {
24444
24659
  doc = (0, import_yaml4.parse)(text);
@@ -24703,14 +24918,14 @@ function createK8sResolveTarget(graph) {
24703
24918
  init_cjs_shims();
24704
24919
  var import_promises3 = require("fs/promises");
24705
24920
  var import_node_os6 = __toESM(require("os"), 1);
24706
- var import_node_path77 = __toESM(require("path"), 1);
24921
+ var import_node_path78 = __toESM(require("path"), 1);
24707
24922
  function defaultHome() {
24708
24923
  const override = process.env.NEAT_HOME;
24709
- if (override && override.length > 0) return import_node_path77.default.resolve(override);
24710
- return import_node_path77.default.join(import_node_os6.default.homedir(), ".neat");
24924
+ if (override && override.length > 0) return import_node_path78.default.resolve(override);
24925
+ return import_node_path78.default.join(import_node_os6.default.homedir(), ".neat");
24711
24926
  }
24712
24927
  function k8sSubstrateConfigPath(home = defaultHome()) {
24713
- return import_node_path77.default.join(home, "k8s.json");
24928
+ return import_node_path78.default.join(home, "k8s.json");
24714
24929
  }
24715
24930
  async function readK8sSubstrateConfig(home = defaultHome()) {
24716
24931
  let raw;
@@ -24809,8 +25024,8 @@ init_auth();
24809
25024
 
24810
25025
  // src/unrouted.ts
24811
25026
  init_cjs_shims();
24812
- var import_node_fs40 = require("fs");
24813
- var import_node_path78 = __toESM(require("path"), 1);
25027
+ var import_node_fs41 = require("fs");
25028
+ var import_node_path79 = __toESM(require("path"), 1);
24814
25029
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
24815
25030
  return {
24816
25031
  timestamp: now.toISOString(),
@@ -24820,34 +25035,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
24820
25035
  };
24821
25036
  }
24822
25037
  async function appendUnroutedSpan(neatHome3, record) {
24823
- const target = import_node_path78.default.join(neatHome3, "errors.ndjson");
24824
- await import_node_fs40.promises.mkdir(neatHome3, { recursive: true });
24825
- await import_node_fs40.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
25038
+ const target = import_node_path79.default.join(neatHome3, "errors.ndjson");
25039
+ await import_node_fs41.promises.mkdir(neatHome3, { recursive: true });
25040
+ await import_node_fs41.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
24826
25041
  }
24827
25042
  function unroutedErrorsPath(neatHome3) {
24828
- return import_node_path78.default.join(neatHome3, "errors.ndjson");
25043
+ return import_node_path79.default.join(neatHome3, "errors.ndjson");
24829
25044
  }
24830
25045
 
24831
25046
  // src/daemon.ts
24832
25047
  var import_types107 = require("@neat.is/types");
24833
25048
  function daemonJsonPath(scanPath) {
24834
- return import_node_path79.default.join(scanPath, "neat-out", "daemon.json");
25049
+ return import_node_path80.default.join(scanPath, "neat-out", "daemon.json");
24835
25050
  }
24836
25051
  function daemonsDiscoveryDir(home) {
24837
25052
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
24838
- return import_node_path79.default.join(base, "daemons");
25053
+ return import_node_path80.default.join(base, "daemons");
24839
25054
  }
24840
25055
  function daemonDiscoveryPath(project, home) {
24841
- return import_node_path79.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
25056
+ return import_node_path80.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
24842
25057
  }
24843
25058
  function sanitizeDiscoveryName(project) {
24844
25059
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
24845
25060
  }
24846
25061
  function neatHomeFromEnv() {
24847
25062
  const env = process.env.NEAT_HOME;
24848
- if (env && env.length > 0) return import_node_path79.default.resolve(env);
25063
+ if (env && env.length > 0) return import_node_path80.default.resolve(env);
24849
25064
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
24850
- return import_node_path79.default.join(home, ".neat");
25065
+ return import_node_path80.default.join(home, ".neat");
24851
25066
  }
24852
25067
  function resolveNeatVersion() {
24853
25068
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -24879,7 +25094,7 @@ async function clearDaemonRecord(record, home) {
24879
25094
  } catch {
24880
25095
  }
24881
25096
  try {
24882
- await import_node_fs41.promises.unlink(daemonDiscoveryPath(record.project, home));
25097
+ await import_node_fs42.promises.unlink(daemonDiscoveryPath(record.project, home));
24883
25098
  } catch {
24884
25099
  }
24885
25100
  }
@@ -24914,11 +25129,11 @@ function teardownSlot(slot) {
24914
25129
  }
24915
25130
  }
24916
25131
  function neatHomeFor(opts) {
24917
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path79.default.resolve(opts.neatHome);
25132
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path80.default.resolve(opts.neatHome);
24918
25133
  const env = process.env.NEAT_HOME;
24919
- if (env && env.length > 0) return import_node_path79.default.resolve(env);
25134
+ if (env && env.length > 0) return import_node_path80.default.resolve(env);
24920
25135
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
24921
- return import_node_path79.default.join(home, ".neat");
25136
+ return import_node_path80.default.join(home, ".neat");
24922
25137
  }
24923
25138
  function routeSpanToProject(serviceName, projects) {
24924
25139
  if (!serviceName) return DEFAULT_PROJECT;
@@ -24970,9 +25185,9 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
24970
25185
  );
24971
25186
  }
24972
25187
  async function bootstrapProject(entry, connectors = [], neatHome3) {
24973
- const paths = pathsForProject(entry.name, import_node_path79.default.join(entry.path, "neat-out"));
25188
+ const paths = pathsForProject(entry.name, import_node_path80.default.join(entry.path, "neat-out"));
24974
25189
  try {
24975
- const stat = await import_node_fs41.promises.stat(entry.path);
25190
+ const stat = await import_node_fs42.promises.stat(entry.path);
24976
25191
  if (!stat.isDirectory()) {
24977
25192
  throw new Error(`registered path ${entry.path} is not a directory`);
24978
25193
  }
@@ -25124,7 +25339,7 @@ async function startDaemon(opts = {}) {
25124
25339
  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;
25125
25340
  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;
25126
25341
  const singleProject = projectArg;
25127
- const singleProjectPath = singleProject && projectPathArg ? import_node_path79.default.resolve(projectPathArg) : null;
25342
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path80.default.resolve(projectPathArg) : null;
25128
25343
  if (singleProject && !singleProjectPath) {
25129
25344
  throw new Error(
25130
25345
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -25132,14 +25347,14 @@ async function startDaemon(opts = {}) {
25132
25347
  }
25133
25348
  if (!singleProject) {
25134
25349
  try {
25135
- await import_node_fs41.promises.access(regPath);
25350
+ await import_node_fs42.promises.access(regPath);
25136
25351
  } catch {
25137
25352
  throw new Error(
25138
25353
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
25139
25354
  );
25140
25355
  }
25141
25356
  }
25142
- const pidPath = import_node_path79.default.join(home, "neatd.pid");
25357
+ const pidPath = import_node_path80.default.join(home, "neatd.pid");
25143
25358
  await writeAtomically(pidPath, `${process.pid}
25144
25359
  `);
25145
25360
  const slots = /* @__PURE__ */ new Map();
@@ -25351,7 +25566,7 @@ async function startDaemon(opts = {}) {
25351
25566
  }
25352
25567
  if (restApp) await restApp.close().catch(() => {
25353
25568
  });
25354
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25569
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25355
25570
  });
25356
25571
  throw new Error(
25357
25572
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -25529,7 +25744,7 @@ async function startDaemon(opts = {}) {
25529
25744
  });
25530
25745
  if (otlpApp) await otlpApp.close().catch(() => {
25531
25746
  });
25532
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25747
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25533
25748
  });
25534
25749
  throw new Error(
25535
25750
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -25564,7 +25779,7 @@ async function startDaemon(opts = {}) {
25564
25779
  });
25565
25780
  if (otlpApp) await otlpApp.close().catch(() => {
25566
25781
  });
25567
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25782
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25568
25783
  });
25569
25784
  throw new Error(
25570
25785
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -25611,9 +25826,9 @@ async function startDaemon(opts = {}) {
25611
25826
  let registryWatcher = null;
25612
25827
  let reloadTimer = null;
25613
25828
  if (!singleProject) try {
25614
- const regDir = import_node_path79.default.dirname(regPath);
25615
- const regBase = import_node_path79.default.basename(regPath);
25616
- registryWatcher = (0, import_node_fs41.watch)(regDir, (_eventType, filename) => {
25829
+ const regDir = import_node_path80.default.dirname(regPath);
25830
+ const regBase = import_node_path80.default.basename(regPath);
25831
+ registryWatcher = (0, import_node_fs42.watch)(regDir, (_eventType, filename) => {
25617
25832
  if (filename !== null && filename !== regBase) return;
25618
25833
  if (reloadTimer) clearTimeout(reloadTimer);
25619
25834
  reloadTimer = setTimeout(() => {
@@ -25662,7 +25877,7 @@ async function startDaemon(opts = {}) {
25662
25877
  if (daemonRecord) {
25663
25878
  await clearDaemonRecord(daemonRecord, home);
25664
25879
  }
25665
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25880
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25666
25881
  });
25667
25882
  };
25668
25883
  return {