@neat.is/core 0.10.1-dev.20260928 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/neatd.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req2, reply, done) => {
64
- const path81 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path81) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path81)) {
64
+ const path82 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path82) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path82)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path53.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
- return import_node_path53.default.resolve(here, "..", "proto");
197
+ const here = import_node_path54.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
+ return import_node_path54.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url, import_node_path53, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url, import_node_path54, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url = require("url");
272
- import_node_path53 = __toESM(require("path"), 1);
272
+ import_node_path54 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
415
415
  const v = attrs[key];
416
416
  if (typeof v === "string" && v.length > 0) {
417
417
  const q = v.indexOf("?");
418
- const path81 = q === -1 ? v : v.slice(0, q);
419
- if (path81.length > 0) return path81;
418
+ const path82 = q === -1 ? v : v.slice(0, q);
419
+ if (path82.length > 0) return path82;
420
420
  }
421
421
  }
422
422
  return void 0;
@@ -478,10 +478,10 @@ function parseOtlpRequest(body) {
478
478
  return out;
479
479
  }
480
480
  function loadProtoRoot() {
481
- const here = import_node_path54.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
- const protoRoot = import_node_path54.default.resolve(here, "..", "proto");
481
+ const here = import_node_path55.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
+ const protoRoot = import_node_path55.default.resolve(here, "..", "proto");
483
483
  const root = new import_protobufjs.default.Root();
484
- root.resolvePath = (_origin, target) => import_node_path54.default.resolve(protoRoot, target);
484
+ root.resolvePath = (_origin, target) => import_node_path55.default.resolve(protoRoot, target);
485
485
  root.loadSync(
486
486
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
487
487
  { keepCase: true }
@@ -759,12 +759,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
759
759
  }
760
760
  }
761
761
  }
762
- 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;
762
+ 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;
763
763
  var init_otel = __esm({
764
764
  "src/otel.ts"() {
765
765
  "use strict";
766
766
  init_cjs_shims();
767
- import_node_path54 = __toESM(require("path"), 1);
767
+ import_node_path55 = __toESM(require("path"), 1);
768
768
  import_node_url2 = require("url");
769
769
  import_node_zlib = __toESM(require("zlib"), 1);
770
770
  import_fastify = __toESM(require("fastify"), 1);
@@ -788,14 +788,14 @@ __export(neatd_exports, {
788
788
  });
789
789
  module.exports = __toCommonJS(neatd_exports);
790
790
  init_cjs_shims();
791
- var import_node_fs43 = require("fs");
792
- var import_node_path81 = __toESM(require("path"), 1);
791
+ var import_node_fs44 = require("fs");
792
+ var import_node_path82 = __toESM(require("path"), 1);
793
793
  var import_node_module2 = require("module");
794
794
 
795
795
  // src/daemon.ts
796
796
  init_cjs_shims();
797
- var import_node_fs41 = require("fs");
798
- var import_node_path79 = __toESM(require("path"), 1);
797
+ var import_node_fs42 = require("fs");
798
+ var import_node_path80 = __toESM(require("path"), 1);
799
799
  var import_node_module = require("module");
800
800
 
801
801
  // src/graph.ts
@@ -831,8 +831,8 @@ init_cjs_shims();
831
831
 
832
832
  // src/ingest.ts
833
833
  init_cjs_shims();
834
- var import_node_fs7 = require("fs");
835
- var import_node_path8 = __toESM(require("path"), 1);
834
+ var import_node_fs8 = require("fs");
835
+ var import_node_path9 = __toESM(require("path"), 1);
836
836
  var sourceMapJs = __toESM(require("source-map-js"), 1);
837
837
 
838
838
  // src/policy.ts
@@ -1337,19 +1337,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1337
1337
  function longestIncomingWalk(graph, start, maxDepth) {
1338
1338
  let best = { path: [start], edges: [] };
1339
1339
  const visited = /* @__PURE__ */ new Set([start]);
1340
- function step(node, path81, edges) {
1341
- if (path81.length > best.path.length) {
1342
- best = { path: [...path81], edges: [...edges] };
1340
+ function step(node, path82, edges) {
1341
+ if (path82.length > best.path.length) {
1342
+ best = { path: [...path82], edges: [...edges] };
1343
1343
  }
1344
- if (path81.length - 1 >= maxDepth) return;
1344
+ if (path82.length - 1 >= maxDepth) return;
1345
1345
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1346
1346
  for (const [srcId, edge] of incoming) {
1347
1347
  if (visited.has(srcId)) continue;
1348
1348
  visited.add(srcId);
1349
- path81.push(srcId);
1349
+ path82.push(srcId);
1350
1350
  edges.push(edge);
1351
- step(srcId, path81, edges);
1352
- path81.pop();
1351
+ step(srcId, path82, edges);
1352
+ path82.pop();
1353
1353
  edges.pop();
1354
1354
  visited.delete(srcId);
1355
1355
  }
@@ -1564,20 +1564,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
1564
1564
  return best;
1565
1565
  }
1566
1566
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1567
- const path81 = [originServiceId];
1567
+ const path82 = [originServiceId];
1568
1568
  const edges = [];
1569
1569
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1570
1570
  let current = originServiceId;
1571
1571
  for (let depth = 0; depth < maxDepth; depth++) {
1572
1572
  const hop = dominantFailingCall(graph, current, visited);
1573
1573
  if (!hop) break;
1574
- path81.push(hop.nextService);
1574
+ path82.push(hop.nextService);
1575
1575
  edges.push(hop.edge);
1576
1576
  visited.add(hop.nextService);
1577
1577
  current = hop.nextService;
1578
1578
  }
1579
1579
  if (edges.length === 0) return null;
1580
- return { path: path81, edges, culprit: current };
1580
+ return { path: path82, edges, culprit: current };
1581
1581
  }
1582
1582
  function isStaleCallEdge(e) {
1583
1583
  return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
@@ -1613,26 +1613,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
1613
1613
  return best;
1614
1614
  }
1615
1615
  function followStaleCallChain(graph, originServiceId, maxDepth) {
1616
- const path81 = [originServiceId];
1616
+ const path82 = [originServiceId];
1617
1617
  const edges = [];
1618
1618
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1619
1619
  let current = originServiceId;
1620
1620
  for (let depth = 0; depth < maxDepth; depth++) {
1621
1621
  const hop = dominantStaleCall(graph, current, visited);
1622
1622
  if (!hop) break;
1623
- path81.push(hop.nextService);
1623
+ path82.push(hop.nextService);
1624
1624
  edges.push(hop.edge);
1625
1625
  visited.add(hop.nextService);
1626
1626
  current = hop.nextService;
1627
1627
  }
1628
1628
  if (edges.length === 0) return null;
1629
- return { path: path81, edges, culprit: current };
1629
+ return { path: path82, edges, culprit: current };
1630
1630
  }
1631
1631
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1632
1632
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1633
1633
  if (!chain) return null;
1634
1634
  const culprit = chain.culprit;
1635
- const path81 = [...chain.path];
1635
+ const path82 = [...chain.path];
1636
1636
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1637
1637
  const baseConfidence = confidenceFromMix(chain.edges);
1638
1638
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1640,14 +1640,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1640
1640
  if (loc) {
1641
1641
  let rootCauseNode = culprit;
1642
1642
  if (loc.fileNode) {
1643
- path81.push(loc.fileNode);
1643
+ path82.push(loc.fileNode);
1644
1644
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1645
1645
  rootCauseNode = loc.fileNode;
1646
1646
  }
1647
1647
  return import_types.RootCauseResultSchema.parse({
1648
1648
  rootCauseNode,
1649
1649
  rootCauseReason: loc.rootCauseReason,
1650
- traversalPath: path81,
1650
+ traversalPath: path82,
1651
1651
  edgeProvenances,
1652
1652
  confidence,
1653
1653
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1659,7 +1659,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1659
1659
  return import_types.RootCauseResultSchema.parse({
1660
1660
  rootCauseNode: culprit,
1661
1661
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1662
- traversalPath: path81,
1662
+ traversalPath: path82,
1663
1663
  edgeProvenances,
1664
1664
  confidence,
1665
1665
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2312,10 +2312,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2312
2312
  traversalPath = staleChain.path;
2313
2313
  edgeProvenances = staleChain.edges.map((e) => e.provenance);
2314
2314
  } else if (top.node !== seedNode) {
2315
- const path81 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2316
- if (path81) {
2317
- traversalPath = path81.nodes;
2318
- edgeProvenances = path81.edges.map((e) => e.provenance);
2315
+ const path82 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2316
+ if (path82) {
2317
+ traversalPath = path82.nodes;
2318
+ edgeProvenances = path82.edges.map((e) => e.provenance);
2319
2319
  } else {
2320
2320
  traversalPath = [errorNodeId, top.node];
2321
2321
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2854,7 +2854,7 @@ var import_types8 = require("@neat.is/types");
2854
2854
 
2855
2855
  // src/extract/routes.ts
2856
2856
  init_cjs_shims();
2857
- var import_node_path7 = __toESM(require("path"), 1);
2857
+ var import_node_path8 = __toESM(require("path"), 1);
2858
2858
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
2859
2859
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
2860
2860
  var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
@@ -2880,6 +2880,17 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
2880
2880
  "dist",
2881
2881
  "build",
2882
2882
  ".next",
2883
+ // `_next` is the *emitted* half of the pair above — `next build`'s output as
2884
+ // it is served or exported. `.next` was listed and `_next` wasn't, so an
2885
+ // exported bundle walked in as first-party source: minified chunks, one file
2886
+ // per line, hundreds of KB each (#1254).
2887
+ "_next",
2888
+ // Agent scratch space. `.claude/worktrees/` holds a full checkout per agent
2889
+ // session, so walking it re-extracts the whole repo once per worktree — and
2890
+ // pulls in whatever build output those checkouts happen to carry. It is
2891
+ // gitignored in practice, but the file walk doesn't read .gitignore, so the
2892
+ // name has to be listed here.
2893
+ ".claude",
2883
2894
  // Python virtualenv shapes (issue #344). Walking into a venv pulls in the
2884
2895
  // entire CPython stdlib + every installed package as if it were first-party
2885
2896
  // service code — 20k+ files, none of which the user wrote. The shape names
@@ -3111,6 +3122,15 @@ function noteExtractedDropped(edge) {
3111
3122
  function drainDroppedExtracted() {
3112
3123
  return droppedSink.splice(0, droppedSink.length);
3113
3124
  }
3125
+ var skippedSink = /* @__PURE__ */ new Map();
3126
+ function noteSkippedFile(file) {
3127
+ if (!skippedSink.has(file.path)) skippedSink.set(file.path, file);
3128
+ }
3129
+ function drainSkippedFiles() {
3130
+ const out = [...skippedSink.values()];
3131
+ skippedSink.clear();
3132
+ return out;
3133
+ }
3114
3134
  function isRejectedLogEnabled() {
3115
3135
  const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
3116
3136
  return raw === "1" || raw === "true";
@@ -3124,14 +3144,91 @@ async function writeRejectedExtracted(drops, rejectedPath) {
3124
3144
 
3125
3145
  // src/extract/calls/shared.ts
3126
3146
  init_cjs_shims();
3147
+ var import_node_fs6 = require("fs");
3148
+ var import_node_path6 = __toESM(require("path"), 1);
3149
+ var import_types4 = require("@neat.is/types");
3150
+
3151
+ // src/extract/gitignore.ts
3152
+ init_cjs_shims();
3127
3153
  var import_node_fs5 = require("fs");
3128
3154
  var import_node_path5 = __toESM(require("path"), 1);
3129
- var import_types4 = require("@neat.is/types");
3155
+ var import_ignore = __toESM(require("ignore"), 1);
3156
+ var LAYER_CACHE = /* @__PURE__ */ new Map();
3157
+ async function layerFor(dir) {
3158
+ const cached = LAYER_CACHE.get(dir);
3159
+ if (cached !== void 0) return cached;
3160
+ let layer = null;
3161
+ try {
3162
+ const raw = await import_node_fs5.promises.readFile(import_node_path5.default.join(dir, ".gitignore"), "utf8");
3163
+ layer = { dir, ig: (0, import_ignore.default)().add(raw) };
3164
+ } catch {
3165
+ layer = null;
3166
+ }
3167
+ LAYER_CACHE.set(dir, layer);
3168
+ return layer;
3169
+ }
3170
+ async function isRepoRoot(dir) {
3171
+ try {
3172
+ await import_node_fs5.promises.stat(import_node_path5.default.join(dir, ".git"));
3173
+ return true;
3174
+ } catch {
3175
+ return false;
3176
+ }
3177
+ }
3178
+ async function loadIgnoreChain(startDir) {
3179
+ const dirs = [];
3180
+ let current = import_node_path5.default.resolve(startDir);
3181
+ for (; ; ) {
3182
+ dirs.push(current);
3183
+ if (await isRepoRoot(current)) break;
3184
+ const parent = import_node_path5.default.dirname(current);
3185
+ if (parent === current) break;
3186
+ current = parent;
3187
+ }
3188
+ dirs.reverse();
3189
+ const layers = [];
3190
+ for (const dir of dirs) {
3191
+ const layer = await layerFor(dir);
3192
+ if (layer) layers.push(layer);
3193
+ }
3194
+ return layers;
3195
+ }
3196
+ async function extendIgnoreChain(chain, dir) {
3197
+ const layer = await layerFor(import_node_path5.default.resolve(dir));
3198
+ return layer ? [...chain, layer] : chain;
3199
+ }
3200
+ function isIgnored(chain, fullPath, isDir) {
3201
+ return decide(chain, fullPath, isDir).ignored;
3202
+ }
3203
+ function decide(chain, fullPath, isDir) {
3204
+ if (chain.length === 0) return { ignored: false, subtree: chain };
3205
+ const abs = import_node_path5.default.resolve(fullPath);
3206
+ let ignored = false;
3207
+ let unignoredAt = -1;
3208
+ for (let i = 0; i < chain.length; i++) {
3209
+ const layer = chain[i];
3210
+ const rel = import_node_path5.default.relative(layer.dir, abs);
3211
+ if (!rel || rel.startsWith("..") || import_node_path5.default.isAbsolute(rel)) continue;
3212
+ const candidate = rel.split(import_node_path5.default.sep).join("/") + (isDir ? "/" : "");
3213
+ const result = layer.ig.test(candidate);
3214
+ if (result.ignored) ignored = true;
3215
+ else if (result.unignored) {
3216
+ ignored = false;
3217
+ unignoredAt = i;
3218
+ }
3219
+ }
3220
+ return {
3221
+ ignored,
3222
+ subtree: unignoredAt >= 0 ? chain.slice(unignoredAt) : chain
3223
+ };
3224
+ }
3225
+
3226
+ // src/extract/calls/shared.ts
3130
3227
  function buildServiceHostIndex(services) {
3131
3228
  const knownHosts = /* @__PURE__ */ new Set();
3132
3229
  const hostToNodeId = /* @__PURE__ */ new Map();
3133
3230
  for (const service of services) {
3134
- const base = import_node_path5.default.basename(service.dir);
3231
+ const base = import_node_path6.default.basename(service.dir);
3135
3232
  knownHosts.add(base);
3136
3233
  knownHosts.add(service.pkg.name);
3137
3234
  hostToNodeId.set(base, service.node.id);
@@ -3140,33 +3237,62 @@ function buildServiceHostIndex(services) {
3140
3237
  return { knownHosts, hostToNodeId };
3141
3238
  }
3142
3239
  async function walkSourceFiles(dir, excludeDirs = []) {
3143
- const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
3240
+ const excluded = new Set(excludeDirs.map((d) => import_node_path6.default.resolve(d)));
3144
3241
  const out = [];
3145
- async function walk11(current) {
3146
- const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3242
+ const rootChain = await loadIgnoreChain(dir);
3243
+ async function walk11(current, inherited) {
3244
+ const entries = await import_node_fs6.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3245
+ const chain = entries.some((e) => e.isFile() && e.name === ".gitignore") ? await extendIgnoreChain(inherited, current) : inherited;
3147
3246
  for (const entry2 of entries) {
3148
- const full = import_node_path5.default.join(current, entry2.name);
3247
+ const full = import_node_path6.default.join(current, entry2.name);
3149
3248
  if (entry2.isDirectory()) {
3150
3249
  if (IGNORED_DIRS.has(entry2.name)) continue;
3151
- if (excluded.has(import_node_path5.default.resolve(full))) continue;
3250
+ if (excluded.has(import_node_path6.default.resolve(full))) continue;
3251
+ const verdict = decide(chain, full, true);
3252
+ if (verdict.ignored) continue;
3152
3253
  if (await isPythonVenvDir(full)) continue;
3153
- await walk11(full);
3154
- } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
3254
+ await walk11(full, verdict.subtree);
3255
+ } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path6.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
3155
3256
  // would attribute our instrumentation imports to the user's service.
3156
- !isNeatAuthoredSourceFile(entry2.name)) {
3257
+ !isNeatAuthoredSourceFile(entry2.name) && !isIgnored(chain, full, false)) {
3157
3258
  out.push(full);
3158
3259
  }
3159
3260
  }
3160
3261
  }
3161
- await walk11(dir);
3262
+ await walk11(dir, rootChain.filter((l) => import_node_path6.default.resolve(l.dir) !== import_node_path6.default.resolve(dir)));
3162
3263
  return out;
3163
3264
  }
3265
+ var MINIFIED_LINE_CHARS = 5e3;
3266
+ function hasMinifiedName(filePath) {
3267
+ return /\.min\.(js|mjs|cjs|jsx|ts|tsx)$/i.test(filePath);
3268
+ }
3269
+ function isMinifiedSource(filePath, content) {
3270
+ if (hasMinifiedName(filePath)) return { minified: true, detail: "named *.min.js" };
3271
+ let longest = 0;
3272
+ let start = 0;
3273
+ for (; ; ) {
3274
+ const nl = content.indexOf("\n", start);
3275
+ const len = (nl === -1 ? content.length : nl) - start;
3276
+ if (len > longest) longest = len;
3277
+ if (nl === -1) break;
3278
+ start = nl + 1;
3279
+ }
3280
+ if (longest > MINIFIED_LINE_CHARS) {
3281
+ return { minified: true, detail: `longest line ${longest} chars` };
3282
+ }
3283
+ return { minified: false, detail: "" };
3284
+ }
3164
3285
  async function loadSourceFiles(dir, excludeDirs = []) {
3165
3286
  const paths = await walkSourceFiles(dir, excludeDirs);
3166
3287
  const out = [];
3167
3288
  for (const p of paths) {
3168
3289
  try {
3169
- const content = await import_node_fs5.promises.readFile(p, "utf8");
3290
+ const content = await import_node_fs6.promises.readFile(p, "utf8");
3291
+ const verdict = isMinifiedSource(p, content);
3292
+ if (verdict.minified) {
3293
+ noteSkippedFile({ path: p, reason: "minified", detail: verdict.detail });
3294
+ continue;
3295
+ }
3170
3296
  out.push({ path: p, content });
3171
3297
  } catch {
3172
3298
  }
@@ -3178,15 +3304,18 @@ function lineOf(text, needle) {
3178
3304
  if (idx < 0) return 1;
3179
3305
  return text.slice(0, idx).split("\n").length;
3180
3306
  }
3307
+ var SNIPPET_MAX_CHARS = 120;
3181
3308
  function snippet(text, line) {
3182
3309
  const lines = text.split("\n");
3183
- return (lines[line - 1] ?? "").trim();
3310
+ const raw = (lines[line - 1] ?? "").trim();
3311
+ if (raw.length <= SNIPPET_MAX_CHARS) return raw;
3312
+ return `${raw.slice(0, SNIPPET_MAX_CHARS)}\u2026 (+${raw.length - SNIPPET_MAX_CHARS} chars)`;
3184
3313
  }
3185
3314
  function toPosix(p) {
3186
3315
  return p.split("\\").join("/");
3187
3316
  }
3188
3317
  function languageForPath(relPath) {
3189
- switch (import_node_path5.default.extname(relPath).toLowerCase()) {
3318
+ switch (import_node_path6.default.extname(relPath).toLowerCase()) {
3190
3319
  case ".py":
3191
3320
  return "python";
3192
3321
  case ".go":
@@ -3260,8 +3389,8 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
3260
3389
 
3261
3390
  // src/extract/imports.ts
3262
3391
  init_cjs_shims();
3263
- var import_node_path6 = __toESM(require("path"), 1);
3264
- var import_node_fs6 = require("fs");
3392
+ var import_node_path7 = __toESM(require("path"), 1);
3393
+ var import_node_fs7 = require("fs");
3265
3394
  var import_tree_sitter = __toESM(require("tree-sitter"), 1);
3266
3395
  var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
3267
3396
  var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
@@ -3406,15 +3535,15 @@ function collectPyImports(node, out) {
3406
3535
  }
3407
3536
  async function fileExists(p) {
3408
3537
  try {
3409
- await import_node_fs6.promises.access(p);
3538
+ await import_node_fs7.promises.access(p);
3410
3539
  return true;
3411
3540
  } catch {
3412
3541
  return false;
3413
3542
  }
3414
3543
  }
3415
3544
  function isWithinServiceDir(candidate, serviceDir) {
3416
- const rel = import_node_path6.default.relative(serviceDir, candidate);
3417
- return rel !== "" && !rel.startsWith("..") && !import_node_path6.default.isAbsolute(rel);
3545
+ const rel = import_node_path7.default.relative(serviceDir, candidate);
3546
+ return rel !== "" && !rel.startsWith("..") && !import_node_path7.default.isAbsolute(rel);
3418
3547
  }
3419
3548
  var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
3420
3549
  var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
@@ -3422,22 +3551,22 @@ async function firstExistingCandidate(base, serviceDir) {
3422
3551
  for (const ext of JS_EXTENSIONS) {
3423
3552
  const candidate = base + ext;
3424
3553
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3425
- return toPosix(import_node_path6.default.relative(serviceDir, candidate));
3554
+ return toPosix(import_node_path7.default.relative(serviceDir, candidate));
3426
3555
  }
3427
3556
  }
3428
3557
  for (const indexFile of JS_INDEX_FILES) {
3429
- const candidate = import_node_path6.default.join(base, indexFile);
3558
+ const candidate = import_node_path7.default.join(base, indexFile);
3430
3559
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3431
- return toPosix(import_node_path6.default.relative(serviceDir, candidate));
3560
+ return toPosix(import_node_path7.default.relative(serviceDir, candidate));
3432
3561
  }
3433
3562
  }
3434
3563
  return null;
3435
3564
  }
3436
3565
  async function loadTsPathConfig(serviceDir) {
3437
- const tsconfigPath = import_node_path6.default.join(serviceDir, "tsconfig.json");
3566
+ const tsconfigPath = import_node_path7.default.join(serviceDir, "tsconfig.json");
3438
3567
  let raw;
3439
3568
  try {
3440
- raw = await import_node_fs6.promises.readFile(tsconfigPath, "utf8");
3569
+ raw = await import_node_fs7.promises.readFile(tsconfigPath, "utf8");
3441
3570
  } catch {
3442
3571
  return null;
3443
3572
  }
@@ -3446,7 +3575,7 @@ async function loadTsPathConfig(serviceDir) {
3446
3575
  const paths = parsed.compilerOptions?.paths;
3447
3576
  if (!paths || Object.keys(paths).length === 0) return null;
3448
3577
  const baseUrl = parsed.compilerOptions?.baseUrl;
3449
- return { paths, baseDir: baseUrl ? import_node_path6.default.resolve(serviceDir, baseUrl) : serviceDir };
3578
+ return { paths, baseDir: baseUrl ? import_node_path7.default.resolve(serviceDir, baseUrl) : serviceDir };
3450
3579
  } catch (err) {
3451
3580
  recordExtractionError("import alias resolution", tsconfigPath, err);
3452
3581
  return null;
@@ -3464,11 +3593,11 @@ async function resolveTsAlias(specifier, config, serviceDir) {
3464
3593
  if (suffix === null) continue;
3465
3594
  for (const target of targets) {
3466
3595
  const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
3467
- const resolvedBase = import_node_path6.default.resolve(config.baseDir, targetBase, suffix);
3596
+ const resolvedBase = import_node_path7.default.resolve(config.baseDir, targetBase, suffix);
3468
3597
  const hit = await firstExistingCandidate(resolvedBase, serviceDir);
3469
3598
  if (hit) return hit;
3470
3599
  if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists(resolvedBase)) {
3471
- return toPosix(import_node_path6.default.relative(serviceDir, resolvedBase));
3600
+ return toPosix(import_node_path7.default.relative(serviceDir, resolvedBase));
3472
3601
  }
3473
3602
  }
3474
3603
  }
@@ -3477,18 +3606,18 @@ async function resolveTsAlias(specifier, config, serviceDir) {
3477
3606
  async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3478
3607
  if (!specifier) return null;
3479
3608
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
3480
- const base = import_node_path6.default.resolve(importerDir, specifier);
3481
- const ext = import_node_path6.default.extname(specifier);
3609
+ const base = import_node_path7.default.resolve(importerDir, specifier);
3610
+ const ext = import_node_path7.default.extname(specifier);
3482
3611
  if (ext) {
3483
3612
  if (ext === ".js" || ext === ".jsx") {
3484
3613
  const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
3485
3614
  const tsSibling = base.slice(0, -ext.length) + tsExt;
3486
3615
  if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists(tsSibling)) {
3487
- return toPosix(import_node_path6.default.relative(serviceDir, tsSibling));
3616
+ return toPosix(import_node_path7.default.relative(serviceDir, tsSibling));
3488
3617
  }
3489
3618
  }
3490
3619
  if (isWithinServiceDir(base, serviceDir) && await fileExists(base)) {
3491
- return toPosix(import_node_path6.default.relative(serviceDir, base));
3620
+ return toPosix(import_node_path7.default.relative(serviceDir, base));
3492
3621
  }
3493
3622
  if (!JS_EXTENSIONS.includes(ext)) {
3494
3623
  return firstExistingCandidate(base, serviceDir);
@@ -3503,30 +3632,30 @@ async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3503
3632
  async function resolvePyImport(imp, importerPath, serviceDir) {
3504
3633
  let baseDir;
3505
3634
  if (imp.level > 0) {
3506
- baseDir = import_node_path6.default.dirname(importerPath);
3507
- for (let i = 1; i < imp.level; i++) baseDir = import_node_path6.default.dirname(baseDir);
3635
+ baseDir = import_node_path7.default.dirname(importerPath);
3636
+ for (let i = 1; i < imp.level; i++) baseDir = import_node_path7.default.dirname(baseDir);
3508
3637
  } else {
3509
3638
  baseDir = serviceDir;
3510
3639
  }
3511
- const moduleBase = imp.modulePath ? import_node_path6.default.join(baseDir, imp.modulePath.split(".").join("/")) : baseDir;
3640
+ const moduleBase = imp.modulePath ? import_node_path7.default.join(baseDir, imp.modulePath.split(".").join("/")) : baseDir;
3512
3641
  const resolved = /* @__PURE__ */ new Set();
3513
3642
  let needModuleFile = imp.names.length === 0;
3514
3643
  for (const name of imp.names) {
3515
- const submoduleFile = import_node_path6.default.join(moduleBase, `${name}.py`);
3516
- const subpackageInit = import_node_path6.default.join(moduleBase, name, "__init__.py");
3644
+ const submoduleFile = import_node_path7.default.join(moduleBase, `${name}.py`);
3645
+ const subpackageInit = import_node_path7.default.join(moduleBase, name, "__init__.py");
3517
3646
  if (isWithinServiceDir(submoduleFile, serviceDir) && await fileExists(submoduleFile)) {
3518
- resolved.add(toPosix(import_node_path6.default.relative(serviceDir, submoduleFile)));
3647
+ resolved.add(toPosix(import_node_path7.default.relative(serviceDir, submoduleFile)));
3519
3648
  } else if (isWithinServiceDir(subpackageInit, serviceDir) && await fileExists(subpackageInit)) {
3520
- resolved.add(toPosix(import_node_path6.default.relative(serviceDir, subpackageInit)));
3649
+ resolved.add(toPosix(import_node_path7.default.relative(serviceDir, subpackageInit)));
3521
3650
  } else {
3522
3651
  needModuleFile = true;
3523
3652
  }
3524
3653
  }
3525
3654
  if (needModuleFile) {
3526
- const moduleFileCandidates = imp.modulePath ? [`${moduleBase}.py`, import_node_path6.default.join(moduleBase, "__init__.py")] : [import_node_path6.default.join(moduleBase, "__init__.py")];
3655
+ const moduleFileCandidates = imp.modulePath ? [`${moduleBase}.py`, import_node_path7.default.join(moduleBase, "__init__.py")] : [import_node_path7.default.join(moduleBase, "__init__.py")];
3527
3656
  for (const candidate of moduleFileCandidates) {
3528
3657
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3529
- resolved.add(toPosix(import_node_path6.default.relative(serviceDir, candidate)));
3658
+ resolved.add(toPosix(import_node_path7.default.relative(serviceDir, candidate)));
3530
3659
  break;
3531
3660
  }
3532
3661
  }
@@ -3536,11 +3665,11 @@ async function resolvePyImport(imp, importerPath, serviceDir) {
3536
3665
  async function resolveGoImport(specifier, modulePath, serviceDir) {
3537
3666
  if (specifier !== modulePath && !specifier.startsWith(`${modulePath}/`)) return null;
3538
3667
  const suffix = specifier === modulePath ? "" : specifier.slice(modulePath.length + 1);
3539
- const dir = import_node_path6.default.join(serviceDir, suffix);
3540
- const entries = await import_node_fs6.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3541
- const candidates = entries.filter((entry2) => entry2.isFile() && entry2.name.endsWith(".go") && !entry2.name.endsWith("_test.go")).map((entry2) => import_node_path6.default.join(dir, entry2.name));
3668
+ const dir = import_node_path7.default.join(serviceDir, suffix);
3669
+ const entries = await import_node_fs7.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3670
+ const candidates = entries.filter((entry2) => entry2.isFile() && entry2.name.endsWith(".go") && !entry2.name.endsWith("_test.go")).map((entry2) => import_node_path7.default.join(dir, entry2.name));
3542
3671
  if (candidates.length !== 1) return null;
3543
- return toPosix(import_node_path6.default.relative(serviceDir, candidates[0]));
3672
+ return toPosix(import_node_path7.default.relative(serviceDir, candidates[0]));
3544
3673
  }
3545
3674
  function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
3546
3675
  const importeeFileId = (0, import_types5.fileId)(serviceName, importeeRelPath);
@@ -3570,12 +3699,12 @@ async function addImports(graph, services) {
3570
3699
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
3571
3700
  for (const file of files) {
3572
3701
  if (isTestPath(file.path)) continue;
3573
- const relFile = toPosix(import_node_path6.default.relative(service.dir, file.path));
3702
+ const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
3574
3703
  const importerFileId = (0, import_types5.fileId)(service.pkg.name, relFile);
3575
- const isPython = import_node_path6.default.extname(file.path) === ".py";
3576
- const isGo = import_node_path6.default.extname(file.path) === ".go";
3577
- if (import_node_path6.default.extname(file.path) === ".rb") continue;
3578
- if (import_node_path6.default.extname(file.path) === ".php") continue;
3704
+ const isPython = import_node_path7.default.extname(file.path) === ".py";
3705
+ const isGo = import_node_path7.default.extname(file.path) === ".go";
3706
+ if (import_node_path7.default.extname(file.path) === ".rb") continue;
3707
+ if (import_node_path7.default.extname(file.path) === ".php") continue;
3579
3708
  if (isGo) {
3580
3709
  let goImports = [];
3581
3710
  try {
@@ -3585,7 +3714,7 @@ async function addImports(graph, services) {
3585
3714
  recordExtractionError("import extraction", file.path, err);
3586
3715
  continue;
3587
3716
  }
3588
- const goMod = await import_node_fs6.promises.readFile(import_node_path6.default.join(service.dir, "go.mod"), "utf8").catch(() => "");
3717
+ const goMod = await import_node_fs7.promises.readFile(import_node_path7.default.join(service.dir, "go.mod"), "utf8").catch(() => "");
3589
3718
  const modulePath = goMod.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
3590
3719
  if (!modulePath) continue;
3591
3720
  for (const imp of goImports) {
@@ -3629,7 +3758,7 @@ async function addImports(graph, services) {
3629
3758
  continue;
3630
3759
  }
3631
3760
  for (const imp of jsImports) {
3632
- const resolved = await resolveJsImport(imp.specifier, import_node_path6.default.dirname(file.path), service.dir, tsPaths);
3761
+ const resolved = await resolveJsImport(imp.specifier, import_node_path7.default.dirname(file.path), service.dir, tsPaths);
3633
3762
  if (!resolved) continue;
3634
3763
  edgesAdded += emitImportEdge(
3635
3764
  graph,
@@ -3754,8 +3883,8 @@ function chiRoutesFromSource(source, parser) {
3754
3883
  chiWalk(tree.rootNode, "", out);
3755
3884
  return out;
3756
3885
  }
3757
- function stripChiRegex(path81) {
3758
- return path81.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3886
+ function stripChiRegex(path82) {
3887
+ return path82.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3759
3888
  }
3760
3889
  function chiWalk(node, prefix, out) {
3761
3890
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4095,7 +4224,7 @@ function isNextPagesApiFile(relFile) {
4095
4224
  if (pagesIdx === -1 || segs[pagesIdx + 1] !== "api") return false;
4096
4225
  const base = segs[segs.length - 1] ?? "";
4097
4226
  if (/^_(app|document|middleware)\./.test(base)) return false;
4098
- return JS_ROUTE_EXTENSIONS.has(import_node_path7.default.extname(base));
4227
+ return JS_ROUTE_EXTENSIONS.has(import_node_path8.default.extname(base));
4099
4228
  }
4100
4229
  function nextSegment(seg) {
4101
4230
  if (seg.startsWith("(") && seg.endsWith(")")) return null;
@@ -4427,9 +4556,9 @@ function rubyRocketRoute(args) {
4427
4556
  if (!pair || pair.type !== "pair") continue;
4428
4557
  const k = pair.childForFieldName("key");
4429
4558
  if (k?.type !== "string") continue;
4430
- const path81 = rubyLiteral(k);
4431
- if (path81 === null) continue;
4432
- return { path: path81, target: rubyLiteral(pair.childForFieldName("value")) };
4559
+ const path82 = rubyLiteral(k);
4560
+ if (path82 === null) continue;
4561
+ return { path: path82, target: rubyLiteral(pair.childForFieldName("value")) };
4433
4562
  }
4434
4563
  return null;
4435
4564
  }
@@ -5277,12 +5406,12 @@ async function expressMountPrefixes(files, serviceDir, tsPaths) {
5277
5406
  const jsParser = makeJsParser2();
5278
5407
  const fileInfo = /* @__PURE__ */ new Map();
5279
5408
  for (const f of files) {
5280
- if (!JS_ROUTE_EXTENSIONS.has(import_node_path7.default.extname(f.path))) continue;
5409
+ if (!JS_ROUTE_EXTENSIONS.has(import_node_path8.default.extname(f.path))) continue;
5281
5410
  if (isTestPath(f.path)) continue;
5282
- const rel = toPosix(import_node_path7.default.relative(serviceDir, f.path));
5411
+ const rel = toPosix(import_node_path8.default.relative(serviceDir, f.path));
5283
5412
  try {
5284
5413
  const tree = parseSource2(jsParser, f.content);
5285
- fileInfo.set(rel, analyzeExpressFile(tree.rootNode, import_node_path7.default.dirname(f.path)));
5414
+ fileInfo.set(rel, analyzeExpressFile(tree.rootNode, import_node_path8.default.dirname(f.path)));
5286
5415
  } catch {
5287
5416
  }
5288
5417
  }
@@ -5381,14 +5510,14 @@ async function addRoutes(graph, services) {
5381
5510
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
5382
5511
  for (const file of files) {
5383
5512
  if (isTestPath(file.path)) continue;
5384
- const ext = import_node_path7.default.extname(file.path);
5513
+ const ext = import_node_path8.default.extname(file.path);
5385
5514
  const isPy = ext === ".py";
5386
5515
  const isGo = ext === ".go";
5387
5516
  const isRb = ext === ".rb";
5388
5517
  const isPhp = ext === ".php";
5389
5518
  const isRs = ext === ".rs";
5390
5519
  if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
5391
- const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
5520
+ const relFile = toPosix(import_node_path8.default.relative(service.dir, file.path));
5392
5521
  let routes;
5393
5522
  try {
5394
5523
  if (isPhp) {
@@ -5896,7 +6025,7 @@ function languageForExt(relPath) {
5896
6025
  function relPathForRuntimeFile(filepath, serviceNode, scanPath) {
5897
6026
  let p = toPosix2(filepath).replace(/^file:\/\//, "");
5898
6027
  if (scanPath && scanPath.length > 0) {
5899
- const absRoot = toPosix2(import_node_path8.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
6028
+ const absRoot = toPosix2(import_node_path9.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
5900
6029
  const anchor = absRoot.endsWith("/") ? absRoot : `${absRoot}/`;
5901
6030
  if (p.startsWith(anchor)) return p.slice(anchor.length);
5902
6031
  }
@@ -5924,10 +6053,10 @@ function resolveDistToSrc(absFilepath, line) {
5924
6053
  entry2 = null;
5925
6054
  const mapPath = `${absFilepath}.map`;
5926
6055
  try {
5927
- if ((0, import_node_fs7.existsSync)(mapPath)) {
5928
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(mapPath, "utf8"));
6056
+ if ((0, import_node_fs8.existsSync)(mapPath)) {
6057
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(mapPath, "utf8"));
5929
6058
  const consumer = new sourceMapJs.SourceMapConsumer(raw);
5930
- entry2 = { consumer, dir: import_node_path8.default.dirname(mapPath) };
6059
+ entry2 = { consumer, dir: import_node_path9.default.dirname(mapPath) };
5931
6060
  }
5932
6061
  } catch {
5933
6062
  entry2 = null;
@@ -5947,7 +6076,7 @@ function resolveDistToSrc(absFilepath, line) {
5947
6076
  }
5948
6077
  if (!pos || !pos.source) return null;
5949
6078
  const root = entry2.consumer.sourceRoot ?? "";
5950
- const resolved = import_node_path8.default.resolve(entry2.dir, root, pos.source);
6079
+ const resolved = import_node_path9.default.resolve(entry2.dir, root, pos.source);
5951
6080
  return { filepath: resolved, ...pos.line ? { line: pos.line } : {} };
5952
6081
  } catch {
5953
6082
  return null;
@@ -6491,8 +6620,8 @@ function emitIncidentEvent(project, ev) {
6491
6620
  });
6492
6621
  }
6493
6622
  async function appendErrorEvent(ctx, ev) {
6494
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(ctx.errorsPath), { recursive: true });
6495
- await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
6623
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(ctx.errorsPath), { recursive: true });
6624
+ await import_node_fs8.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
6496
6625
  emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
6497
6626
  }
6498
6627
  async function appendConnectorIncident(errorsPath, input, project) {
@@ -6507,8 +6636,8 @@ async function appendConnectorIncident(errorsPath, input, project) {
6507
6636
  ...input.attributes && Object.keys(input.attributes).length > 0 ? { attributes: input.attributes } : {},
6508
6637
  affectedNode: input.affectedNode
6509
6638
  };
6510
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6511
- await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6639
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(errorsPath), { recursive: true });
6640
+ await import_node_fs8.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6512
6641
  if (project) emitIncidentEvent(project, ev);
6513
6642
  }
6514
6643
  function landIncidentCallSite(span, callSite, trusted, graph) {
@@ -6630,8 +6759,8 @@ function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJ
6630
6759
  return async (span) => {
6631
6760
  const ev = buildErrorEventForReceiver(span, graph, scanPath);
6632
6761
  if (!ev) return;
6633
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6634
- await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6762
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(errorsPath), { recursive: true });
6763
+ await import_node_fs8.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6635
6764
  emitIncidentEvent(project, ev);
6636
6765
  };
6637
6766
  }
@@ -7131,13 +7260,13 @@ async function markStaleEdges(graph, options = {}) {
7131
7260
  return { count: events.length, events };
7132
7261
  }
7133
7262
  async function appendStaleEvents(staleEventsPath, events) {
7134
- await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(staleEventsPath), { recursive: true });
7263
+ await import_node_fs8.promises.mkdir(import_node_path9.default.dirname(staleEventsPath), { recursive: true });
7135
7264
  const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
7136
- await import_node_fs7.promises.appendFile(staleEventsPath, lines, "utf8");
7265
+ await import_node_fs8.promises.appendFile(staleEventsPath, lines, "utf8");
7137
7266
  }
7138
7267
  async function readStaleEvents(staleEventsPath) {
7139
7268
  try {
7140
- const raw = await import_node_fs7.promises.readFile(staleEventsPath, "utf8");
7269
+ const raw = await import_node_fs8.promises.readFile(staleEventsPath, "utf8");
7141
7270
  return raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
7142
7271
  } catch (err) {
7143
7272
  if (err.code === "ENOENT") return [];
@@ -7173,7 +7302,7 @@ function startStalenessLoop(graph, options = {}) {
7173
7302
  var INCIDENT_READ_MAX_BYTES = 32 * 1024 * 1024;
7174
7303
  var INCIDENT_READ_MAX_EVENTS = 5e3;
7175
7304
  async function readErrorFileTail(errorsPath, maxBytes) {
7176
- const handle = await import_node_fs7.promises.open(errorsPath, "r");
7305
+ const handle = await import_node_fs8.promises.open(errorsPath, "r");
7177
7306
  try {
7178
7307
  const { size } = await handle.stat();
7179
7308
  if (size <= maxBytes) {
@@ -7298,16 +7427,16 @@ function mergeSnapshot(graph, snapshot) {
7298
7427
 
7299
7428
  // src/extract/services.ts
7300
7429
  init_cjs_shims();
7301
- var import_node_fs18 = require("fs");
7302
- var import_node_path20 = __toESM(require("path"), 1);
7303
- var import_ignore = __toESM(require("ignore"), 1);
7430
+ var import_node_fs19 = require("fs");
7431
+ var import_node_path21 = __toESM(require("path"), 1);
7432
+ var import_ignore2 = __toESM(require("ignore"), 1);
7304
7433
  var import_minimatch2 = require("minimatch");
7305
7434
  var import_types18 = require("@neat.is/types");
7306
7435
 
7307
7436
  // src/extract/python.ts
7308
7437
  init_cjs_shims();
7309
- var import_node_fs8 = require("fs");
7310
- var import_node_path9 = __toESM(require("path"), 1);
7438
+ var import_node_fs9 = require("fs");
7439
+ var import_node_path10 = __toESM(require("path"), 1);
7311
7440
  var import_smol_toml = require("smol-toml");
7312
7441
  var REQUIREMENT_LINE = /^\s*([A-Za-z0-9_.-]+)(?:\[[^\]]*\])?\s*(?:(==)\s*([A-Za-z0-9_.+-]+))?/;
7313
7442
  function parseRequirementsTxt(content) {
@@ -7340,25 +7469,25 @@ function depsFromPyProject(pyproject) {
7340
7469
  return out;
7341
7470
  }
7342
7471
  async function discoverPythonService(serviceDir) {
7343
- const pyprojectPath = import_node_path9.default.join(serviceDir, "pyproject.toml");
7344
- const requirementsPath = import_node_path9.default.join(serviceDir, "requirements.txt");
7345
- const setupPath = import_node_path9.default.join(serviceDir, "setup.py");
7472
+ const pyprojectPath = import_node_path10.default.join(serviceDir, "pyproject.toml");
7473
+ const requirementsPath = import_node_path10.default.join(serviceDir, "requirements.txt");
7474
+ const setupPath = import_node_path10.default.join(serviceDir, "setup.py");
7346
7475
  const hasPyproject = await exists(pyprojectPath);
7347
7476
  const hasRequirements = await exists(requirementsPath);
7348
7477
  const hasSetup = await exists(setupPath);
7349
7478
  if (!hasPyproject && !hasRequirements && !hasSetup) return null;
7350
- let name = import_node_path9.default.basename(serviceDir);
7479
+ let name = import_node_path10.default.basename(serviceDir);
7351
7480
  let version;
7352
7481
  const dependencies = {};
7353
7482
  if (hasPyproject) {
7354
- const raw = await import_node_fs8.promises.readFile(pyprojectPath, "utf8");
7483
+ const raw = await import_node_fs9.promises.readFile(pyprojectPath, "utf8");
7355
7484
  const pyproject = (0, import_smol_toml.parse)(raw);
7356
7485
  name = pyproject.project?.name ?? pyproject.tool?.poetry?.name ?? name;
7357
7486
  version = pyproject.project?.version ?? pyproject.tool?.poetry?.version ?? void 0;
7358
7487
  Object.assign(dependencies, depsFromPyProject(pyproject));
7359
7488
  }
7360
7489
  if (hasRequirements) {
7361
- const raw = await import_node_fs8.promises.readFile(requirementsPath, "utf8");
7490
+ const raw = await import_node_fs9.promises.readFile(requirementsPath, "utf8");
7362
7491
  Object.assign(dependencies, parseRequirementsTxt(raw));
7363
7492
  }
7364
7493
  return { name, version, dependencies };
@@ -7373,8 +7502,8 @@ function pythonToPackage(service) {
7373
7502
 
7374
7503
  // src/extract/go.ts
7375
7504
  init_cjs_shims();
7376
- var import_node_fs9 = require("fs");
7377
- var import_node_path10 = __toESM(require("path"), 1);
7505
+ var import_node_fs10 = require("fs");
7506
+ var import_node_path11 = __toESM(require("path"), 1);
7378
7507
  var import_types9 = require("@neat.is/types");
7379
7508
  function parseGoMod(source) {
7380
7509
  const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
@@ -7401,7 +7530,7 @@ function goFramework(deps) {
7401
7530
  async function discoverGoService(scanPath, dir) {
7402
7531
  let raw;
7403
7532
  try {
7404
- raw = await import_node_fs9.promises.readFile(import_node_path10.default.join(dir, "go.mod"), "utf8");
7533
+ raw = await import_node_fs10.promises.readFile(import_node_path11.default.join(dir, "go.mod"), "utf8");
7405
7534
  } catch {
7406
7535
  return null;
7407
7536
  }
@@ -7416,7 +7545,7 @@ async function discoverGoService(scanPath, dir) {
7416
7545
  name,
7417
7546
  language: "go",
7418
7547
  dependencies: mod.dependencies,
7419
- repoPath: import_node_path10.default.relative(scanPath, dir),
7548
+ repoPath: import_node_path11.default.relative(scanPath, dir),
7420
7549
  ...framework ? { framework } : {}
7421
7550
  };
7422
7551
  return { pkg, dir, node };
@@ -7424,8 +7553,8 @@ async function discoverGoService(scanPath, dir) {
7424
7553
 
7425
7554
  // src/extract/ruby.ts
7426
7555
  init_cjs_shims();
7427
- var import_node_fs10 = require("fs");
7428
- var import_node_path11 = __toESM(require("path"), 1);
7556
+ var import_node_fs11 = require("fs");
7557
+ var import_node_path12 = __toESM(require("path"), 1);
7429
7558
  var import_types10 = require("@neat.is/types");
7430
7559
  function parseGemfile(source) {
7431
7560
  const dependencies = {};
@@ -7442,12 +7571,12 @@ function parseGemfile(source) {
7442
7571
  async function discoverRubyService(scanPath, dir) {
7443
7572
  let raw;
7444
7573
  try {
7445
- raw = await import_node_fs10.promises.readFile(import_node_path11.default.join(dir, "Gemfile"), "utf8");
7574
+ raw = await import_node_fs11.promises.readFile(import_node_path12.default.join(dir, "Gemfile"), "utf8");
7446
7575
  } catch {
7447
7576
  return null;
7448
7577
  }
7449
7578
  const gemfile = parseGemfile(raw);
7450
- const name = import_node_path11.default.basename(dir);
7579
+ const name = import_node_path12.default.basename(dir);
7451
7580
  const pkg = { name, dependencies: gemfile.dependencies };
7452
7581
  const node = {
7453
7582
  id: (0, import_types10.serviceId)(name),
@@ -7455,7 +7584,7 @@ async function discoverRubyService(scanPath, dir) {
7455
7584
  name,
7456
7585
  language: "ruby",
7457
7586
  dependencies: gemfile.dependencies,
7458
- repoPath: import_node_path11.default.relative(scanPath, dir),
7587
+ repoPath: import_node_path12.default.relative(scanPath, dir),
7459
7588
  ...gemfile.dependencies["rails"] !== void 0 ? { framework: "rails" } : {}
7460
7589
  };
7461
7590
  return { pkg, dir, node };
@@ -7463,7 +7592,7 @@ async function discoverRubyService(scanPath, dir) {
7463
7592
 
7464
7593
  // src/extract/php.ts
7465
7594
  init_cjs_shims();
7466
- var import_node_path12 = __toESM(require("path"), 1);
7595
+ var import_node_path13 = __toESM(require("path"), 1);
7467
7596
  var import_types11 = require("@neat.is/types");
7468
7597
  function parseComposerJson(composer) {
7469
7598
  return {
@@ -7476,12 +7605,12 @@ function parseComposerJson(composer) {
7476
7605
  async function discoverPhpService(scanPath, dir) {
7477
7606
  let composer;
7478
7607
  try {
7479
- composer = await readJson(import_node_path12.default.join(dir, "composer.json"));
7608
+ composer = await readJson(import_node_path13.default.join(dir, "composer.json"));
7480
7609
  } catch {
7481
7610
  return null;
7482
7611
  }
7483
7612
  const { dependencies } = parseComposerJson(composer);
7484
- const name = import_node_path12.default.basename(dir);
7613
+ const name = import_node_path13.default.basename(dir);
7485
7614
  const pkg = { name, dependencies };
7486
7615
  const node = {
7487
7616
  id: (0, import_types11.serviceId)(name),
@@ -7489,7 +7618,7 @@ async function discoverPhpService(scanPath, dir) {
7489
7618
  name,
7490
7619
  language: "php",
7491
7620
  dependencies,
7492
- repoPath: import_node_path12.default.relative(scanPath, dir),
7621
+ repoPath: import_node_path13.default.relative(scanPath, dir),
7493
7622
  ...dependencies["laravel/framework"] !== void 0 ? { framework: "laravel" } : {}
7494
7623
  };
7495
7624
  return { pkg, dir, node };
@@ -7497,23 +7626,23 @@ async function discoverPhpService(scanPath, dir) {
7497
7626
 
7498
7627
  // src/extract/csharp.ts
7499
7628
  init_cjs_shims();
7500
- var import_node_fs11 = require("fs");
7501
- var import_node_path13 = __toESM(require("path"), 1);
7629
+ var import_node_fs12 = require("fs");
7630
+ var import_node_path14 = __toESM(require("path"), 1);
7502
7631
  var import_types12 = require("@neat.is/types");
7503
7632
  var CSHARP_PROJECT_EXTS = /* @__PURE__ */ new Set([".csproj", ".sln"]);
7504
7633
  async function projectFiles(dir) {
7505
- const entries = await import_node_fs11.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7634
+ const entries = await import_node_fs12.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7506
7635
  const out = [];
7507
7636
  for (const entry2 of entries) {
7508
7637
  if (!entry2.isFile()) continue;
7509
- if (CSHARP_PROJECT_EXTS.has(import_node_path13.default.extname(entry2.name).toLowerCase())) out.push(entry2.name);
7638
+ if (CSHARP_PROJECT_EXTS.has(import_node_path14.default.extname(entry2.name).toLowerCase())) out.push(entry2.name);
7510
7639
  }
7511
7640
  return out.sort();
7512
7641
  }
7513
7642
  var NESTED_PROJECT_DIR = "src";
7514
7643
  async function resolveProjectDir(dir) {
7515
7644
  if ((await projectFiles(dir)).length > 0) return dir;
7516
- const nested = import_node_path13.default.join(dir, NESTED_PROJECT_DIR);
7645
+ const nested = import_node_path14.default.join(dir, NESTED_PROJECT_DIR);
7517
7646
  if ((await projectFiles(nested)).length > 0) return nested;
7518
7647
  return null;
7519
7648
  }
@@ -7538,10 +7667,10 @@ async function discoverCsharpService(scanPath, dir) {
7538
7667
  if (projectDir === null) return null;
7539
7668
  const projects = await projectFiles(projectDir);
7540
7669
  const csproj = projects.find((p) => p.toLowerCase().endsWith(".csproj"));
7541
- const name = csproj ? import_node_path13.default.basename(csproj, import_node_path13.default.extname(csproj)) : import_node_path13.default.basename(dir);
7670
+ const name = csproj ? import_node_path14.default.basename(csproj, import_node_path14.default.extname(csproj)) : import_node_path14.default.basename(dir);
7542
7671
  let dependencies = {};
7543
7672
  if (csproj) {
7544
- const raw = await import_node_fs11.promises.readFile(import_node_path13.default.join(projectDir, csproj), "utf8").catch(() => "");
7673
+ const raw = await import_node_fs12.promises.readFile(import_node_path14.default.join(projectDir, csproj), "utf8").catch(() => "");
7545
7674
  dependencies = parseCsproj(raw).dependencies;
7546
7675
  }
7547
7676
  const pkg = { name, dependencies };
@@ -7553,20 +7682,20 @@ async function discoverCsharpService(scanPath, dir) {
7553
7682
  dependencies,
7554
7683
  // Anchor on the directory that holds the project and its `.cs` files, so file
7555
7684
  // attribution reaches the source whether it sits at the root or under `src/`.
7556
- repoPath: import_node_path13.default.relative(scanPath, projectDir)
7685
+ repoPath: import_node_path14.default.relative(scanPath, projectDir)
7557
7686
  };
7558
7687
  return { pkg, dir: projectDir, node };
7559
7688
  }
7560
7689
 
7561
7690
  // src/extract/java.ts
7562
7691
  init_cjs_shims();
7563
- var import_node_fs12 = require("fs");
7564
- var import_node_path14 = __toESM(require("path"), 1);
7692
+ var import_node_fs13 = require("fs");
7693
+ var import_node_path15 = __toESM(require("path"), 1);
7565
7694
  var import_types13 = require("@neat.is/types");
7566
7695
  var JAVA_MANIFESTS = ["pom.xml", "build.gradle", "build.gradle.kts"];
7567
7696
  async function hasJavaManifest(dir) {
7568
7697
  for (const manifest of JAVA_MANIFESTS) {
7569
- if (await exists(import_node_path14.default.join(dir, manifest))) return true;
7698
+ if (await exists(import_node_path15.default.join(dir, manifest))) return true;
7570
7699
  }
7571
7700
  return false;
7572
7701
  }
@@ -7601,19 +7730,19 @@ function parseGradle(source) {
7601
7730
  return { dependencies };
7602
7731
  }
7603
7732
  async function discoverJavaService(scanPath, dir) {
7604
- const pomPath = import_node_path14.default.join(dir, "pom.xml");
7605
- const gradlePath = import_node_path14.default.join(dir, "build.gradle");
7606
- const gradleKtsPath = import_node_path14.default.join(dir, "build.gradle.kts");
7733
+ const pomPath = import_node_path15.default.join(dir, "pom.xml");
7734
+ const gradlePath = import_node_path15.default.join(dir, "build.gradle");
7735
+ const gradleKtsPath = import_node_path15.default.join(dir, "build.gradle.kts");
7607
7736
  let manifest = null;
7608
7737
  if (await exists(pomPath)) {
7609
- manifest = parsePomXml(await import_node_fs12.promises.readFile(pomPath, "utf8").catch(() => ""));
7738
+ manifest = parsePomXml(await import_node_fs13.promises.readFile(pomPath, "utf8").catch(() => ""));
7610
7739
  } else if (await exists(gradlePath)) {
7611
- manifest = parseGradle(await import_node_fs12.promises.readFile(gradlePath, "utf8").catch(() => ""));
7740
+ manifest = parseGradle(await import_node_fs13.promises.readFile(gradlePath, "utf8").catch(() => ""));
7612
7741
  } else if (await exists(gradleKtsPath)) {
7613
- manifest = parseGradle(await import_node_fs12.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7742
+ manifest = parseGradle(await import_node_fs13.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7614
7743
  }
7615
7744
  if (!manifest) return null;
7616
- const name = manifest.name?.trim() || import_node_path14.default.basename(dir);
7745
+ const name = manifest.name?.trim() || import_node_path15.default.basename(dir);
7617
7746
  const dependencies = manifest.dependencies;
7618
7747
  const pkg = { name, dependencies };
7619
7748
  const node = {
@@ -7622,15 +7751,15 @@ async function discoverJavaService(scanPath, dir) {
7622
7751
  name,
7623
7752
  language: "java",
7624
7753
  dependencies,
7625
- repoPath: import_node_path14.default.relative(scanPath, dir)
7754
+ repoPath: import_node_path15.default.relative(scanPath, dir)
7626
7755
  };
7627
7756
  return { pkg, dir, node };
7628
7757
  }
7629
7758
 
7630
7759
  // src/extract/kotlin.ts
7631
7760
  init_cjs_shims();
7632
- var import_node_fs13 = require("fs");
7633
- var import_node_path15 = __toESM(require("path"), 1);
7761
+ var import_node_fs14 = require("fs");
7762
+ var import_node_path16 = __toESM(require("path"), 1);
7634
7763
  var import_types14 = require("@neat.is/types");
7635
7764
  var KOTLIN_SOURCE_EXT = ".kt";
7636
7765
  var JAVA_SOURCE_EXT = ".java";
@@ -7640,15 +7769,15 @@ async function dominantJvmSourceLanguage(dir) {
7640
7769
  let java = 0;
7641
7770
  async function recurse(current, depth) {
7642
7771
  if (depth > SOURCE_PROBE_MAX_DEPTH) return;
7643
- const entries = await import_node_fs13.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7772
+ const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7644
7773
  for (const entry2 of entries) {
7645
7774
  if (entry2.isDirectory()) {
7646
7775
  if (IGNORED_DIRS.has(entry2.name)) continue;
7647
- await recurse(import_node_path15.default.join(current, entry2.name), depth + 1);
7776
+ await recurse(import_node_path16.default.join(current, entry2.name), depth + 1);
7648
7777
  continue;
7649
7778
  }
7650
7779
  if (!entry2.isFile()) continue;
7651
- const ext = import_node_path15.default.extname(entry2.name).toLowerCase();
7780
+ const ext = import_node_path16.default.extname(entry2.name).toLowerCase();
7652
7781
  if (ext === KOTLIN_SOURCE_EXT) kt++;
7653
7782
  else if (ext === JAVA_SOURCE_EXT) java++;
7654
7783
  }
@@ -7659,17 +7788,17 @@ async function dominantJvmSourceLanguage(dir) {
7659
7788
  return void 0;
7660
7789
  }
7661
7790
  async function readJvmManifest(dir) {
7662
- const pomPath = import_node_path15.default.join(dir, JAVA_MANIFESTS[0]);
7663
- const gradlePath = import_node_path15.default.join(dir, JAVA_MANIFESTS[1]);
7664
- const gradleKtsPath = import_node_path15.default.join(dir, JAVA_MANIFESTS[2]);
7791
+ const pomPath = import_node_path16.default.join(dir, JAVA_MANIFESTS[0]);
7792
+ const gradlePath = import_node_path16.default.join(dir, JAVA_MANIFESTS[1]);
7793
+ const gradleKtsPath = import_node_path16.default.join(dir, JAVA_MANIFESTS[2]);
7665
7794
  if (await exists(pomPath)) {
7666
- return parsePomXml(await import_node_fs13.promises.readFile(pomPath, "utf8").catch(() => ""));
7795
+ return parsePomXml(await import_node_fs14.promises.readFile(pomPath, "utf8").catch(() => ""));
7667
7796
  }
7668
7797
  if (await exists(gradlePath)) {
7669
- return parseGradle(await import_node_fs13.promises.readFile(gradlePath, "utf8").catch(() => ""));
7798
+ return parseGradle(await import_node_fs14.promises.readFile(gradlePath, "utf8").catch(() => ""));
7670
7799
  }
7671
7800
  if (await exists(gradleKtsPath)) {
7672
- return parseGradle(await import_node_fs13.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7801
+ return parseGradle(await import_node_fs14.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7673
7802
  }
7674
7803
  return null;
7675
7804
  }
@@ -7678,7 +7807,7 @@ async function discoverKotlinService(scanPath, dir) {
7678
7807
  if (await dominantJvmSourceLanguage(dir) !== "kotlin") return null;
7679
7808
  const manifest = await readJvmManifest(dir);
7680
7809
  if (!manifest) return null;
7681
- const name = manifest.name?.trim() || import_node_path15.default.basename(dir);
7810
+ const name = manifest.name?.trim() || import_node_path16.default.basename(dir);
7682
7811
  const dependencies = manifest.dependencies;
7683
7812
  const pkg = { name, dependencies };
7684
7813
  const node = {
@@ -7687,20 +7816,20 @@ async function discoverKotlinService(scanPath, dir) {
7687
7816
  name,
7688
7817
  language: "kotlin",
7689
7818
  dependencies,
7690
- repoPath: import_node_path15.default.relative(scanPath, dir)
7819
+ repoPath: import_node_path16.default.relative(scanPath, dir)
7691
7820
  };
7692
7821
  return { pkg, dir, node };
7693
7822
  }
7694
7823
 
7695
7824
  // src/extract/rust.ts
7696
7825
  init_cjs_shims();
7697
- var import_node_fs14 = require("fs");
7698
- var import_node_path16 = __toESM(require("path"), 1);
7826
+ var import_node_fs15 = require("fs");
7827
+ var import_node_path17 = __toESM(require("path"), 1);
7699
7828
  var import_smol_toml2 = require("smol-toml");
7700
7829
  var import_types15 = require("@neat.is/types");
7701
7830
  var CARGO_MANIFEST = "Cargo.toml";
7702
7831
  async function hasRustManifest(dir) {
7703
- return exists(import_node_path16.default.join(dir, CARGO_MANIFEST));
7832
+ return exists(import_node_path17.default.join(dir, CARGO_MANIFEST));
7704
7833
  }
7705
7834
  function parseCargoToml(source) {
7706
7835
  const dependencies = {};
@@ -7725,10 +7854,10 @@ function parseCargoToml(source) {
7725
7854
  return { name, dependencies };
7726
7855
  }
7727
7856
  async function discoverRustService(scanPath, dir) {
7728
- const cargoPath = import_node_path16.default.join(dir, CARGO_MANIFEST);
7857
+ const cargoPath = import_node_path17.default.join(dir, CARGO_MANIFEST);
7729
7858
  if (!await exists(cargoPath)) return null;
7730
- const manifest = parseCargoToml(await import_node_fs14.promises.readFile(cargoPath, "utf8").catch(() => ""));
7731
- const name = manifest.name?.trim() || import_node_path16.default.basename(dir);
7859
+ const manifest = parseCargoToml(await import_node_fs15.promises.readFile(cargoPath, "utf8").catch(() => ""));
7860
+ const name = manifest.name?.trim() || import_node_path17.default.basename(dir);
7732
7861
  const dependencies = manifest.dependencies;
7733
7862
  const pkg = { name, dependencies };
7734
7863
  const node = {
@@ -7737,19 +7866,19 @@ async function discoverRustService(scanPath, dir) {
7737
7866
  name,
7738
7867
  language: "rust",
7739
7868
  dependencies,
7740
- repoPath: import_node_path16.default.relative(scanPath, dir)
7869
+ repoPath: import_node_path17.default.relative(scanPath, dir)
7741
7870
  };
7742
7871
  return { pkg, dir, node };
7743
7872
  }
7744
7873
 
7745
7874
  // src/extract/cpp.ts
7746
7875
  init_cjs_shims();
7747
- var import_node_fs15 = require("fs");
7748
- var import_node_path17 = __toESM(require("path"), 1);
7876
+ var import_node_fs16 = require("fs");
7877
+ var import_node_path18 = __toESM(require("path"), 1);
7749
7878
  var import_types16 = require("@neat.is/types");
7750
7879
  var CMAKE_MANIFEST = "CMakeLists.txt";
7751
7880
  async function hasCppManifest(dir) {
7752
- return exists(import_node_path17.default.join(dir, CMAKE_MANIFEST));
7881
+ return exists(import_node_path18.default.join(dir, CMAKE_MANIFEST));
7753
7882
  }
7754
7883
  function parseCMakeProjectName(source) {
7755
7884
  const withoutComments = source.replace(/#.*$/gm, "");
@@ -7757,10 +7886,10 @@ function parseCMakeProjectName(source) {
7757
7886
  return m?.[1];
7758
7887
  }
7759
7888
  async function discoverCppService(scanPath, dir) {
7760
- const cmakePath = import_node_path17.default.join(dir, CMAKE_MANIFEST);
7889
+ const cmakePath = import_node_path18.default.join(dir, CMAKE_MANIFEST);
7761
7890
  if (!await exists(cmakePath)) return null;
7762
- const source = await import_node_fs15.promises.readFile(cmakePath, "utf8").catch(() => "");
7763
- const name = parseCMakeProjectName(source)?.trim() || import_node_path17.default.basename(dir);
7891
+ const source = await import_node_fs16.promises.readFile(cmakePath, "utf8").catch(() => "");
7892
+ const name = parseCMakeProjectName(source)?.trim() || import_node_path18.default.basename(dir);
7764
7893
  const dependencies = {};
7765
7894
  const pkg = { name, dependencies };
7766
7895
  const node = {
@@ -7769,15 +7898,15 @@ async function discoverCppService(scanPath, dir) {
7769
7898
  name,
7770
7899
  language: "cpp",
7771
7900
  dependencies,
7772
- repoPath: import_node_path17.default.relative(scanPath, dir)
7901
+ repoPath: import_node_path18.default.relative(scanPath, dir)
7773
7902
  };
7774
7903
  return { pkg, dir, node };
7775
7904
  }
7776
7905
 
7777
7906
  // src/extract/dockerfile-service.ts
7778
7907
  init_cjs_shims();
7779
- var import_node_fs16 = require("fs");
7780
- var import_node_path18 = __toESM(require("path"), 1);
7908
+ var import_node_fs17 = require("fs");
7909
+ var import_node_path19 = __toESM(require("path"), 1);
7781
7910
  var import_types17 = require("@neat.is/types");
7782
7911
  function languageForSourceExt(ext) {
7783
7912
  switch (ext) {
@@ -7819,12 +7948,12 @@ function languageForSourceExt(ext) {
7819
7948
  }
7820
7949
  var LANGUAGE_PRECEDENCE = ["typescript", "javascript", "python", "go", "ruby", "php", "csharp", "java", "kotlin", "rust", "cpp"];
7821
7950
  async function inferServiceLanguage(dir) {
7822
- const entries = await import_node_fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7951
+ const entries = await import_node_fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7823
7952
  const counts = /* @__PURE__ */ new Map();
7824
7953
  for (const entry2 of entries) {
7825
7954
  if (!entry2.isFile()) continue;
7826
7955
  if (isNeatAuthoredSourceFile(entry2.name)) continue;
7827
- const ext = import_node_path18.default.extname(entry2.name).toLowerCase();
7956
+ const ext = import_node_path19.default.extname(entry2.name).toLowerCase();
7828
7957
  if (!SERVICE_FILE_EXTENSIONS.has(ext)) continue;
7829
7958
  const lang = languageForSourceExt(ext);
7830
7959
  if (!lang) continue;
@@ -7843,7 +7972,7 @@ async function inferServiceLanguage(dir) {
7843
7972
  return best;
7844
7973
  }
7845
7974
  async function hasLanguageManifest(dir) {
7846
- 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
7975
+ 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
7847
7976
  // rather than probing a fixed filename (ADR-196).
7848
7977
  await hasCsharpProject(dir) || // Java (and its JVM sibling Kotlin, ADR-199) key on the same Maven / Gradle
7849
7978
  // build manifest (ADR-197), so this one check stands the Dockerfile fallback
@@ -7853,15 +7982,15 @@ async function hasLanguageManifest(dir) {
7853
7982
  await hasCppManifest(dir);
7854
7983
  }
7855
7984
  async function hasDockerfileDeclaredService(dir) {
7856
- if (!await exists(import_node_path18.default.join(dir, "Dockerfile"))) return false;
7985
+ if (!await exists(import_node_path19.default.join(dir, "Dockerfile"))) return false;
7857
7986
  return await inferServiceLanguage(dir) !== null;
7858
7987
  }
7859
7988
  async function discoverDockerfileService(scanPath, dir) {
7860
- if (!await exists(import_node_path18.default.join(dir, "Dockerfile"))) return null;
7989
+ if (!await exists(import_node_path19.default.join(dir, "Dockerfile"))) return null;
7861
7990
  if (await hasLanguageManifest(dir)) return null;
7862
7991
  const language = await inferServiceLanguage(dir);
7863
7992
  if (!language) return null;
7864
- const name = import_node_path18.default.basename(dir);
7993
+ const name = import_node_path19.default.basename(dir);
7865
7994
  const pkg = { name, dependencies: {} };
7866
7995
  const node = {
7867
7996
  id: (0, import_types17.serviceId)(name),
@@ -7869,24 +7998,24 @@ async function discoverDockerfileService(scanPath, dir) {
7869
7998
  name,
7870
7999
  language,
7871
8000
  dependencies: {},
7872
- repoPath: import_node_path18.default.relative(scanPath, dir)
8001
+ repoPath: import_node_path19.default.relative(scanPath, dir)
7873
8002
  };
7874
8003
  return { pkg, dir, node };
7875
8004
  }
7876
8005
 
7877
8006
  // src/extract/owners.ts
7878
8007
  init_cjs_shims();
7879
- var import_node_fs17 = require("fs");
7880
- var import_node_path19 = __toESM(require("path"), 1);
8008
+ var import_node_fs18 = require("fs");
8009
+ var import_node_path20 = __toESM(require("path"), 1);
7881
8010
  var import_minimatch = require("minimatch");
7882
8011
  async function loadCodeowners(scanPath) {
7883
8012
  const candidates = [
7884
- import_node_path19.default.join(scanPath, "CODEOWNERS"),
7885
- import_node_path19.default.join(scanPath, ".github", "CODEOWNERS")
8013
+ import_node_path20.default.join(scanPath, "CODEOWNERS"),
8014
+ import_node_path20.default.join(scanPath, ".github", "CODEOWNERS")
7886
8015
  ];
7887
8016
  for (const file of candidates) {
7888
8017
  if (await exists(file)) {
7889
- const raw = await import_node_fs17.promises.readFile(file, "utf8");
8018
+ const raw = await import_node_fs18.promises.readFile(file, "utf8");
7890
8019
  return parseCodeowners(raw);
7891
8020
  }
7892
8021
  }
@@ -7904,7 +8033,7 @@ function parseCodeowners(raw) {
7904
8033
  return { rules };
7905
8034
  }
7906
8035
  function matchOwner(file, repoPath) {
7907
- const normalized = repoPath.split(import_node_path19.default.sep).join("/");
8036
+ const normalized = repoPath.split(import_node_path20.default.sep).join("/");
7908
8037
  for (const rule of file.rules) {
7909
8038
  if (matchesPattern(rule.pattern, normalized)) return rule.owners;
7910
8039
  }
@@ -7920,7 +8049,7 @@ function matchesPattern(rawPattern, repoPath) {
7920
8049
  return false;
7921
8050
  }
7922
8051
  async function readPackageJsonAuthor(serviceDir) {
7923
- const pkgPath = import_node_path19.default.join(serviceDir, "package.json");
8052
+ const pkgPath = import_node_path20.default.join(serviceDir, "package.json");
7924
8053
  if (!await exists(pkgPath)) return null;
7925
8054
  try {
7926
8055
  const pkg = await readJson(pkgPath);
@@ -7957,36 +8086,36 @@ function workspaceGlobs(pkg) {
7957
8086
  return null;
7958
8087
  }
7959
8088
  async function hasPythonManifest(dir) {
7960
- 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"));
8089
+ 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"));
7961
8090
  }
7962
8091
  async function hasGoManifest(dir) {
7963
- return exists(import_node_path20.default.join(dir, "go.mod"));
8092
+ return exists(import_node_path21.default.join(dir, "go.mod"));
7964
8093
  }
7965
8094
  async function hasRubyManifest(dir) {
7966
- return exists(import_node_path20.default.join(dir, "Gemfile"));
8095
+ return exists(import_node_path21.default.join(dir, "Gemfile"));
7967
8096
  }
7968
8097
  async function hasPhpManifest(dir) {
7969
- return exists(import_node_path20.default.join(dir, "composer.json"));
8098
+ return exists(import_node_path21.default.join(dir, "composer.json"));
7970
8099
  }
7971
8100
  async function hasCsharpManifest(dir) {
7972
8101
  return hasCsharpProject(dir);
7973
8102
  }
7974
8103
  async function loadGitignore(scanPath) {
7975
- const gitignorePath = import_node_path20.default.join(scanPath, ".gitignore");
8104
+ const gitignorePath = import_node_path21.default.join(scanPath, ".gitignore");
7976
8105
  if (!await exists(gitignorePath)) return null;
7977
- const raw = await import_node_fs18.promises.readFile(gitignorePath, "utf8");
7978
- return (0, import_ignore.default)().add(raw);
8106
+ const raw = await import_node_fs19.promises.readFile(gitignorePath, "utf8");
8107
+ return (0, import_ignore2.default)().add(raw);
7979
8108
  }
7980
8109
  async function walkDirs(start, scanPath, options, visit) {
7981
8110
  async function recurse(current, depth) {
7982
8111
  if (depth > options.maxDepth) return;
7983
- const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8112
+ const entries = await import_node_fs19.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7984
8113
  for (const entry2 of entries) {
7985
8114
  if (!entry2.isDirectory()) continue;
7986
8115
  if (IGNORED_DIRS.has(entry2.name)) continue;
7987
- const child = import_node_path20.default.join(current, entry2.name);
8116
+ const child = import_node_path21.default.join(current, entry2.name);
7988
8117
  if (options.ig) {
7989
- const rel = import_node_path20.default.relative(scanPath, child).split(import_node_path20.default.sep).join("/");
8118
+ const rel = import_node_path21.default.relative(scanPath, child).split(import_node_path21.default.sep).join("/");
7990
8119
  if (rel && options.ig.ignores(rel + "/")) continue;
7991
8120
  }
7992
8121
  if (await isPythonVenvDir(child)) continue;
@@ -8002,8 +8131,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
8002
8131
  for (const raw of globs) {
8003
8132
  const pattern = raw.replace(/^\.\//, "");
8004
8133
  if (!pattern.includes("*")) {
8005
- const candidate = import_node_path20.default.join(scanPath, pattern);
8006
- if (await exists(import_node_path20.default.join(candidate, "package.json"))) found.add(candidate);
8134
+ const candidate = import_node_path21.default.join(scanPath, pattern);
8135
+ if (await exists(import_node_path21.default.join(candidate, "package.json"))) found.add(candidate);
8007
8136
  continue;
8008
8137
  }
8009
8138
  const segments = pattern.split("/");
@@ -8012,13 +8141,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
8012
8141
  if (seg.includes("*")) break;
8013
8142
  staticSegments.push(seg);
8014
8143
  }
8015
- const start = import_node_path20.default.join(scanPath, ...staticSegments);
8144
+ const start = import_node_path21.default.join(scanPath, ...staticSegments);
8016
8145
  if (!await exists(start)) continue;
8017
8146
  const hasDoubleStar = pattern.includes("**");
8018
8147
  const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
8019
8148
  await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
8020
- const rel = import_node_path20.default.relative(scanPath, dir).split(import_node_path20.default.sep).join("/");
8021
- if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path20.default.join(dir, "package.json"))) {
8149
+ const rel = import_node_path21.default.relative(scanPath, dir).split(import_node_path21.default.sep).join("/");
8150
+ if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path21.default.join(dir, "package.json"))) {
8022
8151
  found.add(dir);
8023
8152
  }
8024
8153
  });
@@ -8041,18 +8170,18 @@ function detectJsFramework(pkg) {
8041
8170
  async function detectJsServiceLanguage(dir, pkg) {
8042
8171
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
8043
8172
  if (deps["typescript"] !== void 0) return "typescript";
8044
- const entries = await import_node_fs18.promises.readdir(dir).catch(() => []);
8173
+ const entries = await import_node_fs19.promises.readdir(dir).catch(() => []);
8045
8174
  if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
8046
8175
  return "javascript";
8047
8176
  }
8048
8177
  async function discoverNodeService(scanPath, dir) {
8049
- const pkgPath = import_node_path20.default.join(dir, "package.json");
8178
+ const pkgPath = import_node_path21.default.join(dir, "package.json");
8050
8179
  if (!await exists(pkgPath)) return null;
8051
8180
  let pkg;
8052
8181
  try {
8053
8182
  pkg = await readJson(pkgPath);
8054
8183
  } catch (err) {
8055
- recordExtractionError("services", import_node_path20.default.relative(scanPath, pkgPath), err);
8184
+ recordExtractionError("services", import_node_path21.default.relative(scanPath, pkgPath), err);
8056
8185
  return null;
8057
8186
  }
8058
8187
  if (!pkg.name) return null;
@@ -8065,7 +8194,7 @@ async function discoverNodeService(scanPath, dir) {
8065
8194
  language,
8066
8195
  version: pkg.version,
8067
8196
  dependencies: pkg.dependencies ?? {},
8068
- repoPath: import_node_path20.default.relative(scanPath, dir),
8197
+ repoPath: import_node_path21.default.relative(scanPath, dir),
8069
8198
  ...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
8070
8199
  ...framework ? { framework } : {}
8071
8200
  };
@@ -8082,12 +8211,12 @@ async function discoverPyService(scanPath, dir) {
8082
8211
  language: "python",
8083
8212
  version: py.version,
8084
8213
  dependencies: py.dependencies,
8085
- repoPath: import_node_path20.default.relative(scanPath, dir)
8214
+ repoPath: import_node_path21.default.relative(scanPath, dir)
8086
8215
  };
8087
8216
  return { pkg, dir, node };
8088
8217
  }
8089
8218
  async function discoverServices(scanPath) {
8090
- const rootPkgPath = import_node_path20.default.join(scanPath, "package.json");
8219
+ const rootPkgPath = import_node_path21.default.join(scanPath, "package.json");
8091
8220
  let rootPkg = null;
8092
8221
  if (await exists(rootPkgPath)) {
8093
8222
  try {
@@ -8095,7 +8224,7 @@ async function discoverServices(scanPath) {
8095
8224
  } catch (err) {
8096
8225
  recordExtractionError(
8097
8226
  "services workspaces",
8098
- import_node_path20.default.relative(scanPath, rootPkgPath),
8227
+ import_node_path21.default.relative(scanPath, rootPkgPath),
8099
8228
  err
8100
8229
  );
8101
8230
  }
@@ -8116,7 +8245,7 @@ async function discoverServices(scanPath) {
8116
8245
  scanPath,
8117
8246
  { maxDepth: parseScanDepth(), ig },
8118
8247
  async (dir) => {
8119
- if (await exists(import_node_path20.default.join(dir, "package.json"))) {
8248
+ if (await exists(import_node_path21.default.join(dir, "package.json"))) {
8120
8249
  candidateDirs.push(dir);
8121
8250
  } 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)) {
8122
8251
  candidateDirs.push(dir);
@@ -8144,8 +8273,8 @@ async function discoverServices(scanPath) {
8144
8273
  seenDirs.add(service.dir);
8145
8274
  const existingDir = seen.get(service.node.name);
8146
8275
  if (existingDir !== void 0) {
8147
- const a = import_node_path20.default.relative(scanPath, existingDir) || ".";
8148
- const b = import_node_path20.default.relative(scanPath, dir) || ".";
8276
+ const a = import_node_path21.default.relative(scanPath, existingDir) || ".";
8277
+ const b = import_node_path21.default.relative(scanPath, dir) || ".";
8149
8278
  console.warn(
8150
8279
  `[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
8151
8280
  );
@@ -8159,14 +8288,14 @@ async function discoverServices(scanPath) {
8159
8288
  const owner = await computeServiceOwner(codeowners, service.node.repoPath, service.dir);
8160
8289
  if (owner !== void 0) service.node.owner = owner;
8161
8290
  }
8162
- const resolvedDirs = out.map((s) => import_node_path20.default.resolve(s.dir));
8291
+ const resolvedDirs = out.map((s) => import_node_path21.default.resolve(s.dir));
8163
8292
  for (let i = 0; i < out.length; i++) {
8164
8293
  const self = resolvedDirs[i];
8165
8294
  const nested = [];
8166
8295
  for (let j = 0; j < out.length; j++) {
8167
8296
  if (i === j) continue;
8168
8297
  const other = resolvedDirs[j];
8169
- if (other.startsWith(self + import_node_path20.default.sep)) nested.push(other);
8298
+ if (other.startsWith(self + import_node_path21.default.sep)) nested.push(other);
8170
8299
  }
8171
8300
  out[i].excludeDirs = nested;
8172
8301
  }
@@ -8193,8 +8322,8 @@ function addServiceNodes(graph, services) {
8193
8322
 
8194
8323
  // src/extract/aliases.ts
8195
8324
  init_cjs_shims();
8196
- var import_node_path21 = __toESM(require("path"), 1);
8197
- var import_node_fs19 = require("fs");
8325
+ var import_node_path22 = __toESM(require("path"), 1);
8326
+ var import_node_fs20 = require("fs");
8198
8327
  var import_yaml2 = require("yaml");
8199
8328
  var import_types19 = require("@neat.is/types");
8200
8329
  var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
@@ -8221,14 +8350,14 @@ function indexServicesByName(services) {
8221
8350
  const map = /* @__PURE__ */ new Map();
8222
8351
  for (const s of services) {
8223
8352
  map.set(s.node.name, s.node.id);
8224
- map.set(import_node_path21.default.basename(s.dir), s.node.id);
8353
+ map.set(import_node_path22.default.basename(s.dir), s.node.id);
8225
8354
  }
8226
8355
  return map;
8227
8356
  }
8228
8357
  async function collectComposeAliases(graph, scanPath, serviceIndex) {
8229
8358
  let composePath = null;
8230
8359
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
8231
- const abs = import_node_path21.default.join(scanPath, name);
8360
+ const abs = import_node_path22.default.join(scanPath, name);
8232
8361
  if (await exists(abs)) {
8233
8362
  composePath = abs;
8234
8363
  break;
@@ -8241,7 +8370,7 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
8241
8370
  } catch (err) {
8242
8371
  recordExtractionError(
8243
8372
  "aliases compose",
8244
- import_node_path21.default.relative(scanPath, composePath),
8373
+ import_node_path22.default.relative(scanPath, composePath),
8245
8374
  err
8246
8375
  );
8247
8376
  return;
@@ -8284,11 +8413,11 @@ function parseDockerfileLabels(content) {
8284
8413
  }
8285
8414
  async function collectDockerfileAliases(graph, services) {
8286
8415
  for (const service of services) {
8287
- const dockerfilePath = import_node_path21.default.join(service.dir, "Dockerfile");
8416
+ const dockerfilePath = import_node_path22.default.join(service.dir, "Dockerfile");
8288
8417
  if (!await exists(dockerfilePath)) continue;
8289
8418
  let content;
8290
8419
  try {
8291
- content = await import_node_fs19.promises.readFile(dockerfilePath, "utf8");
8420
+ content = await import_node_fs20.promises.readFile(dockerfilePath, "utf8");
8292
8421
  } catch (err) {
8293
8422
  recordExtractionError("aliases dockerfile", dockerfilePath, err);
8294
8423
  continue;
@@ -8300,15 +8429,15 @@ async function collectDockerfileAliases(graph, services) {
8300
8429
  async function walkYamlFiles(start, depth = 0, max = 5) {
8301
8430
  if (depth > max) return [];
8302
8431
  const out = [];
8303
- const entries = await import_node_fs19.promises.readdir(start, { withFileTypes: true }).catch(() => []);
8432
+ const entries = await import_node_fs20.promises.readdir(start, { withFileTypes: true }).catch(() => []);
8304
8433
  for (const entry2 of entries) {
8305
8434
  if (entry2.isDirectory()) {
8306
8435
  if (IGNORED_DIRS.has(entry2.name)) continue;
8307
- const child = import_node_path21.default.join(start, entry2.name);
8436
+ const child = import_node_path22.default.join(start, entry2.name);
8308
8437
  if (await isPythonVenvDir(child)) continue;
8309
8438
  out.push(...await walkYamlFiles(child, depth + 1, max));
8310
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path21.default.extname(entry2.name))) {
8311
- out.push(import_node_path21.default.join(start, entry2.name));
8439
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path22.default.extname(entry2.name))) {
8440
+ out.push(import_node_path22.default.join(start, entry2.name));
8312
8441
  }
8313
8442
  }
8314
8443
  return out;
@@ -8335,7 +8464,7 @@ function k8sServiceTarget(doc, byName) {
8335
8464
  async function collectK8sAliases(graph, scanPath, serviceIndex) {
8336
8465
  const files = await walkYamlFiles(scanPath);
8337
8466
  for (const file of files) {
8338
- const content = await import_node_fs19.promises.readFile(file, "utf8");
8467
+ const content = await import_node_fs20.promises.readFile(file, "utf8");
8339
8468
  let docs;
8340
8469
  try {
8341
8470
  docs = (0, import_yaml2.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -8360,14 +8489,14 @@ async function addServiceAliases(graph, scanPath, services) {
8360
8489
 
8361
8490
  // src/extract/files.ts
8362
8491
  init_cjs_shims();
8363
- var import_node_path22 = __toESM(require("path"), 1);
8492
+ var import_node_path23 = __toESM(require("path"), 1);
8364
8493
  async function addFiles(graph, services) {
8365
8494
  let nodesAdded = 0;
8366
8495
  let edgesAdded = 0;
8367
8496
  for (const service of services) {
8368
8497
  const filePaths = await walkSourceFiles(service.dir, service.excludeDirs);
8369
8498
  for (const filePath of filePaths) {
8370
- const relPath = toPosix(import_node_path22.default.relative(service.dir, filePath));
8499
+ const relPath = toPosix(import_node_path23.default.relative(service.dir, filePath));
8371
8500
  const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
8372
8501
  graph,
8373
8502
  service.pkg.name,
@@ -8383,7 +8512,7 @@ async function addFiles(graph, services) {
8383
8512
 
8384
8513
  // src/extract/symbols.ts
8385
8514
  init_cjs_shims();
8386
- var import_node_path23 = __toESM(require("path"), 1);
8515
+ var import_node_path24 = __toESM(require("path"), 1);
8387
8516
  var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
8388
8517
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
8389
8518
  var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
@@ -9135,10 +9264,10 @@ async function addSymbols(graph, services) {
9135
9264
  for (const service of services) {
9136
9265
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
9137
9266
  for (const file of files) {
9138
- const ext = import_node_path23.default.extname(file.path);
9267
+ const ext = import_node_path24.default.extname(file.path);
9139
9268
  const parser = parserForExt5(ext);
9140
9269
  if (!parser) continue;
9141
- const relPath = toPosix(import_node_path23.default.relative(service.dir, file.path));
9270
+ const relPath = toPosix(import_node_path24.default.relative(service.dir, file.path));
9142
9271
  let defs;
9143
9272
  try {
9144
9273
  const tree = parseSource3(parser, file.content);
@@ -9198,7 +9327,7 @@ async function addSymbols(graph, services) {
9198
9327
 
9199
9328
  // src/extract/symbol-edges.ts
9200
9329
  init_cjs_shims();
9201
- var import_node_path24 = __toESM(require("path"), 1);
9330
+ var import_node_path25 = __toESM(require("path"), 1);
9202
9331
  var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
9203
9332
  var import_types21 = require("@neat.is/types");
9204
9333
  function extendsInfo(classHeritage) {
@@ -9305,10 +9434,10 @@ async function addSymbolEdges(graph, services) {
9305
9434
  const tsPaths = await loadTsPathConfig(service.dir);
9306
9435
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
9307
9436
  for (const file of files) {
9308
- const parser = parserForExt5(import_node_path24.default.extname(file.path));
9437
+ const parser = parserForExt5(import_node_path25.default.extname(file.path));
9309
9438
  if (!parser) continue;
9310
- const relPath = toPosix(import_node_path24.default.relative(service.dir, file.path));
9311
- const fileDir = import_node_path24.default.dirname(file.path);
9439
+ const relPath = toPosix(import_node_path25.default.relative(service.dir, file.path));
9440
+ const fileDir = import_node_path25.default.dirname(file.path);
9312
9441
  let root;
9313
9442
  try {
9314
9443
  root = parseSource3(parser, file.content).rootNode;
@@ -9443,7 +9572,7 @@ async function addSymbolEdges(graph, services) {
9443
9572
 
9444
9573
  // src/extract/actions.ts
9445
9574
  init_cjs_shims();
9446
- var import_node_path25 = __toESM(require("path"), 1);
9575
+ var import_node_path26 = __toESM(require("path"), 1);
9447
9576
  var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
9448
9577
  var import_types22 = require("@neat.is/types");
9449
9578
  function stringLiteralText2(node) {
@@ -9590,9 +9719,9 @@ async function addServerActions(graph, services) {
9590
9719
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
9591
9720
  for (const file of files) {
9592
9721
  if (isTestPath(file.path)) continue;
9593
- const parser = parserForExt5(import_node_path25.default.extname(file.path));
9722
+ const parser = parserForExt5(import_node_path26.default.extname(file.path));
9594
9723
  if (!parser) continue;
9595
- const relPath = toPosix(import_node_path25.default.relative(service.dir, file.path));
9724
+ const relPath = toPosix(import_node_path26.default.relative(service.dir, file.path));
9596
9725
  let root;
9597
9726
  try {
9598
9727
  root = parseSource3(parser, file.content).rootNode;
@@ -9653,10 +9782,10 @@ async function addServerActions(graph, services) {
9653
9782
  }
9654
9783
  for (const file of files) {
9655
9784
  if (isTestPath(file.path)) continue;
9656
- const parser = parserForExt5(import_node_path25.default.extname(file.path));
9785
+ const parser = parserForExt5(import_node_path26.default.extname(file.path));
9657
9786
  if (!parser) continue;
9658
- const relPath = toPosix(import_node_path25.default.relative(service.dir, file.path));
9659
- const fileDir = import_node_path25.default.dirname(file.path);
9787
+ const relPath = toPosix(import_node_path26.default.relative(service.dir, file.path));
9788
+ const fileDir = import_node_path26.default.dirname(file.path);
9660
9789
  let root;
9661
9790
  try {
9662
9791
  root = parseSource3(parser, file.content).rootNode;
@@ -9709,14 +9838,14 @@ async function addServerActions(graph, services) {
9709
9838
 
9710
9839
  // src/extract/databases/index.ts
9711
9840
  init_cjs_shims();
9712
- var import_node_path34 = __toESM(require("path"), 1);
9841
+ var import_node_path35 = __toESM(require("path"), 1);
9713
9842
  var import_types23 = require("@neat.is/types");
9714
9843
 
9715
9844
  // src/extract/databases/db-config-yaml.ts
9716
9845
  init_cjs_shims();
9717
- var import_node_path26 = __toESM(require("path"), 1);
9846
+ var import_node_path27 = __toESM(require("path"), 1);
9718
9847
  async function parse(serviceDir) {
9719
- const yamlPath = import_node_path26.default.join(serviceDir, "db-config.yaml");
9848
+ const yamlPath = import_node_path27.default.join(serviceDir, "db-config.yaml");
9720
9849
  if (!await exists(yamlPath)) return [];
9721
9850
  const raw = await readYaml(yamlPath);
9722
9851
  return [
@@ -9734,13 +9863,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
9734
9863
 
9735
9864
  // src/extract/databases/dotenv.ts
9736
9865
  init_cjs_shims();
9737
- var import_node_fs21 = require("fs");
9738
- var import_node_path28 = __toESM(require("path"), 1);
9866
+ var import_node_fs22 = require("fs");
9867
+ var import_node_path29 = __toESM(require("path"), 1);
9739
9868
 
9740
9869
  // src/extract/databases/shared.ts
9741
9870
  init_cjs_shims();
9742
- var import_node_fs20 = require("fs");
9743
- var import_node_path27 = __toESM(require("path"), 1);
9871
+ var import_node_fs21 = require("fs");
9872
+ var import_node_path28 = __toESM(require("path"), 1);
9744
9873
  function schemeToEngine(scheme) {
9745
9874
  const s = scheme.toLowerCase().split("+")[0];
9746
9875
  switch (s) {
@@ -9779,18 +9908,18 @@ function parseConnectionString(url) {
9779
9908
  }
9780
9909
  async function readIfExists(filePath) {
9781
9910
  try {
9782
- return await import_node_fs20.promises.readFile(filePath, "utf8");
9911
+ return await import_node_fs21.promises.readFile(filePath, "utf8");
9783
9912
  } catch {
9784
9913
  return null;
9785
9914
  }
9786
9915
  }
9787
9916
  async function resolveEnvVar(serviceDir, name) {
9788
- const entries = await import_node_fs20.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9917
+ const entries = await import_node_fs21.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9789
9918
  const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
9790
9919
  const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
9791
9920
  envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
9792
9921
  for (const fileName of envNames) {
9793
- const content = await readIfExists(import_node_path27.default.join(serviceDir, fileName));
9922
+ const content = await readIfExists(import_node_path28.default.join(serviceDir, fileName));
9794
9923
  if (!content) continue;
9795
9924
  for (const line of content.split("\n")) {
9796
9925
  const trimmed = line.trim();
@@ -9809,7 +9938,7 @@ async function resolveEnvVar(serviceDir, name) {
9809
9938
  }
9810
9939
  async function findFirst(serviceDir, candidates) {
9811
9940
  for (const rel of candidates) {
9812
- const abs = import_node_path27.default.join(serviceDir, rel);
9941
+ const abs = import_node_path28.default.join(serviceDir, rel);
9813
9942
  const content = await readIfExists(abs);
9814
9943
  if (content !== null) return abs;
9815
9944
  }
@@ -9867,15 +9996,15 @@ function parseDotenvLine(line) {
9867
9996
  return { key, value };
9868
9997
  }
9869
9998
  async function parse2(serviceDir) {
9870
- const entries = await import_node_fs21.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9999
+ const entries = await import_node_fs22.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9871
10000
  const configs = [];
9872
10001
  const seen = /* @__PURE__ */ new Set();
9873
10002
  for (const entry2 of entries) {
9874
10003
  if (!entry2.isFile()) continue;
9875
10004
  const match = isConfigFile(entry2.name);
9876
10005
  if (!match.match || match.fileType !== "env") continue;
9877
- const filePath = import_node_path28.default.join(serviceDir, entry2.name);
9878
- const content = await import_node_fs21.promises.readFile(filePath, "utf8");
10006
+ const filePath = import_node_path29.default.join(serviceDir, entry2.name);
10007
+ const content = await import_node_fs22.promises.readFile(filePath, "utf8");
9879
10008
  for (const line of content.split("\n")) {
9880
10009
  const parsed = parseDotenvLine(line);
9881
10010
  if (!parsed) continue;
@@ -9894,9 +10023,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
9894
10023
 
9895
10024
  // src/extract/databases/prisma.ts
9896
10025
  init_cjs_shims();
9897
- var import_node_path29 = __toESM(require("path"), 1);
10026
+ var import_node_path30 = __toESM(require("path"), 1);
9898
10027
  async function parse3(serviceDir) {
9899
- const schemaPath = import_node_path29.default.join(serviceDir, "prisma", "schema.prisma");
10028
+ const schemaPath = import_node_path30.default.join(serviceDir, "prisma", "schema.prisma");
9900
10029
  const content = await readIfExists(schemaPath);
9901
10030
  if (!content) return [];
9902
10031
  const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
@@ -10054,10 +10183,10 @@ var knexParser = { name: "knex", parse: parse5 };
10054
10183
 
10055
10184
  // src/extract/databases/ormconfig.ts
10056
10185
  init_cjs_shims();
10057
- var import_node_path30 = __toESM(require("path"), 1);
10186
+ var import_node_path31 = __toESM(require("path"), 1);
10058
10187
  async function parse6(serviceDir) {
10059
10188
  for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
10060
- const abs = import_node_path30.default.join(serviceDir, candidate);
10189
+ const abs = import_node_path31.default.join(serviceDir, candidate);
10061
10190
  if (!await exists(abs)) continue;
10062
10191
  const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
10063
10192
  const entries = Array.isArray(raw) ? raw : [raw];
@@ -10117,9 +10246,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
10117
10246
 
10118
10247
  // src/extract/databases/sequelize.ts
10119
10248
  init_cjs_shims();
10120
- var import_node_path31 = __toESM(require("path"), 1);
10249
+ var import_node_path32 = __toESM(require("path"), 1);
10121
10250
  async function parse8(serviceDir) {
10122
- const configPath = import_node_path31.default.join(serviceDir, "config", "config.json");
10251
+ const configPath = import_node_path32.default.join(serviceDir, "config", "config.json");
10123
10252
  if (!await exists(configPath)) return [];
10124
10253
  const raw = await readJson(configPath);
10125
10254
  const out = [];
@@ -10146,8 +10275,8 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
10146
10275
 
10147
10276
  // src/extract/databases/csharp.ts
10148
10277
  init_cjs_shims();
10149
- var import_node_fs22 = require("fs");
10150
- var import_node_path32 = __toESM(require("path"), 1);
10278
+ var import_node_fs23 = require("fs");
10279
+ var import_node_path33 = __toESM(require("path"), 1);
10151
10280
  var CS_EXT = ".cs";
10152
10281
  var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
10153
10282
  var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
@@ -10194,12 +10323,12 @@ function parseRedisEndpoint(raw) {
10194
10323
  return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
10195
10324
  }
10196
10325
  async function resolveEnvUpTree(startDir, name) {
10197
- let dir = import_node_path32.default.resolve(startDir);
10326
+ let dir = import_node_path33.default.resolve(startDir);
10198
10327
  for (let depth = 0; depth < 12; depth++) {
10199
10328
  const value = await resolveEnvVar(dir, name);
10200
10329
  if (value !== null) return value;
10201
- const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
10202
- const parent = import_node_path32.default.dirname(dir);
10330
+ const atRepoRoot = await import_node_fs23.promises.access(import_node_path33.default.join(dir, ".git")).then(() => true).catch(() => false);
10331
+ const parent = import_node_path33.default.dirname(dir);
10203
10332
  if (atRepoRoot || parent === dir) break;
10204
10333
  dir = parent;
10205
10334
  }
@@ -10254,12 +10383,12 @@ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
10254
10383
  }
10255
10384
  async function parse9(serviceDir) {
10256
10385
  const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
10257
- (f) => import_node_path32.default.extname(f) === CS_EXT
10386
+ (f) => import_node_path33.default.extname(f) === CS_EXT
10258
10387
  );
10259
10388
  if (files.length === 0) return [];
10260
10389
  const sources = [];
10261
10390
  for (const file of files) {
10262
- const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
10391
+ const content = await import_node_fs23.promises.readFile(file, "utf8").catch(() => null);
10263
10392
  if (content !== null) sources.push({ file, content });
10264
10393
  }
10265
10394
  let pgGateFile = null;
@@ -10300,7 +10429,7 @@ var csharpParser = { name: "csharp", parse: parse9 };
10300
10429
 
10301
10430
  // src/extract/databases/docker-compose.ts
10302
10431
  init_cjs_shims();
10303
- var import_node_path33 = __toESM(require("path"), 1);
10432
+ var import_node_path34 = __toESM(require("path"), 1);
10304
10433
  function portFromService(svc) {
10305
10434
  for (const raw of svc.ports ?? []) {
10306
10435
  const str = String(raw);
@@ -10327,7 +10456,7 @@ function databaseFromEnv(svc) {
10327
10456
  }
10328
10457
  async function parse10(serviceDir) {
10329
10458
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
10330
- const abs = import_node_path33.default.join(serviceDir, name);
10459
+ const abs = import_node_path34.default.join(serviceDir, name);
10331
10460
  if (!await exists(abs)) continue;
10332
10461
  const raw = await readYaml(abs);
10333
10462
  if (!raw?.services) return [];
@@ -10500,7 +10629,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10500
10629
  discoveredVia: mergedDiscoveredVia
10501
10630
  });
10502
10631
  }
10503
- const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
10632
+ const relConfigFile = toPosix(import_node_path35.default.relative(service.dir, config.sourceFile));
10504
10633
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10505
10634
  graph,
10506
10635
  service.pkg.name,
@@ -10509,7 +10638,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10509
10638
  );
10510
10639
  nodesAdded += fn;
10511
10640
  edgesAdded += fe;
10512
- const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
10641
+ const evidenceFile = toPosix(import_node_path35.default.relative(scanPath, config.sourceFile));
10513
10642
  const edge = {
10514
10643
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
10515
10644
  source: fileNodeId,
@@ -10530,15 +10659,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10530
10659
  if (allConfigs.length === 1) {
10531
10660
  const primary = allConfigs[0];
10532
10661
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
10533
- const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
10662
+ const relPath = import_node_path35.default.relative(scanPath, primary.sourceFile);
10534
10663
  const cfgId = (0, import_types23.configId)(relPath);
10535
10664
  if (!graph.hasNode(cfgId)) {
10536
10665
  const cfgNode = {
10537
10666
  id: cfgId,
10538
10667
  type: import_types23.NodeType.ConfigNode,
10539
- name: import_node_path34.default.basename(primary.sourceFile),
10668
+ name: import_node_path35.default.basename(primary.sourceFile),
10540
10669
  path: relPath,
10541
- fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
10670
+ fileType: isConfigFile(import_node_path35.default.basename(primary.sourceFile)).fileType || "config"
10542
10671
  };
10543
10672
  graph.addNode(cfgId, cfgNode);
10544
10673
  nodesAdded++;
@@ -10579,19 +10708,19 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10579
10708
 
10580
10709
  // src/extract/configs.ts
10581
10710
  init_cjs_shims();
10582
- var import_node_fs23 = require("fs");
10583
- var import_node_path35 = __toESM(require("path"), 1);
10711
+ var import_node_fs24 = require("fs");
10712
+ var import_node_path36 = __toESM(require("path"), 1);
10584
10713
  var import_types24 = require("@neat.is/types");
10585
10714
  async function walkConfigFiles(dir, excludeDirs = []) {
10586
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10715
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10587
10716
  const out = [];
10588
10717
  async function walk11(current) {
10589
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
10718
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true });
10590
10719
  for (const entry2 of entries) {
10591
- const full = import_node_path35.default.join(current, entry2.name);
10720
+ const full = import_node_path36.default.join(current, entry2.name);
10592
10721
  if (entry2.isDirectory()) {
10593
10722
  if (IGNORED_DIRS.has(entry2.name)) continue;
10594
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10723
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
10595
10724
  if (await isPythonVenvDir(full)) continue;
10596
10725
  await walk11(full);
10597
10726
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
@@ -10608,19 +10737,19 @@ async function addConfigNodes(graph, services, scanPath) {
10608
10737
  for (const service of services) {
10609
10738
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
10610
10739
  for (const file of configFiles) {
10611
- const relPath = import_node_path35.default.relative(scanPath, file);
10740
+ const relPath = import_node_path36.default.relative(scanPath, file);
10612
10741
  const node = {
10613
10742
  id: (0, import_types24.configId)(relPath),
10614
10743
  type: import_types24.NodeType.ConfigNode,
10615
- name: import_node_path35.default.basename(file),
10744
+ name: import_node_path36.default.basename(file),
10616
10745
  path: relPath,
10617
- fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
10746
+ fileType: isConfigFile(import_node_path36.default.basename(file)).fileType
10618
10747
  };
10619
10748
  if (!graph.hasNode(node.id)) {
10620
10749
  graph.addNode(node.id, node);
10621
10750
  nodesAdded++;
10622
10751
  }
10623
- const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
10752
+ const relToService = toPosix(import_node_path36.default.relative(service.dir, file));
10624
10753
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10625
10754
  graph,
10626
10755
  service.pkg.name,
@@ -10636,7 +10765,7 @@ async function addConfigNodes(graph, services, scanPath) {
10636
10765
  type: import_types24.EdgeType.CONFIGURED_BY,
10637
10766
  provenance: import_types24.Provenance.EXTRACTED,
10638
10767
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
10639
- evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
10768
+ evidence: { file: relPath.split(import_node_path36.default.sep).join("/") }
10640
10769
  };
10641
10770
  if (!graph.hasEdge(edge.id)) {
10642
10771
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -10649,8 +10778,8 @@ async function addConfigNodes(graph, services, scanPath) {
10649
10778
 
10650
10779
  // src/extract/proto.ts
10651
10780
  init_cjs_shims();
10652
- var import_node_fs24 = require("fs");
10653
- var import_node_path36 = __toESM(require("path"), 1);
10781
+ var import_node_fs25 = require("fs");
10782
+ var import_node_path37 = __toESM(require("path"), 1);
10654
10783
  var import_types25 = require("@neat.is/types");
10655
10784
  var PROTO_EXTENSION = ".proto";
10656
10785
  function packageOf(content) {
@@ -10688,18 +10817,18 @@ function grpcMethodsFromProto(content, fqPackage) {
10688
10817
  return out;
10689
10818
  }
10690
10819
  async function walkProtoFiles(dir, excludeDirs = []) {
10691
- const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10820
+ const excluded = new Set(excludeDirs.map((d) => import_node_path37.default.resolve(d)));
10692
10821
  const out = [];
10693
10822
  async function walk11(current) {
10694
- const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10823
+ const entries = await import_node_fs25.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10695
10824
  for (const entry2 of entries) {
10696
- const full = import_node_path36.default.join(current, entry2.name);
10825
+ const full = import_node_path37.default.join(current, entry2.name);
10697
10826
  if (entry2.isDirectory()) {
10698
10827
  if (IGNORED_DIRS.has(entry2.name)) continue;
10699
- if (excluded.has(import_node_path36.default.resolve(full))) continue;
10828
+ if (excluded.has(import_node_path37.default.resolve(full))) continue;
10700
10829
  if (await isPythonVenvDir(full)) continue;
10701
10830
  await walk11(full);
10702
- } else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
10831
+ } else if (entry2.isFile() && import_node_path37.default.extname(entry2.name) === PROTO_EXTENSION) {
10703
10832
  out.push(full);
10704
10833
  }
10705
10834
  }
@@ -10714,10 +10843,10 @@ async function addGrpcMethods(graph, services) {
10714
10843
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
10715
10844
  for (const protoPath of protoPaths) {
10716
10845
  if (isTestPath(protoPath)) continue;
10717
- const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
10846
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, protoPath));
10718
10847
  let content;
10719
10848
  try {
10720
- content = await import_node_fs24.promises.readFile(protoPath, "utf8");
10849
+ content = await import_node_fs25.promises.readFile(protoPath, "utf8");
10721
10850
  } catch (err) {
10722
10851
  recordExtractionError("proto extraction", protoPath, err);
10723
10852
  continue;
@@ -10776,7 +10905,7 @@ var import_types45 = require("@neat.is/types");
10776
10905
 
10777
10906
  // src/extract/calls/http.ts
10778
10907
  init_cjs_shims();
10779
- var import_node_path37 = __toESM(require("path"), 1);
10908
+ var import_node_path38 = __toESM(require("path"), 1);
10780
10909
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
10781
10910
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
10782
10911
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -10859,7 +10988,7 @@ async function addHttpCallEdges(graph, services) {
10859
10988
  const seen = /* @__PURE__ */ new Set();
10860
10989
  for (const file of files) {
10861
10990
  if (isTestPath(file.path)) continue;
10862
- const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
10991
+ const parser = parserForExt(import_node_path38.default.extname(file.path), parserCache);
10863
10992
  let sites;
10864
10993
  try {
10865
10994
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -10868,7 +10997,7 @@ async function addHttpCallEdges(graph, services) {
10868
10997
  continue;
10869
10998
  }
10870
10999
  if (sites.length === 0) continue;
10871
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
11000
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10872
11001
  for (const site of sites) {
10873
11002
  const targetId = hostToNodeId.get(site.host);
10874
11003
  if (!targetId || targetId === service.node.id) continue;
@@ -10922,7 +11051,7 @@ async function addHttpCallEdges(graph, services) {
10922
11051
 
10923
11052
  // src/extract/calls/route-match.ts
10924
11053
  init_cjs_shims();
10925
- var import_node_path38 = __toESM(require("path"), 1);
11054
+ var import_node_path39 = __toESM(require("path"), 1);
10926
11055
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
10927
11056
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
10928
11057
  var import_types27 = require("@neat.is/types");
@@ -11200,7 +11329,7 @@ async function addRouteCallEdges(graph, services) {
11200
11329
  const seen = /* @__PURE__ */ new Set();
11201
11330
  for (const file of files) {
11202
11331
  if (isTestPath(file.path)) continue;
11203
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
11332
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path39.default.extname(file.path))) continue;
11204
11333
  let sites;
11205
11334
  try {
11206
11335
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -11209,7 +11338,7 @@ async function addRouteCallEdges(graph, services) {
11209
11338
  continue;
11210
11339
  }
11211
11340
  if (sites.length === 0) continue;
11212
- const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
11341
+ const relFile = toPosix(import_node_path39.default.relative(service.dir, file.path));
11213
11342
  for (const site of sites) {
11214
11343
  if (site.host === null) {
11215
11344
  const dedupKey2 = `${relFile}|unresolved-host`;
@@ -11302,7 +11431,7 @@ async function addRouteCallEdges(graph, services) {
11302
11431
 
11303
11432
  // src/extract/calls/kafka.ts
11304
11433
  init_cjs_shims();
11305
- var import_node_path39 = __toESM(require("path"), 1);
11434
+ var import_node_path40 = __toESM(require("path"), 1);
11306
11435
  var import_types28 = require("@neat.is/types");
11307
11436
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
11308
11437
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
@@ -11390,13 +11519,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
11390
11519
  // call sites — verified-call-site tier (ADR-066).
11391
11520
  confidenceKind: "verified-call-site",
11392
11521
  evidence: {
11393
- file: import_node_path39.default.relative(serviceDir, file.path),
11522
+ file: import_node_path40.default.relative(serviceDir, file.path),
11394
11523
  line,
11395
11524
  snippet: snippet(file.content, line)
11396
11525
  }
11397
11526
  });
11398
11527
  };
11399
- if (import_node_path39.default.extname(file.path) === ".go") {
11528
+ if (import_node_path40.default.extname(file.path) === ".go") {
11400
11529
  goSaramaEndpoints(file.content, make);
11401
11530
  } else {
11402
11531
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -11407,7 +11536,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
11407
11536
 
11408
11537
  // src/extract/calls/redis.ts
11409
11538
  init_cjs_shims();
11410
- var import_node_path40 = __toESM(require("path"), 1);
11539
+ var import_node_path41 = __toESM(require("path"), 1);
11411
11540
  var import_types29 = require("@neat.is/types");
11412
11541
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
11413
11542
  function redisEndpointsFromFile(file, serviceDir) {
@@ -11430,7 +11559,7 @@ function redisEndpointsFromFile(file, serviceDir) {
11430
11559
  // support tier (ADR-066).
11431
11560
  confidenceKind: "url-with-structural-support",
11432
11561
  evidence: {
11433
- file: import_node_path40.default.relative(serviceDir, file.path),
11562
+ file: import_node_path41.default.relative(serviceDir, file.path),
11434
11563
  line,
11435
11564
  snippet: snippet(file.content, line)
11436
11565
  }
@@ -11441,7 +11570,7 @@ function redisEndpointsFromFile(file, serviceDir) {
11441
11570
 
11442
11571
  // src/extract/calls/aws.ts
11443
11572
  init_cjs_shims();
11444
- var import_node_path41 = __toESM(require("path"), 1);
11573
+ var import_node_path42 = __toESM(require("path"), 1);
11445
11574
  var import_types30 = require("@neat.is/types");
11446
11575
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
11447
11576
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -11475,7 +11604,7 @@ function awsEndpointsFromFile(file, serviceDir) {
11475
11604
  // (ADR-066).
11476
11605
  confidenceKind: "verified-call-site",
11477
11606
  evidence: {
11478
- file: import_node_path41.default.relative(serviceDir, file.path),
11607
+ file: import_node_path42.default.relative(serviceDir, file.path),
11479
11608
  line,
11480
11609
  snippet: snippet(file.content, line)
11481
11610
  }
@@ -11500,7 +11629,7 @@ function awsEndpointsFromFile(file, serviceDir) {
11500
11629
 
11501
11630
  // src/extract/calls/grpc.ts
11502
11631
  init_cjs_shims();
11503
- var import_node_path42 = __toESM(require("path"), 1);
11632
+ var import_node_path43 = __toESM(require("path"), 1);
11504
11633
  var import_types31 = require("@neat.is/types");
11505
11634
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
11506
11635
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -11559,7 +11688,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11559
11688
  // tier (ADR-066).
11560
11689
  confidenceKind: "verified-call-site",
11561
11690
  evidence: {
11562
- file: import_node_path42.default.relative(serviceDir, file.path),
11691
+ file: import_node_path43.default.relative(serviceDir, file.path),
11563
11692
  line,
11564
11693
  snippet: snippet(file.content, line)
11565
11694
  }
@@ -11570,7 +11699,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11570
11699
 
11571
11700
  // src/extract/calls/supabase.ts
11572
11701
  init_cjs_shims();
11573
- var import_node_path43 = __toESM(require("path"), 1);
11702
+ var import_node_path44 = __toESM(require("path"), 1);
11574
11703
  var import_types32 = require("@neat.is/types");
11575
11704
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
11576
11705
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -11629,7 +11758,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11629
11758
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
11630
11759
  confidenceKind: "verified-call-site",
11631
11760
  evidence: {
11632
- file: import_node_path43.default.relative(serviceDir, file.path),
11761
+ file: import_node_path44.default.relative(serviceDir, file.path),
11633
11762
  line,
11634
11763
  snippet: snippet(file.content, line)
11635
11764
  }
@@ -11656,7 +11785,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11656
11785
  edgeType: "CALLS",
11657
11786
  confidenceKind: "verified-call-site",
11658
11787
  evidence: {
11659
- file: import_node_path43.default.relative(serviceDir, file.path),
11788
+ file: import_node_path44.default.relative(serviceDir, file.path),
11660
11789
  line,
11661
11790
  snippet: snippet(file.content, line)
11662
11791
  }
@@ -11668,7 +11797,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11668
11797
 
11669
11798
  // src/extract/calls/firestore.ts
11670
11799
  init_cjs_shims();
11671
- var import_node_path44 = __toESM(require("path"), 1);
11800
+ var import_node_path45 = __toESM(require("path"), 1);
11672
11801
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
11673
11802
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
11674
11803
  var import_types33 = require("@neat.is/types");
@@ -11841,7 +11970,7 @@ function firestoreEndpointsFromFile(file, serviceDir, imported) {
11841
11970
  if (!hasClient && !hasAdmin && importedNames.size === 0) return [];
11842
11971
  const ownSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11843
11972
  const fileSdk = !hasClient && !hasAdmin ? imported?.sdk ?? null : ownSdk;
11844
- const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
11973
+ const tree = parseSource3(parserForExt2(import_node_path45.default.extname(file.path)), file.content);
11845
11974
  const clientVars = firestoreClientVars(tree.rootNode);
11846
11975
  for (const n of importedNames) clientVars.add(n);
11847
11976
  const collLine = /* @__PURE__ */ new Map();
@@ -11946,7 +12075,7 @@ function firestoreEndpointsFromFile(file, serviceDir, imported) {
11946
12075
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
11947
12076
  ...sdkWrites ? { sdkWrites } : {},
11948
12077
  evidence: {
11949
- file: import_node_path44.default.relative(serviceDir, file.path),
12078
+ file: import_node_path45.default.relative(serviceDir, file.path),
11950
12079
  line,
11951
12080
  snippet: snippet(file.content, line)
11952
12081
  }
@@ -12006,7 +12135,7 @@ function sdkOf(content) {
12006
12135
  return hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
12007
12136
  }
12008
12137
  function parseOrNull(file) {
12009
- const ext = import_node_path44.default.extname(file.path);
12138
+ const ext = import_node_path45.default.extname(file.path);
12010
12139
  if (!(ext in GRAMMAR_BY_EXT)) return null;
12011
12140
  try {
12012
12141
  return parseSource3(parserForExt2(ext), file.content).rootNode;
@@ -12022,7 +12151,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
12022
12151
  if (!root) continue;
12023
12152
  const names = exportedFirestoreClients(root);
12024
12153
  if (names.size === 0) continue;
12025
- registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), { names, sdk: sdkOf(f.content) });
12154
+ registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), { names, sdk: sdkOf(f.content) });
12026
12155
  }
12027
12156
  if (registry.size === 0) return [];
12028
12157
  const out = [];
@@ -12035,7 +12164,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
12035
12164
  const names = /* @__PURE__ */ new Set();
12036
12165
  let sdk = null;
12037
12166
  for (const b of bindings) {
12038
- const rel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
12167
+ const rel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
12039
12168
  if (!rel) continue;
12040
12169
  const entry2 = registry.get(rel);
12041
12170
  if (!entry2 || !entry2.names.has(b.imported)) continue;
@@ -12050,7 +12179,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
12050
12179
 
12051
12180
  // src/extract/calls/mongoose.ts
12052
12181
  init_cjs_shims();
12053
- var import_node_path45 = __toESM(require("path"), 1);
12182
+ var import_node_path46 = __toESM(require("path"), 1);
12054
12183
  var import_types34 = require("@neat.is/types");
12055
12184
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
12056
12185
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -12201,7 +12330,7 @@ function endpoint(r, file, serviceDir, matchText) {
12201
12330
  kind: r.kind,
12202
12331
  edgeType: "CALLS",
12203
12332
  confidenceKind: "verified-call-site",
12204
- evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12333
+ evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12205
12334
  };
12206
12335
  }
12207
12336
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -12291,7 +12420,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
12291
12420
  const registry = /* @__PURE__ */ new Map();
12292
12421
  for (const f of mongooseFiles) {
12293
12422
  const fx = fileExportsOf(f.content, pluralizeOn);
12294
- if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
12423
+ if (fx) registry.set(toPosix(import_node_path46.default.relative(serviceDir, f.path)), fx);
12295
12424
  }
12296
12425
  if (registry.size === 0) return [];
12297
12426
  const out = [];
@@ -12302,7 +12431,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
12302
12431
  const directColl = /* @__PURE__ */ new Map();
12303
12432
  const nsExports = /* @__PURE__ */ new Map();
12304
12433
  for (const b of bindings) {
12305
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
12434
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path46.default.dirname(f.path), serviceDir, null);
12306
12435
  if (!resolvedRel) continue;
12307
12436
  const fx = registry.get(resolvedRel);
12308
12437
  if (!fx) continue;
@@ -12346,7 +12475,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
12346
12475
 
12347
12476
  // src/extract/calls/sqlalchemy.ts
12348
12477
  init_cjs_shims();
12349
- var import_node_path46 = __toESM(require("path"), 1);
12478
+ var import_node_path47 = __toESM(require("path"), 1);
12350
12479
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
12351
12480
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
12352
12481
  var import_types35 = require("@neat.is/types");
@@ -12485,7 +12614,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
12485
12614
  childTable,
12486
12615
  parentTable,
12487
12616
  evidence: {
12488
- file: import_node_path46.default.relative(serviceDir, file.path),
12617
+ file: import_node_path47.default.relative(serviceDir, file.path),
12489
12618
  line,
12490
12619
  snippet: snippet(file.content, line)
12491
12620
  }
@@ -12510,7 +12639,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
12510
12639
  confidenceKind: "verified-call-site",
12511
12640
  ...columns && columns.length > 0 ? { columns } : {},
12512
12641
  evidence: {
12513
- file: import_node_path46.default.relative(serviceDir, file.path),
12642
+ file: import_node_path47.default.relative(serviceDir, file.path),
12514
12643
  line,
12515
12644
  snippet: snippet(file.content, line)
12516
12645
  }
@@ -12617,7 +12746,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12617
12746
  edgeType: "CALLS",
12618
12747
  confidenceKind: "verified-call-site",
12619
12748
  evidence: {
12620
- file: import_node_path46.default.relative(serviceDir, file.path),
12749
+ file: import_node_path47.default.relative(serviceDir, file.path),
12621
12750
  line,
12622
12751
  snippet: snippet(file.content, line)
12623
12752
  }
@@ -12629,7 +12758,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12629
12758
 
12630
12759
  // src/extract/calls/socket.ts
12631
12760
  init_cjs_shims();
12632
- var import_node_path47 = __toESM(require("path"), 1);
12761
+ var import_node_path48 = __toESM(require("path"), 1);
12633
12762
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
12634
12763
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
12635
12764
  var import_types36 = require("@neat.is/types");
@@ -12705,7 +12834,7 @@ function socketEndpointsFromFile(file, serviceDir) {
12705
12834
  // A verified `socket.socket(...)` + `.connect((host, port))` call site.
12706
12835
  confidenceKind: "verified-call-site",
12707
12836
  evidence: {
12708
- file: import_node_path47.default.relative(serviceDir, file.path),
12837
+ file: import_node_path48.default.relative(serviceDir, file.path),
12709
12838
  line,
12710
12839
  snippet: snippet(file.content, line)
12711
12840
  }
@@ -12716,7 +12845,7 @@ function socketEndpointsFromFile(file, serviceDir) {
12716
12845
 
12717
12846
  // src/extract/calls/django-orm.ts
12718
12847
  init_cjs_shims();
12719
- var import_node_path48 = __toESM(require("path"), 1);
12848
+ var import_node_path49 = __toESM(require("path"), 1);
12720
12849
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12721
12850
  var import_tree_sitter_python7 = __toESM(require("tree-sitter-python"), 1);
12722
12851
  var import_types37 = require("@neat.is/types");
@@ -12786,7 +12915,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12786
12915
  const tree = parseSource8(makePyParser5(), file.content);
12787
12916
  const out = [];
12788
12917
  const seen = /* @__PURE__ */ new Set();
12789
- const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
12918
+ const defaultAppLabel = import_node_path49.default.basename(import_node_path49.default.dirname(file.path));
12790
12919
  walk5(tree.rootNode, (node) => {
12791
12920
  if (node.type !== "class_definition") return;
12792
12921
  if (!extendsDjangoModel(node)) return;
@@ -12804,7 +12933,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12804
12933
  kind: "sql-table",
12805
12934
  edgeType: "CALLS",
12806
12935
  confidenceKind: "verified-call-site",
12807
- evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12936
+ evidence: { file: import_node_path49.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12808
12937
  });
12809
12938
  });
12810
12939
  return out;
@@ -12812,7 +12941,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12812
12941
 
12813
12942
  // src/extract/calls/drizzle.ts
12814
12943
  init_cjs_shims();
12815
- var import_node_path49 = __toESM(require("path"), 1);
12944
+ var import_node_path50 = __toESM(require("path"), 1);
12816
12945
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12817
12946
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12818
12947
  var import_types38 = require("@neat.is/types");
@@ -12890,7 +13019,7 @@ function columnsFromObject(obj) {
12890
13019
  }
12891
13020
  function drizzleEndpointsFromFile(file, serviceDir) {
12892
13021
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12893
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
13022
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12894
13023
  const out = [];
12895
13024
  const seen = /* @__PURE__ */ new Set();
12896
13025
  const walk11 = (node) => {
@@ -12913,7 +13042,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12913
13042
  confidenceKind: "structural",
12914
13043
  columns,
12915
13044
  evidence: {
12916
- file: import_node_path49.default.relative(serviceDir, file.path),
13045
+ file: import_node_path50.default.relative(serviceDir, file.path),
12917
13046
  line,
12918
13047
  snippet: snippet(file.content, line)
12919
13048
  }
@@ -12979,7 +13108,7 @@ function referencesTargetVar(call) {
12979
13108
  }
12980
13109
  function drizzleForeignKeys(file, serviceDir) {
12981
13110
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12982
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
13111
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12983
13112
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
12984
13113
  const out = [];
12985
13114
  const seen = /* @__PURE__ */ new Set();
@@ -12998,7 +13127,7 @@ function drizzleForeignKeys(file, serviceDir) {
12998
13127
  childTable: table.tableName,
12999
13128
  parentTable,
13000
13129
  evidence: {
13001
- file: import_node_path49.default.relative(serviceDir, file.path),
13130
+ file: import_node_path50.default.relative(serviceDir, file.path),
13002
13131
  line,
13003
13132
  snippet: snippet(file.content, line)
13004
13133
  }
@@ -13015,7 +13144,7 @@ function drizzleForeignKeys(file, serviceDir) {
13015
13144
 
13016
13145
  // src/extract/calls/prisma.ts
13017
13146
  init_cjs_shims();
13018
- var import_node_path50 = __toESM(require("path"), 1);
13147
+ var import_node_path51 = __toESM(require("path"), 1);
13019
13148
  var import_types39 = require("@neat.is/types");
13020
13149
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
13021
13150
  "Int",
@@ -13071,7 +13200,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
13071
13200
  confidenceKind: "structural",
13072
13201
  columns: b.columns,
13073
13202
  evidence: {
13074
- file: import_node_path50.default.relative(serviceDir, file.path),
13203
+ file: import_node_path51.default.relative(serviceDir, file.path),
13075
13204
  line: b.startLine,
13076
13205
  snippet: snippet(content, b.startLine)
13077
13206
  }
@@ -13132,7 +13261,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
13132
13261
  }
13133
13262
  async function prismaColumnEndpoints(serviceDir) {
13134
13263
  const schemaPath = await findFirst(serviceDir, [
13135
- import_node_path50.default.join("prisma", "schema.prisma"),
13264
+ import_node_path51.default.join("prisma", "schema.prisma"),
13136
13265
  "schema.prisma"
13137
13266
  ]);
13138
13267
  if (!schemaPath) return [];
@@ -13207,7 +13336,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
13207
13336
  childTable: current.table,
13208
13337
  parentTable,
13209
13338
  evidence: {
13210
- file: import_node_path50.default.relative(serviceDir, file.path),
13339
+ file: import_node_path51.default.relative(serviceDir, file.path),
13211
13340
  line: lineNo,
13212
13341
  snippet: snippet(content, lineNo)
13213
13342
  }
@@ -13222,7 +13351,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
13222
13351
  }
13223
13352
  async function prismaForeignKeys(serviceDir) {
13224
13353
  const schemaPath = await findFirst(serviceDir, [
13225
- import_node_path50.default.join("prisma", "schema.prisma"),
13354
+ import_node_path51.default.join("prisma", "schema.prisma"),
13226
13355
  "schema.prisma"
13227
13356
  ]);
13228
13357
  if (!schemaPath) return [];
@@ -13233,7 +13362,7 @@ async function prismaForeignKeys(serviceDir) {
13233
13362
 
13234
13363
  // src/extract/calls/activerecord.ts
13235
13364
  init_cjs_shims();
13236
- var import_node_path51 = __toESM(require("path"), 1);
13365
+ var import_node_path52 = __toESM(require("path"), 1);
13237
13366
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
13238
13367
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
13239
13368
  var import_types40 = require("@neat.is/types");
@@ -13441,7 +13570,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
13441
13570
  confidenceKind: "structural",
13442
13571
  ...table.columns.length > 0 ? { columns: table.columns } : {},
13443
13572
  evidence: {
13444
- file: import_node_path51.default.relative(serviceDir, file.path),
13573
+ file: import_node_path52.default.relative(serviceDir, file.path),
13445
13574
  line: table.line,
13446
13575
  snippet: snippet(file.content, table.line)
13447
13576
  }
@@ -13463,7 +13592,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
13463
13592
  childTable,
13464
13593
  parentTable,
13465
13594
  evidence: {
13466
- file: import_node_path51.default.relative(serviceDir, file.path),
13595
+ file: import_node_path52.default.relative(serviceDir, file.path),
13467
13596
  line,
13468
13597
  snippet: snippet(file.content, line)
13469
13598
  }
@@ -13546,7 +13675,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
13546
13675
  edgeType: "CALLS",
13547
13676
  confidenceKind: "verified-call-site",
13548
13677
  evidence: {
13549
- file: import_node_path51.default.relative(serviceDir, file.path),
13678
+ file: import_node_path52.default.relative(serviceDir, file.path),
13550
13679
  line,
13551
13680
  snippet: snippet(file.content, line)
13552
13681
  }
@@ -13583,7 +13712,7 @@ function railsModelForeignKeys(file, serviceDir) {
13583
13712
  childTable,
13584
13713
  parentTable,
13585
13714
  evidence: {
13586
- file: import_node_path51.default.relative(serviceDir, file.path),
13715
+ file: import_node_path52.default.relative(serviceDir, file.path),
13587
13716
  line,
13588
13717
  snippet: snippet(file.content, line)
13589
13718
  }
@@ -13595,7 +13724,7 @@ function railsModelForeignKeys(file, serviceDir) {
13595
13724
 
13596
13725
  // src/extract/calls/eloquent.ts
13597
13726
  init_cjs_shims();
13598
- var import_node_path52 = __toESM(require("path"), 1);
13727
+ var import_node_path53 = __toESM(require("path"), 1);
13599
13728
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13600
13729
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
13601
13730
  var import_types41 = require("@neat.is/types");
@@ -13852,7 +13981,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13852
13981
  confidenceKind: "structural",
13853
13982
  ...columns.length > 0 ? { columns } : {},
13854
13983
  evidence: {
13855
- file: import_node_path52.default.relative(serviceDir, file.path),
13984
+ file: import_node_path53.default.relative(serviceDir, file.path),
13856
13985
  line: bp.line,
13857
13986
  snippet: snippet(file.content, bp.line)
13858
13987
  }
@@ -13874,7 +14003,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13874
14003
  childTable,
13875
14004
  parentTable,
13876
14005
  evidence: {
13877
- file: import_node_path52.default.relative(serviceDir, file.path),
14006
+ file: import_node_path53.default.relative(serviceDir, file.path),
13878
14007
  line,
13879
14008
  snippet: snippet(file.content, line)
13880
14009
  }
@@ -13994,7 +14123,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
13994
14123
  edgeType: "CALLS",
13995
14124
  confidenceKind: "verified-call-site",
13996
14125
  evidence: {
13997
- file: import_node_path52.default.relative(serviceDir, file.path),
14126
+ file: import_node_path53.default.relative(serviceDir, file.path),
13998
14127
  line,
13999
14128
  snippet: snippet(file.content, line)
14000
14129
  }
@@ -14030,7 +14159,7 @@ function laravelModelForeignKeys(file, serviceDir) {
14030
14159
  childTable,
14031
14160
  parentTable,
14032
14161
  evidence: {
14033
- file: import_node_path52.default.relative(serviceDir, file.path),
14162
+ file: import_node_path53.default.relative(serviceDir, file.path),
14034
14163
  line,
14035
14164
  snippet: snippet(file.content, line)
14036
14165
  }
@@ -14042,7 +14171,7 @@ function laravelModelForeignKeys(file, serviceDir) {
14042
14171
 
14043
14172
  // src/extract/calls/go.ts
14044
14173
  init_cjs_shims();
14045
- var import_node_path55 = __toESM(require("path"), 1);
14174
+ var import_node_path56 = __toESM(require("path"), 1);
14046
14175
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
14047
14176
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
14048
14177
  var import_types42 = require("@neat.is/types");
@@ -14117,7 +14246,7 @@ function firstStringLiteralArg(argsNode) {
14117
14246
  return null;
14118
14247
  }
14119
14248
  function goSqlEndpointsFromFile(file, serviceDir) {
14120
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14249
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
14121
14250
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
14122
14251
  const tree = parseSource11(makeGoParser3(), file.content);
14123
14252
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -14146,7 +14275,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
14146
14275
  confidenceKind: "verified-call-site",
14147
14276
  ...columns.length > 0 ? { columns } : {},
14148
14277
  evidence: {
14149
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14278
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14150
14279
  line,
14151
14280
  snippet: snippet(file.content, line)
14152
14281
  }
@@ -14157,7 +14286,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
14157
14286
 
14158
14287
  // src/extract/calls/gorm.ts
14159
14288
  init_cjs_shims();
14160
- var import_node_path56 = __toESM(require("path"), 1);
14289
+ var import_node_path57 = __toESM(require("path"), 1);
14161
14290
  var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14162
14291
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
14163
14292
  var import_types43 = require("@neat.is/types");
@@ -14590,7 +14719,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
14590
14719
  seen.delete(struct.name);
14591
14720
  }
14592
14721
  function gormEndpointsFromFile(file, serviceDir) {
14593
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14722
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
14594
14723
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14595
14724
  const tree = parseSource12(makeGoParser4(), file.content);
14596
14725
  const { structs, models, tableFor } = analyze(tree);
@@ -14612,7 +14741,7 @@ function gormEndpointsFromFile(file, serviceDir) {
14612
14741
  confidenceKind: "structural",
14613
14742
  ...columns.length > 0 ? { columns } : {},
14614
14743
  evidence: {
14615
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14744
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14616
14745
  line: struct.line,
14617
14746
  snippet: snippet(file.content, struct.line)
14618
14747
  }
@@ -14621,7 +14750,7 @@ function gormEndpointsFromFile(file, serviceDir) {
14621
14750
  return out;
14622
14751
  }
14623
14752
  function gormForeignKeys(file, serviceDir) {
14624
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14753
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
14625
14754
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14626
14755
  const tree = parseSource12(makeGoParser4(), file.content);
14627
14756
  const { structs, models, tableFor } = analyze(tree);
@@ -14636,7 +14765,7 @@ function gormForeignKeys(file, serviceDir) {
14636
14765
  childTable,
14637
14766
  parentTable,
14638
14767
  evidence: {
14639
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14768
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14640
14769
  line,
14641
14770
  snippet: snippet(file.content, line)
14642
14771
  }
@@ -14673,7 +14802,7 @@ function gormForeignKeys(file, serviceDir) {
14673
14802
 
14674
14803
  // src/extract/calls/efcore.ts
14675
14804
  init_cjs_shims();
14676
- var import_node_path57 = __toESM(require("path"), 1);
14805
+ var import_node_path58 = __toESM(require("path"), 1);
14677
14806
  var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14678
14807
  var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14679
14808
  var import_types44 = require("@neat.is/types");
@@ -14752,7 +14881,7 @@ function tableFromToTable(call) {
14752
14881
  return value ? csStringLiteral(value) : null;
14753
14882
  }
14754
14883
  function efcoreEndpointsFromFile(file, serviceDir) {
14755
- if (import_node_path57.default.extname(file.path) !== ".cs") return [];
14884
+ if (import_node_path58.default.extname(file.path) !== ".cs") return [];
14756
14885
  if (!EFCORE_GATE.test(file.content)) return [];
14757
14886
  const tree = parseSource13(makeCsParser(), file.content);
14758
14887
  const out = [];
@@ -14767,7 +14896,7 @@ function efcoreEndpointsFromFile(file, serviceDir) {
14767
14896
  edgeType: "CALLS",
14768
14897
  confidenceKind: "structural",
14769
14898
  evidence: {
14770
- file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14899
+ file: toPosix(import_node_path58.default.relative(serviceDir, file.path)),
14771
14900
  line,
14772
14901
  snippet: snippet(file.content, line)
14773
14902
  }
@@ -15022,7 +15151,7 @@ init_cjs_shims();
15022
15151
 
15023
15152
  // src/extract/infra/docker-compose.ts
15024
15153
  init_cjs_shims();
15025
- var import_node_path58 = __toESM(require("path"), 1);
15154
+ var import_node_path59 = __toESM(require("path"), 1);
15026
15155
  var import_types48 = require("@neat.is/types");
15027
15156
 
15028
15157
  // src/extract/infra/shared.ts
@@ -15093,7 +15222,7 @@ function dependsOnList(value) {
15093
15222
  }
15094
15223
  function serviceNameToServiceNode(name, services) {
15095
15224
  for (const s of services) {
15096
- if (s.node.name === name || import_node_path58.default.basename(s.dir) === name) return s.node.id;
15225
+ if (s.node.name === name || import_node_path59.default.basename(s.dir) === name) return s.node.id;
15097
15226
  }
15098
15227
  return null;
15099
15228
  }
@@ -15102,7 +15231,7 @@ async function addComposeInfra(graph, scanPath, services) {
15102
15231
  let edgesAdded = 0;
15103
15232
  let composePath = null;
15104
15233
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
15105
- const abs = import_node_path58.default.join(scanPath, name);
15234
+ const abs = import_node_path59.default.join(scanPath, name);
15106
15235
  if (await exists(abs)) {
15107
15236
  composePath = abs;
15108
15237
  break;
@@ -15115,13 +15244,13 @@ async function addComposeInfra(graph, scanPath, services) {
15115
15244
  } catch (err) {
15116
15245
  recordExtractionError(
15117
15246
  "infra docker-compose",
15118
- import_node_path58.default.relative(scanPath, composePath),
15247
+ import_node_path59.default.relative(scanPath, composePath),
15119
15248
  err
15120
15249
  );
15121
15250
  return { nodesAdded, edgesAdded };
15122
15251
  }
15123
15252
  if (!compose?.services) return { nodesAdded, edgesAdded };
15124
- const evidenceFile = import_node_path58.default.relative(scanPath, composePath).split(import_node_path58.default.sep).join("/");
15253
+ const evidenceFile = import_node_path59.default.relative(scanPath, composePath).split(import_node_path59.default.sep).join("/");
15125
15254
  const composeNameToNodeId = /* @__PURE__ */ new Map();
15126
15255
  for (const [composeName, svc] of Object.entries(compose.services)) {
15127
15256
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -15163,8 +15292,8 @@ async function addComposeInfra(graph, scanPath, services) {
15163
15292
 
15164
15293
  // src/extract/infra/dockerfile.ts
15165
15294
  init_cjs_shims();
15166
- var import_node_path59 = __toESM(require("path"), 1);
15167
- var import_node_fs25 = require("fs");
15295
+ var import_node_path60 = __toESM(require("path"), 1);
15296
+ var import_node_fs26 = require("fs");
15168
15297
  var import_types49 = require("@neat.is/types");
15169
15298
  function readDockerfile(content) {
15170
15299
  let image = null;
@@ -15194,15 +15323,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15194
15323
  let nodesAdded = 0;
15195
15324
  let edgesAdded = 0;
15196
15325
  for (const service of services) {
15197
- const dockerfilePath = import_node_path59.default.join(service.dir, "Dockerfile");
15326
+ const dockerfilePath = import_node_path60.default.join(service.dir, "Dockerfile");
15198
15327
  if (!await exists(dockerfilePath)) continue;
15199
15328
  let content;
15200
15329
  try {
15201
- content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
15330
+ content = await import_node_fs26.promises.readFile(dockerfilePath, "utf8");
15202
15331
  } catch (err) {
15203
15332
  recordExtractionError(
15204
15333
  "infra dockerfile",
15205
- import_node_path59.default.relative(scanPath, dockerfilePath),
15334
+ import_node_path60.default.relative(scanPath, dockerfilePath),
15206
15335
  err
15207
15336
  );
15208
15337
  continue;
@@ -15214,8 +15343,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15214
15343
  graph.addNode(node.id, node);
15215
15344
  nodesAdded++;
15216
15345
  }
15217
- const relDockerfile = toPosix(import_node_path59.default.relative(service.dir, dockerfilePath));
15218
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, dockerfilePath));
15346
+ const relDockerfile = toPosix(import_node_path60.default.relative(service.dir, dockerfilePath));
15347
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, dockerfilePath));
15219
15348
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
15220
15349
  graph,
15221
15350
  service.pkg.name,
@@ -15267,23 +15396,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15267
15396
 
15268
15397
  // src/extract/infra/terraform.ts
15269
15398
  init_cjs_shims();
15270
- var import_node_fs26 = require("fs");
15271
- var import_node_path60 = __toESM(require("path"), 1);
15399
+ var import_node_fs27 = require("fs");
15400
+ var import_node_path61 = __toESM(require("path"), 1);
15272
15401
  var import_types50 = require("@neat.is/types");
15273
15402
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
15274
15403
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
15275
15404
  async function walkTfFiles(start, depth = 0, max = 5) {
15276
15405
  if (depth > max) return [];
15277
15406
  const out = [];
15278
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15407
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15279
15408
  for (const entry2 of entries) {
15280
15409
  if (entry2.isDirectory()) {
15281
15410
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
15282
- const child = import_node_path60.default.join(start, entry2.name);
15411
+ const child = import_node_path61.default.join(start, entry2.name);
15283
15412
  if (await isPythonVenvDir(child)) continue;
15284
15413
  out.push(...await walkTfFiles(child, depth + 1, max));
15285
15414
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
15286
- out.push(import_node_path60.default.join(start, entry2.name));
15415
+ out.push(import_node_path61.default.join(start, entry2.name));
15287
15416
  }
15288
15417
  }
15289
15418
  return out;
@@ -15314,8 +15443,8 @@ async function addTerraformResources(graph, scanPath) {
15314
15443
  let edgesAdded = 0;
15315
15444
  const files = await walkTfFiles(scanPath);
15316
15445
  for (const file of files) {
15317
- const content = await import_node_fs26.promises.readFile(file, "utf8");
15318
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, file));
15446
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
15447
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, file));
15319
15448
  const resources = [];
15320
15449
  const byKey = /* @__PURE__ */ new Map();
15321
15450
  RESOURCE_RE.lastIndex = 0;
@@ -15372,8 +15501,8 @@ async function addTerraformResources(graph, scanPath) {
15372
15501
 
15373
15502
  // src/extract/infra/k8s.ts
15374
15503
  init_cjs_shims();
15375
- var import_node_fs27 = require("fs");
15376
- var import_node_path61 = __toESM(require("path"), 1);
15504
+ var import_node_fs28 = require("fs");
15505
+ var import_node_path62 = __toESM(require("path"), 1);
15377
15506
  var import_yaml3 = require("yaml");
15378
15507
  var import_types51 = require("@neat.is/types");
15379
15508
  var K8S_KIND_TO_INFRA_KIND = {
@@ -15389,15 +15518,15 @@ var WORKLOAD_KINDS = /* @__PURE__ */ new Set(["Deployment", "StatefulSet", "Daem
15389
15518
  async function walkYamlFiles2(start, depth = 0, max = 5) {
15390
15519
  if (depth > max) return [];
15391
15520
  const out = [];
15392
- const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15521
+ const entries = await import_node_fs28.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15393
15522
  for (const entry2 of entries) {
15394
15523
  if (entry2.isDirectory()) {
15395
15524
  if (IGNORED_DIRS.has(entry2.name)) continue;
15396
- const child = import_node_path61.default.join(start, entry2.name);
15525
+ const child = import_node_path62.default.join(start, entry2.name);
15397
15526
  if (await isPythonVenvDir(child)) continue;
15398
15527
  out.push(...await walkYamlFiles2(child, depth + 1, max));
15399
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path61.default.extname(entry2.name))) {
15400
- out.push(import_node_path61.default.join(start, entry2.name));
15528
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path62.default.extname(entry2.name))) {
15529
+ out.push(import_node_path62.default.join(start, entry2.name));
15401
15530
  }
15402
15531
  }
15403
15532
  return out;
@@ -15407,14 +15536,14 @@ async function addK8sResources(graph, scanPath) {
15407
15536
  let edgesAdded = 0;
15408
15537
  const files = await walkYamlFiles2(scanPath);
15409
15538
  for (const file of files) {
15410
- const content = await import_node_fs27.promises.readFile(file, "utf8");
15539
+ const content = await import_node_fs28.promises.readFile(file, "utf8");
15411
15540
  let docs;
15412
15541
  try {
15413
15542
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
15414
15543
  } catch {
15415
15544
  continue;
15416
15545
  }
15417
- const relFile = toPosix(import_node_path61.default.relative(scanPath, file));
15546
+ const relFile = toPosix(import_node_path62.default.relative(scanPath, file));
15418
15547
  for (const doc of docs) {
15419
15548
  if (!doc?.kind || !doc.metadata?.name) continue;
15420
15549
  const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind];
@@ -15463,16 +15592,16 @@ async function addK8sResources(graph, scanPath) {
15463
15592
 
15464
15593
  // src/extract/infra/cloudflare.ts
15465
15594
  init_cjs_shims();
15466
- var import_node_fs28 = require("fs");
15467
- var import_node_path62 = __toESM(require("path"), 1);
15595
+ var import_node_fs29 = require("fs");
15596
+ var import_node_path63 = __toESM(require("path"), 1);
15468
15597
  var import_smol_toml3 = require("smol-toml");
15469
15598
  var import_types52 = require("@neat.is/types");
15470
15599
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
15471
15600
  async function readWranglerConfig(dir) {
15472
15601
  for (const filename of WRANGLER_FILENAMES) {
15473
- const abs = import_node_path62.default.join(dir, filename);
15602
+ const abs = import_node_path63.default.join(dir, filename);
15474
15603
  if (!await exists(abs)) continue;
15475
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
15604
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15476
15605
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
15477
15606
  return { config, relFile: filename, raw };
15478
15607
  }
@@ -15533,11 +15662,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15533
15662
  try {
15534
15663
  read = await readWranglerConfig(service.dir);
15535
15664
  } catch (err) {
15536
- recordExtractionError("infra cloudflare", import_node_path62.default.relative(scanPath, service.dir), err);
15665
+ recordExtractionError("infra cloudflare", import_node_path63.default.relative(scanPath, service.dir), err);
15537
15666
  continue;
15538
15667
  }
15539
15668
  if (!read || !read.config.name) continue;
15540
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, read.relFile)));
15669
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, read.relFile)));
15541
15670
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
15542
15671
  }
15543
15672
  for (const worker of discovered) {
@@ -15549,7 +15678,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15549
15678
  }
15550
15679
  let anchorId = service.node.id;
15551
15680
  if (config.main) {
15552
- const entryRelPath = toPosix(import_node_path62.default.normalize(config.main));
15681
+ const entryRelPath = toPosix(import_node_path63.default.normalize(config.main));
15553
15682
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
15554
15683
  graph,
15555
15684
  service.pkg.name,
@@ -15696,24 +15825,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15696
15825
 
15697
15826
  // src/extract/infra/vercel.ts
15698
15827
  init_cjs_shims();
15699
- var import_node_fs29 = require("fs");
15700
- var import_node_path63 = __toESM(require("path"), 1);
15828
+ var import_node_fs30 = require("fs");
15829
+ var import_node_path64 = __toESM(require("path"), 1);
15701
15830
  var import_types53 = require("@neat.is/types");
15702
15831
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
15703
15832
  async function readVercelConfig(dir) {
15704
15833
  for (const filename of VERCEL_CONFIG_FILENAMES) {
15705
- const abs = import_node_path63.default.join(dir, filename);
15834
+ const abs = import_node_path64.default.join(dir, filename);
15706
15835
  if (!await exists(abs)) continue;
15707
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15836
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15708
15837
  const config = JSON.parse(maskCommentsInSource(raw));
15709
15838
  return { config, relFile: filename, raw };
15710
15839
  }
15711
15840
  return null;
15712
15841
  }
15713
15842
  async function readLinkedProjectName(dir) {
15714
- const abs = import_node_path63.default.join(dir, ".vercel", "project.json");
15843
+ const abs = import_node_path64.default.join(dir, ".vercel", "project.json");
15715
15844
  if (!await exists(abs)) return void 0;
15716
- const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
15845
+ const parsed = JSON.parse(await import_node_fs30.promises.readFile(abs, "utf8"));
15717
15846
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
15718
15847
  }
15719
15848
  function routeSource(route) {
@@ -15729,7 +15858,7 @@ async function addVercelServices(graph, services, scanPath) {
15729
15858
  read = await readVercelConfig(service.dir);
15730
15859
  projectName = await readLinkedProjectName(service.dir);
15731
15860
  } catch (err) {
15732
- recordExtractionError("infra vercel", import_node_path63.default.relative(scanPath, service.dir), err);
15861
+ recordExtractionError("infra vercel", import_node_path64.default.relative(scanPath, service.dir), err);
15733
15862
  continue;
15734
15863
  }
15735
15864
  if (!read && !projectName) continue;
@@ -15745,7 +15874,7 @@ async function addVercelServices(graph, services, scanPath) {
15745
15874
  const anchorId = service.node.id;
15746
15875
  if (!read) continue;
15747
15876
  const { config, relFile, raw } = read;
15748
- const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15877
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15749
15878
  const add = (edgeType, kind, name) => {
15750
15879
  if (!name) return;
15751
15880
  const result = emitPlatformResourceEdge(
@@ -15774,16 +15903,16 @@ async function addVercelServices(graph, services, scanPath) {
15774
15903
 
15775
15904
  // src/extract/infra/railway.ts
15776
15905
  init_cjs_shims();
15777
- var import_node_fs30 = require("fs");
15778
- var import_node_path64 = __toESM(require("path"), 1);
15906
+ var import_node_fs31 = require("fs");
15907
+ var import_node_path65 = __toESM(require("path"), 1);
15779
15908
  var import_smol_toml4 = require("smol-toml");
15780
15909
  var import_types54 = require("@neat.is/types");
15781
15910
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
15782
15911
  async function readRailwayConfig(dir) {
15783
15912
  for (const filename of RAILWAY_FILENAMES) {
15784
- const abs = import_node_path64.default.join(dir, filename);
15913
+ const abs = import_node_path65.default.join(dir, filename);
15785
15914
  if (!await exists(abs)) continue;
15786
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15915
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15787
15916
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
15788
15917
  return { config, relFile: filename, raw };
15789
15918
  }
@@ -15797,7 +15926,7 @@ async function addRailwayServices(graph, services, scanPath) {
15797
15926
  try {
15798
15927
  read = await readRailwayConfig(service.dir);
15799
15928
  } catch (err) {
15800
- recordExtractionError("infra railway", import_node_path64.default.relative(scanPath, service.dir), err);
15929
+ recordExtractionError("infra railway", import_node_path65.default.relative(scanPath, service.dir), err);
15801
15930
  continue;
15802
15931
  }
15803
15932
  if (!read) continue;
@@ -15807,7 +15936,7 @@ async function addRailwayServices(graph, services, scanPath) {
15807
15936
  }
15808
15937
  const anchorId = service.node.id;
15809
15938
  const { config, relFile, raw } = read;
15810
- const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15939
+ const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
15811
15940
  const add = (edgeType, kind, name) => {
15812
15941
  if (!name) return;
15813
15942
  const result = emitPlatformResourceEdge(
@@ -15832,15 +15961,15 @@ async function addRailwayServices(graph, services, scanPath) {
15832
15961
 
15833
15962
  // src/extract/infra/supabase.ts
15834
15963
  init_cjs_shims();
15835
- var import_node_fs31 = require("fs");
15836
- var import_node_path65 = __toESM(require("path"), 1);
15964
+ var import_node_fs32 = require("fs");
15965
+ var import_node_path66 = __toESM(require("path"), 1);
15837
15966
  var import_smol_toml5 = require("smol-toml");
15838
15967
  var import_types55 = require("@neat.is/types");
15839
15968
  async function readSupabaseConfig(dir) {
15840
- const relFile = import_node_path65.default.join("supabase", "config.toml");
15841
- const abs = import_node_path65.default.join(dir, relFile);
15969
+ const relFile = import_node_path66.default.join("supabase", "config.toml");
15970
+ const abs = import_node_path66.default.join(dir, relFile);
15842
15971
  if (!await exists(abs)) return null;
15843
- const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15972
+ const raw = await import_node_fs32.promises.readFile(abs, "utf8");
15844
15973
  const config = (0, import_smol_toml5.parse)(raw);
15845
15974
  return { config, relFile, raw };
15846
15975
  }
@@ -15852,7 +15981,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15852
15981
  try {
15853
15982
  read = await readSupabaseConfig(service.dir);
15854
15983
  } catch (err) {
15855
- recordExtractionError("infra supabase", import_node_path65.default.relative(scanPath, service.dir), err);
15984
+ recordExtractionError("infra supabase", import_node_path66.default.relative(scanPath, service.dir), err);
15856
15985
  continue;
15857
15986
  }
15858
15987
  if (!read) continue;
@@ -15867,7 +15996,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15867
15996
  });
15868
15997
  }
15869
15998
  const anchorId = service.node.id;
15870
- const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
15999
+ const evidenceFile = toPosix(import_node_path66.default.relative(scanPath, import_node_path66.default.join(service.dir, relFile)));
15871
16000
  const add = (edgeType, kind, name) => {
15872
16001
  if (!name) return;
15873
16002
  const result = emitPlatformResourceEdge(
@@ -15909,7 +16038,7 @@ async function addInfra(graph, scanPath, services) {
15909
16038
 
15910
16039
  // src/extract/zod-shapes.ts
15911
16040
  init_cjs_shims();
15912
- var import_node_path66 = __toESM(require("path"), 1);
16041
+ var import_node_path67 = __toESM(require("path"), 1);
15913
16042
  var import_tree_sitter18 = __toESM(require("tree-sitter"), 1);
15914
16043
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15915
16044
  var import_types56 = require("@neat.is/types");
@@ -16004,7 +16133,7 @@ function topLevelSchemas(root) {
16004
16133
  }
16005
16134
  function zodShapesFromFile(file, serviceDir) {
16006
16135
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
16007
- const tree = parseSource3(parserForExt4(import_node_path66.default.extname(file.path)), file.content);
16136
+ const tree = parseSource3(parserForExt4(import_node_path67.default.extname(file.path)), file.content);
16008
16137
  const out = [];
16009
16138
  const seen = /* @__PURE__ */ new Set();
16010
16139
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -16022,7 +16151,7 @@ function zodShapesFromFile(file, serviceDir) {
16022
16151
  name,
16023
16152
  fields,
16024
16153
  evidence: {
16025
- file: import_node_path66.default.relative(serviceDir, file.path),
16154
+ file: import_node_path67.default.relative(serviceDir, file.path),
16026
16155
  line,
16027
16156
  snippet: snippet(file.content, line)
16028
16157
  }
@@ -16290,12 +16419,12 @@ async function addFirestoreRules(graph, services) {
16290
16419
  }
16291
16420
 
16292
16421
  // src/extract/index.ts
16293
- var import_node_path68 = __toESM(require("path"), 1);
16422
+ var import_node_path69 = __toESM(require("path"), 1);
16294
16423
 
16295
16424
  // src/extract/retire.ts
16296
16425
  init_cjs_shims();
16297
- var import_node_fs32 = require("fs");
16298
- var import_node_path67 = __toESM(require("path"), 1);
16426
+ var import_node_fs33 = require("fs");
16427
+ var import_node_path68 = __toESM(require("path"), 1);
16299
16428
  var import_types58 = require("@neat.is/types");
16300
16429
  function dropOrphanedFileNodes(graph) {
16301
16430
  const orphans = [];
@@ -16316,11 +16445,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
16316
16445
  if (edge.provenance !== import_types58.Provenance.EXTRACTED) return;
16317
16446
  const evidenceFile = edge.evidence?.file;
16318
16447
  if (!evidenceFile) return;
16319
- if (import_node_path67.default.isAbsolute(evidenceFile)) {
16320
- if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
16448
+ if (import_node_path68.default.isAbsolute(evidenceFile)) {
16449
+ if (!(0, import_node_fs33.existsSync)(evidenceFile)) toDrop.push(id);
16321
16450
  return;
16322
16451
  }
16323
- const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path67.default.join(base, evidenceFile)));
16452
+ const found = bases.some((base) => (0, import_node_fs33.existsSync)(import_node_path68.default.join(base, evidenceFile)));
16324
16453
  if (!found) toDrop.push(id);
16325
16454
  });
16326
16455
  for (const id of toDrop) graph.dropEdge(id);
@@ -16332,6 +16461,7 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
16332
16461
  async function extractFromDirectory(graph, scanPath, opts = {}) {
16333
16462
  await ensureCompatLoaded();
16334
16463
  drainExtractionErrors();
16464
+ drainSkippedFiles();
16335
16465
  const services = await discoverServices(scanPath);
16336
16466
  const phase1Nodes = addServiceNodes(graph, services);
16337
16467
  await addServiceAliases(graph, scanPath, services);
@@ -16376,8 +16506,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16376
16506
  }
16377
16507
  }
16378
16508
  const droppedEntries = drainDroppedExtracted();
16509
+ const skippedEntries = drainSkippedFiles();
16379
16510
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
16380
- const rejectedPath = import_node_path68.default.join(import_node_path68.default.dirname(opts.errorsPath), "rejected.ndjson");
16511
+ const rejectedPath = import_node_path69.default.join(import_node_path69.default.dirname(opts.errorsPath), "rejected.ndjson");
16381
16512
  try {
16382
16513
  await writeRejectedExtracted(droppedEntries, rejectedPath);
16383
16514
  } catch (err) {
@@ -16394,7 +16525,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16394
16525
  errorEntries,
16395
16526
  ghostsRetired,
16396
16527
  extractedDropped: droppedEntries.length,
16397
- droppedEntries
16528
+ droppedEntries,
16529
+ skippedFiles: skippedEntries.length,
16530
+ skippedEntries
16398
16531
  };
16399
16532
  emitNeatEvent({
16400
16533
  type: "extraction-complete",
@@ -16411,8 +16544,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16411
16544
 
16412
16545
  // src/persist.ts
16413
16546
  init_cjs_shims();
16414
- var import_node_fs33 = require("fs");
16415
- var import_node_path69 = __toESM(require("path"), 1);
16547
+ var import_node_fs34 = require("fs");
16548
+ var import_node_path70 = __toESM(require("path"), 1);
16416
16549
  var import_types59 = require("@neat.is/types");
16417
16550
  var SCHEMA_VERSION = 7;
16418
16551
  function migrateV1ToV2(payload) {
@@ -16467,7 +16600,43 @@ function migrateV2ToV3(payload) {
16467
16600
  return { ...payload, schemaVersion: 3 };
16468
16601
  }
16469
16602
  async function ensureDir(filePath) {
16470
- await import_node_fs33.promises.mkdir(import_node_path69.default.dirname(filePath), { recursive: true });
16603
+ await import_node_fs34.promises.mkdir(import_node_path70.default.dirname(filePath), { recursive: true });
16604
+ }
16605
+ var WRITE_FLUSH_CHARS = 4 * 1024 * 1024;
16606
+ function jsonOrUndefined(value) {
16607
+ return JSON.stringify(value);
16608
+ }
16609
+ function* serializeArray(items) {
16610
+ yield "[";
16611
+ for (let i = 0; i < items.length; i++) {
16612
+ if (i > 0) yield ",";
16613
+ yield jsonOrUndefined(items[i]) ?? "null";
16614
+ }
16615
+ yield "]";
16616
+ }
16617
+ function isPlainObject(value) {
16618
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
16619
+ const proto = Object.getPrototypeOf(value);
16620
+ return proto === Object.prototype || proto === null;
16621
+ }
16622
+ function* serializeObject(obj) {
16623
+ yield "{";
16624
+ let first = true;
16625
+ for (const key of Object.keys(obj)) {
16626
+ const value = obj[key];
16627
+ const descend = Array.isArray(value) || isPlainObject(value);
16628
+ if (!descend && jsonOrUndefined(value) === void 0) continue;
16629
+ if (!first) yield ",";
16630
+ first = false;
16631
+ yield `${JSON.stringify(key)}:`;
16632
+ if (Array.isArray(value)) yield* serializeArray(value);
16633
+ else if (isPlainObject(value)) yield* serializeObject(value);
16634
+ else yield jsonOrUndefined(value);
16635
+ }
16636
+ yield "}";
16637
+ }
16638
+ function* serializeGraphChunks(payload) {
16639
+ yield* serializeObject(payload);
16471
16640
  }
16472
16641
  async function saveGraphToDisk(graph, outPath) {
16473
16642
  await ensureDir(outPath);
@@ -16477,13 +16646,26 @@ async function saveGraphToDisk(graph, outPath) {
16477
16646
  graph: graph.export()
16478
16647
  };
16479
16648
  const tmp = `${outPath}.tmp`;
16480
- await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
16481
- await import_node_fs33.promises.rename(tmp, outPath);
16649
+ const handle = await import_node_fs34.promises.open(tmp, "w");
16650
+ try {
16651
+ let buffered = "";
16652
+ for (const chunk of serializeGraphChunks(payload)) {
16653
+ buffered += chunk;
16654
+ if (buffered.length >= WRITE_FLUSH_CHARS) {
16655
+ await handle.write(buffered, null, "utf8");
16656
+ buffered = "";
16657
+ }
16658
+ }
16659
+ if (buffered.length > 0) await handle.write(buffered, null, "utf8");
16660
+ } finally {
16661
+ await handle.close();
16662
+ }
16663
+ await import_node_fs34.promises.rename(tmp, outPath);
16482
16664
  }
16483
16665
  async function loadGraphFromDisk(graph, outPath) {
16484
16666
  let raw;
16485
16667
  try {
16486
- raw = await import_node_fs33.promises.readFile(outPath, "utf8");
16668
+ raw = await import_node_fs34.promises.readFile(outPath, "utf8");
16487
16669
  } catch (err) {
16488
16670
  if (err.code === "ENOENT") return;
16489
16671
  throw err;
@@ -16557,23 +16739,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
16557
16739
 
16558
16740
  // src/projects.ts
16559
16741
  init_cjs_shims();
16560
- var import_node_path70 = __toESM(require("path"), 1);
16742
+ var import_node_path71 = __toESM(require("path"), 1);
16561
16743
  function pathsForProject(project, baseDir) {
16562
16744
  if (project === DEFAULT_PROJECT) {
16563
16745
  return {
16564
- snapshotPath: import_node_path70.default.join(baseDir, "graph.json"),
16565
- errorsPath: import_node_path70.default.join(baseDir, "errors.ndjson"),
16566
- staleEventsPath: import_node_path70.default.join(baseDir, "stale-events.ndjson"),
16567
- embeddingsCachePath: import_node_path70.default.join(baseDir, "embeddings.json"),
16568
- policyViolationsPath: import_node_path70.default.join(baseDir, "policy-violations.ndjson")
16746
+ snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
16747
+ errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
16748
+ staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
16749
+ embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
16750
+ policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
16569
16751
  };
16570
16752
  }
16571
16753
  return {
16572
- snapshotPath: import_node_path70.default.join(baseDir, `${project}.json`),
16573
- errorsPath: import_node_path70.default.join(baseDir, `errors.${project}.ndjson`),
16574
- staleEventsPath: import_node_path70.default.join(baseDir, `stale-events.${project}.ndjson`),
16575
- embeddingsCachePath: import_node_path70.default.join(baseDir, `embeddings.${project}.json`),
16576
- policyViolationsPath: import_node_path70.default.join(baseDir, `policy-violations.${project}.ndjson`)
16754
+ snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
16755
+ errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
16756
+ staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
16757
+ embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
16758
+ policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
16577
16759
  };
16578
16760
  }
16579
16761
  var Projects = class {
@@ -16615,15 +16797,15 @@ var import_types101 = require("@neat.is/types");
16615
16797
 
16616
16798
  // src/extend/index.ts
16617
16799
  init_cjs_shims();
16618
- var import_node_fs35 = require("fs");
16619
- var import_node_path72 = __toESM(require("path"), 1);
16800
+ var import_node_fs36 = require("fs");
16801
+ var import_node_path73 = __toESM(require("path"), 1);
16620
16802
  var import_node_os2 = __toESM(require("os"), 1);
16621
16803
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
16622
16804
 
16623
16805
  // src/installers/package-manager.ts
16624
16806
  init_cjs_shims();
16625
- var import_node_fs34 = require("fs");
16626
- var import_node_path71 = __toESM(require("path"), 1);
16807
+ var import_node_fs35 = require("fs");
16808
+ var import_node_path72 = __toESM(require("path"), 1);
16627
16809
  var import_node_child_process = require("child_process");
16628
16810
  var LOCKFILE_PRIORITY = [
16629
16811
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -16638,29 +16820,29 @@ var LOCKFILE_PRIORITY = [
16638
16820
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
16639
16821
  async function exists2(p) {
16640
16822
  try {
16641
- await import_node_fs34.promises.access(p);
16823
+ await import_node_fs35.promises.access(p);
16642
16824
  return true;
16643
16825
  } catch {
16644
16826
  return false;
16645
16827
  }
16646
16828
  }
16647
16829
  async function detectPackageManager(serviceDir) {
16648
- let dir = import_node_path71.default.resolve(serviceDir);
16830
+ let dir = import_node_path72.default.resolve(serviceDir);
16649
16831
  const stops = /* @__PURE__ */ new Set();
16650
16832
  for (let i = 0; i < 64; i++) {
16651
16833
  if (stops.has(dir)) break;
16652
16834
  stops.add(dir);
16653
16835
  for (const candidate of LOCKFILE_PRIORITY) {
16654
- const lockPath = import_node_path71.default.join(dir, candidate.lockfile);
16836
+ const lockPath = import_node_path72.default.join(dir, candidate.lockfile);
16655
16837
  if (await exists2(lockPath)) {
16656
16838
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
16657
16839
  }
16658
16840
  }
16659
- const parent = import_node_path71.default.dirname(dir);
16841
+ const parent = import_node_path72.default.dirname(dir);
16660
16842
  if (parent === dir) break;
16661
16843
  dir = parent;
16662
16844
  }
16663
- return { pm: "npm", cwd: import_node_path71.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16845
+ return { pm: "npm", cwd: import_node_path72.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16664
16846
  }
16665
16847
  async function runPackageManagerInstall(cmd) {
16666
16848
  return new Promise((resolve) => {
@@ -16702,15 +16884,15 @@ ${err.message}`
16702
16884
  // src/extend/index.ts
16703
16885
  async function fileExists2(p) {
16704
16886
  try {
16705
- await import_node_fs35.promises.access(p);
16887
+ await import_node_fs36.promises.access(p);
16706
16888
  return true;
16707
16889
  } catch {
16708
16890
  return false;
16709
16891
  }
16710
16892
  }
16711
16893
  async function readPackageJson(scanPath) {
16712
- const pkgPath = import_node_path72.default.join(scanPath, "package.json");
16713
- const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
16894
+ const pkgPath = import_node_path73.default.join(scanPath, "package.json");
16895
+ const raw = await import_node_fs36.promises.readFile(pkgPath, "utf8");
16714
16896
  return JSON.parse(raw);
16715
16897
  }
16716
16898
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -16724,15 +16906,15 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
16724
16906
  async function findHookFiles(scanPath) {
16725
16907
  const found = [];
16726
16908
  const walk11 = async (dir) => {
16727
- const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
16909
+ const entries = await import_node_fs36.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
16728
16910
  for (const entry2 of entries) {
16729
16911
  if (entry2.isDirectory()) {
16730
16912
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
16731
- await walk11(import_node_path72.default.join(dir, entry2.name));
16913
+ await walk11(import_node_path73.default.join(dir, entry2.name));
16732
16914
  } else if (entry2.isFile()) {
16733
16915
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
16734
- const rel = import_node_path72.default.relative(scanPath, import_node_path72.default.join(dir, entry2.name));
16735
- found.push(rel.split(import_node_path72.default.sep).join("/"));
16916
+ const rel = import_node_path73.default.relative(scanPath, import_node_path73.default.join(dir, entry2.name));
16917
+ found.push(rel.split(import_node_path73.default.sep).join("/"));
16736
16918
  }
16737
16919
  }
16738
16920
  }
@@ -16743,7 +16925,7 @@ async function findHookFiles(scanPath) {
16743
16925
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16744
16926
  let fallback = null;
16745
16927
  for (const file of hookFiles) {
16746
- const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(scanPath, file), "utf8");
16928
+ const content = await import_node_fs36.promises.readFile(import_node_path73.default.join(scanPath, file), "utf8");
16747
16929
  const patched = splicedContent(content, snippet2);
16748
16930
  if (patched !== null) return { file, content, patched };
16749
16931
  if (fallback === null) fallback = { file, content };
@@ -16751,12 +16933,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16751
16933
  return { file: fallback.file, content: fallback.content, patched: null };
16752
16934
  }
16753
16935
  function extendLogPath() {
16754
- return process.env.NEAT_EXTEND_LOG ?? import_node_path72.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16936
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path73.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16755
16937
  }
16756
16938
  async function appendExtendLog(entry2) {
16757
16939
  const logPath = extendLogPath();
16758
- await import_node_fs35.promises.mkdir(import_node_path72.default.dirname(logPath), { recursive: true });
16759
- await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
16940
+ await import_node_fs36.promises.mkdir(import_node_path73.default.dirname(logPath), { recursive: true });
16941
+ await import_node_fs36.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
16760
16942
  }
16761
16943
  function splicedContent(fileContent, snippet2) {
16762
16944
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -16814,7 +16996,7 @@ function lookupInstrumentation(library, installedVersion) {
16814
16996
  }
16815
16997
  async function describeProjectInstrumentation(ctx) {
16816
16998
  const hookFiles = await findHookFiles(ctx.scanPath);
16817
- const envNeat = await fileExists2(import_node_path72.default.join(ctx.scanPath, ".env.neat"));
16999
+ const envNeat = await fileExists2(import_node_path73.default.join(ctx.scanPath, ".env.neat"));
16818
17000
  const registryInstrPackages = new Set(
16819
17001
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
16820
17002
  );
@@ -16836,7 +17018,7 @@ async function applyExtension(ctx, args, options) {
16836
17018
  );
16837
17019
  }
16838
17020
  for (const file of hookFiles) {
16839
- const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
17021
+ const content = await import_node_fs36.promises.readFile(import_node_path73.default.join(ctx.scanPath, file), "utf8");
16840
17022
  if (content.includes(args.registration_snippet)) {
16841
17023
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
16842
17024
  }
@@ -16848,18 +17030,18 @@ async function applyExtension(ctx, args, options) {
16848
17030
  );
16849
17031
  }
16850
17032
  const primaryFile = primary.file;
16851
- const primaryPath = import_node_path72.default.join(ctx.scanPath, primaryFile);
17033
+ const primaryPath = import_node_path73.default.join(ctx.scanPath, primaryFile);
16852
17034
  const filesTouched = [];
16853
17035
  const depsAdded = [];
16854
- const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
17036
+ const pkgPath = import_node_path73.default.join(ctx.scanPath, "package.json");
16855
17037
  const pkg = await readPackageJson(ctx.scanPath);
16856
17038
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
16857
17039
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
16858
- await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
17040
+ await import_node_fs36.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16859
17041
  filesTouched.push("package.json");
16860
17042
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
16861
17043
  }
16862
- await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
17044
+ await import_node_fs36.promises.writeFile(primaryPath, primary.patched, "utf8");
16863
17045
  filesTouched.push(primaryFile);
16864
17046
  const cmd = await detectPackageManager(ctx.scanPath);
16865
17047
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -16890,7 +17072,7 @@ async function dryRunExtension(ctx, args) {
16890
17072
  };
16891
17073
  }
16892
17074
  for (const file of hookFiles) {
16893
- const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
17075
+ const content = await import_node_fs36.promises.readFile(import_node_path73.default.join(ctx.scanPath, file), "utf8");
16894
17076
  if (content.includes(args.registration_snippet)) {
16895
17077
  return {
16896
17078
  library: args.library,
@@ -16925,28 +17107,28 @@ async function rollbackExtension(ctx, args) {
16925
17107
  if (!await fileExists2(logPath)) {
16926
17108
  return { undone: false, message: "no apply found for library" };
16927
17109
  }
16928
- const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
17110
+ const raw = await import_node_fs36.promises.readFile(logPath, "utf8");
16929
17111
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
16930
17112
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
16931
17113
  if (!match) {
16932
17114
  return { undone: false, message: "no apply found for library" };
16933
17115
  }
16934
- const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
17116
+ const pkgPath = import_node_path73.default.join(ctx.scanPath, "package.json");
16935
17117
  if (await fileExists2(pkgPath)) {
16936
17118
  const pkg = await readPackageJson(ctx.scanPath);
16937
17119
  if (pkg.dependencies?.[match.instrumentation_package]) {
16938
17120
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
16939
17121
  pkg.dependencies = rest;
16940
- await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
17122
+ await import_node_fs36.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16941
17123
  }
16942
17124
  }
16943
17125
  const hookFiles = await findHookFiles(ctx.scanPath);
16944
17126
  for (const file of hookFiles) {
16945
- const filePath = import_node_path72.default.join(ctx.scanPath, file);
16946
- const content = await import_node_fs35.promises.readFile(filePath, "utf8");
17127
+ const filePath = import_node_path73.default.join(ctx.scanPath, file);
17128
+ const content = await import_node_fs36.promises.readFile(filePath, "utf8");
16947
17129
  if (content.includes(match.registration_snippet)) {
16948
17130
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
16949
- await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
17131
+ await import_node_fs36.promises.writeFile(filePath, filtered, "utf8");
16950
17132
  break;
16951
17133
  }
16952
17134
  }
@@ -18583,7 +18765,7 @@ async function askGraph(graph, question, opts = {}) {
18583
18765
 
18584
18766
  // src/diff.ts
18585
18767
  init_cjs_shims();
18586
- var import_node_fs36 = require("fs");
18768
+ var import_node_fs37 = require("fs");
18587
18769
  async function loadSnapshotForDiff(target) {
18588
18770
  if (/^https?:\/\//i.test(target)) {
18589
18771
  const res = await fetch(target);
@@ -18592,7 +18774,7 @@ async function loadSnapshotForDiff(target) {
18592
18774
  }
18593
18775
  return await res.json();
18594
18776
  }
18595
- const raw = await import_node_fs36.promises.readFile(target, "utf8");
18777
+ const raw = await import_node_fs37.promises.readFile(target, "utf8");
18596
18778
  return JSON.parse(raw);
18597
18779
  }
18598
18780
  function indexEntries(entries) {
@@ -18660,28 +18842,28 @@ function canonicalJson(value) {
18660
18842
 
18661
18843
  // src/registry.ts
18662
18844
  init_cjs_shims();
18663
- var import_node_fs37 = require("fs");
18845
+ var import_node_fs38 = require("fs");
18664
18846
  var import_node_os3 = __toESM(require("os"), 1);
18665
- var import_node_path73 = __toESM(require("path"), 1);
18847
+ var import_node_path74 = __toESM(require("path"), 1);
18666
18848
  var import_types63 = require("@neat.is/types");
18667
18849
  var LOCK_TIMEOUT_MS = 5e3;
18668
18850
  var LOCK_RETRY_MS = 50;
18669
18851
  function neatHome() {
18670
18852
  const override = process.env.NEAT_HOME;
18671
- if (override && override.length > 0) return import_node_path73.default.resolve(override);
18672
- return import_node_path73.default.join(import_node_os3.default.homedir(), ".neat");
18853
+ if (override && override.length > 0) return import_node_path74.default.resolve(override);
18854
+ return import_node_path74.default.join(import_node_os3.default.homedir(), ".neat");
18673
18855
  }
18674
18856
  function registryPath() {
18675
- return import_node_path73.default.join(neatHome(), "projects.json");
18857
+ return import_node_path74.default.join(neatHome(), "projects.json");
18676
18858
  }
18677
18859
  function registryLockPath() {
18678
- return import_node_path73.default.join(neatHome(), "projects.json.lock");
18860
+ return import_node_path74.default.join(neatHome(), "projects.json.lock");
18679
18861
  }
18680
18862
  function daemonPidPath() {
18681
- return import_node_path73.default.join(neatHome(), "neatd.pid");
18863
+ return import_node_path74.default.join(neatHome(), "neatd.pid");
18682
18864
  }
18683
18865
  function daemonsDir() {
18684
- return import_node_path73.default.join(neatHome(), "daemons");
18866
+ return import_node_path74.default.join(neatHome(), "daemons");
18685
18867
  }
18686
18868
  function isFiniteInt(v) {
18687
18869
  return typeof v === "number" && Number.isFinite(v);
@@ -18714,7 +18896,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
18714
18896
  const dir = daemonsDir();
18715
18897
  let names;
18716
18898
  try {
18717
- names = await import_node_fs37.promises.readdir(dir);
18899
+ names = await import_node_fs38.promises.readdir(dir);
18718
18900
  } catch (err) {
18719
18901
  if (err.code === "ENOENT") return [];
18720
18902
  throw err;
@@ -18722,10 +18904,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
18722
18904
  const out = [];
18723
18905
  for (const name of names) {
18724
18906
  if (!name.endsWith(".json")) continue;
18725
- const file = import_node_path73.default.join(dir, name);
18907
+ const file = import_node_path74.default.join(dir, name);
18726
18908
  let raw;
18727
18909
  try {
18728
- raw = await import_node_fs37.promises.readFile(file, "utf8");
18910
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
18729
18911
  } catch {
18730
18912
  continue;
18731
18913
  }
@@ -18751,7 +18933,7 @@ function isPidAliveDefault(pid) {
18751
18933
  }
18752
18934
  async function readPidFile(file) {
18753
18935
  try {
18754
- const raw = await import_node_fs37.promises.readFile(file, "utf8");
18936
+ const raw = await import_node_fs38.promises.readFile(file, "utf8");
18755
18937
  const pid = Number.parseInt(raw.trim(), 10);
18756
18938
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
18757
18939
  } catch {
@@ -18795,28 +18977,28 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
18795
18977
  case "command":
18796
18978
  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.`;
18797
18979
  case "stale":
18798
- 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.`;
18980
+ 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.`;
18799
18981
  }
18800
18982
  }
18801
18983
  async function writeAtomically(target, contents) {
18802
- await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(target), { recursive: true });
18984
+ await import_node_fs38.promises.mkdir(import_node_path74.default.dirname(target), { recursive: true });
18803
18985
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
18804
- const fd = await import_node_fs37.promises.open(tmp, "w");
18986
+ const fd = await import_node_fs38.promises.open(tmp, "w");
18805
18987
  try {
18806
18988
  await fd.writeFile(contents, "utf8");
18807
18989
  await fd.sync();
18808
18990
  } finally {
18809
18991
  await fd.close();
18810
18992
  }
18811
- await import_node_fs37.promises.rename(tmp, target);
18993
+ await import_node_fs38.promises.rename(tmp, target);
18812
18994
  }
18813
18995
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
18814
18996
  const deadline = Date.now() + timeoutMs;
18815
- await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(lockPath), { recursive: true });
18997
+ await import_node_fs38.promises.mkdir(import_node_path74.default.dirname(lockPath), { recursive: true });
18816
18998
  let probedHolder = false;
18817
18999
  while (true) {
18818
19000
  try {
18819
- const fd = await import_node_fs37.promises.open(lockPath, "wx");
19001
+ const fd = await import_node_fs38.promises.open(lockPath, "wx");
18820
19002
  try {
18821
19003
  await fd.writeFile(`${process.pid}
18822
19004
  `, "utf8");
@@ -18830,18 +19012,36 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
18830
19012
  if (!probedHolder) {
18831
19013
  probedHolder = true;
18832
19014
  const holder = await classifyLockHolder(lockPath, probe);
18833
- if (holder.kind === "daemon") throw new Error(lockHolderMessage(holder, lockPath, timeoutMs));
19015
+ if (holder.kind === "daemon") {
19016
+ throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
19017
+ }
19018
+ if (holder.kind === "stale" && await isReclaimableLock(lockPath)) {
19019
+ await import_node_fs38.promises.unlink(lockPath).catch(() => {
19020
+ });
19021
+ continue;
19022
+ }
18834
19023
  }
18835
19024
  if (Date.now() >= deadline) {
18836
19025
  const holder = await classifyLockHolder(lockPath, probe);
18837
- throw new Error(lockHolderMessage(holder, lockPath, timeoutMs));
19026
+ throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
18838
19027
  }
18839
19028
  await new Promise((r) => setTimeout(r, LOCK_RETRY_MS));
18840
19029
  }
18841
19030
  }
18842
19031
  }
19032
+ var LOCK_UNSTAMPED_GRACE_MS = 5e3;
19033
+ async function isReclaimableLock(lockPath) {
19034
+ const pid = await readLockPid(lockPath);
19035
+ if (pid !== void 0) return true;
19036
+ try {
19037
+ const stat = await import_node_fs38.promises.stat(lockPath);
19038
+ return Date.now() - stat.mtimeMs > LOCK_UNSTAMPED_GRACE_MS;
19039
+ } catch {
19040
+ return false;
19041
+ }
19042
+ }
18843
19043
  async function releaseLock(lockPath) {
18844
- await import_node_fs37.promises.unlink(lockPath).catch(() => {
19044
+ await import_node_fs38.promises.unlink(lockPath).catch(() => {
18845
19045
  });
18846
19046
  }
18847
19047
  async function withLock(fn) {
@@ -18857,7 +19057,7 @@ async function readRegistry() {
18857
19057
  const file = registryPath();
18858
19058
  let raw;
18859
19059
  try {
18860
- raw = await import_node_fs37.promises.readFile(file, "utf8");
19060
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
18861
19061
  } catch (err) {
18862
19062
  if (err.code === "ENOENT") {
18863
19063
  return { version: 1, projects: [] };
@@ -18871,6 +19071,18 @@ async function writeRegistry(reg) {
18871
19071
  const validated = import_types63.RegistryFileSchema.parse(reg);
18872
19072
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
18873
19073
  }
19074
+ var RegistryError = class extends Error {
19075
+ constructor(message) {
19076
+ super(message);
19077
+ this.name = "RegistryError";
19078
+ }
19079
+ };
19080
+ var RegistryLockError = class extends RegistryError {
19081
+ constructor(message) {
19082
+ super(message);
19083
+ this.name = "RegistryLockError";
19084
+ }
19085
+ };
18874
19086
  async function getProject(name) {
18875
19087
  const reg = await readRegistry();
18876
19088
  return reg.projects.find((p) => p.name === name);
@@ -18883,7 +19095,7 @@ async function setStatus(name, status2) {
18883
19095
  return withLock(async () => {
18884
19096
  const reg = await readRegistry();
18885
19097
  const entry2 = reg.projects.find((p) => p.name === name);
18886
- if (!entry2) throw new Error(`neat registry: no project named "${name}"`);
19098
+ if (!entry2) throw new RegistryError(`no project named "${name}"`);
18887
19099
  entry2.status = status2;
18888
19100
  await writeRegistry(reg);
18889
19101
  return entry2;
@@ -18912,7 +19124,7 @@ function pruneTtlMs() {
18912
19124
  }
18913
19125
  async function statPathStatus(p) {
18914
19126
  try {
18915
- const stat = await import_node_fs37.promises.stat(p);
19127
+ const stat = await import_node_fs38.promises.stat(p);
18916
19128
  return stat.isDirectory() ? "present" : "unknown";
18917
19129
  } catch (err) {
18918
19130
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -19013,8 +19225,8 @@ init_auth();
19013
19225
  // src/connectors-config.ts
19014
19226
  init_cjs_shims();
19015
19227
  var import_node_os4 = __toESM(require("os"), 1);
19016
- var import_node_path74 = __toESM(require("path"), 1);
19017
- var import_node_fs38 = require("fs");
19228
+ var import_node_path75 = __toESM(require("path"), 1);
19229
+ var import_node_fs39 = require("fs");
19018
19230
  var CONNECTORS_CONFIG_VERSION = 1;
19019
19231
  var EnvRefUnsetError = class extends Error {
19020
19232
  ref;
@@ -19028,17 +19240,17 @@ var EnvRefUnsetError = class extends Error {
19028
19240
  };
19029
19241
  function neatHome2() {
19030
19242
  const override = process.env.NEAT_HOME;
19031
- if (override && override.length > 0) return import_node_path74.default.resolve(override);
19032
- return import_node_path74.default.join(import_node_os4.default.homedir(), ".neat");
19243
+ if (override && override.length > 0) return import_node_path75.default.resolve(override);
19244
+ return import_node_path75.default.join(import_node_os4.default.homedir(), ".neat");
19033
19245
  }
19034
19246
  function connectorsConfigPath(home = neatHome2()) {
19035
- return import_node_path74.default.join(home, "connectors.json");
19247
+ return import_node_path75.default.join(home, "connectors.json");
19036
19248
  }
19037
19249
  var MODE_MASK_LOOSER_THAN_0600 = 63;
19038
19250
  async function warnIfModeLooserThan0600(file) {
19039
19251
  if (process.platform === "win32") return;
19040
19252
  try {
19041
- const stat = await import_node_fs38.promises.stat(file);
19253
+ const stat = await import_node_fs39.promises.stat(file);
19042
19254
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
19043
19255
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
19044
19256
  console.warn(
@@ -19052,7 +19264,7 @@ async function readConnectorsConfig(home = neatHome2()) {
19052
19264
  const file = connectorsConfigPath(home);
19053
19265
  let raw;
19054
19266
  try {
19055
- raw = await import_node_fs38.promises.readFile(file, "utf8");
19267
+ raw = await import_node_fs39.promises.readFile(file, "utf8");
19056
19268
  } catch (err) {
19057
19269
  if (err.code === "ENOENT") {
19058
19270
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -19848,10 +20060,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
19848
20060
  // src/connectors/supabase/map.ts
19849
20061
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
19850
20062
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
19851
- function targetFromRestPath(path81) {
19852
- const rpcMatch = REST_RPC_PATH_RE.exec(path81);
20063
+ function targetFromRestPath(path82) {
20064
+ const rpcMatch = REST_RPC_PATH_RE.exec(path82);
19853
20065
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
19854
- const tableMatch = REST_TABLE_PATH_RE.exec(path81);
20066
+ const tableMatch = REST_TABLE_PATH_RE.exec(path82);
19855
20067
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
19856
20068
  return null;
19857
20069
  }
@@ -20455,9 +20667,9 @@ function parseFirebaseTargetName(targetName) {
20455
20667
  const secondSep = rest.indexOf(FIELD_SEP);
20456
20668
  if (secondSep === -1) return null;
20457
20669
  const method = rest.slice(0, secondSep);
20458
- const path81 = rest.slice(secondSep + 1);
20459
- if (!resourceName || !method || !path81) return null;
20460
- return { resourceName, method, path: path81 };
20670
+ const path82 = rest.slice(secondSep + 1);
20671
+ if (!resourceName || !method || !path82) return null;
20672
+ return { resourceName, method, path: path82 };
20461
20673
  }
20462
20674
  function resourceNameFor(type, labels) {
20463
20675
  if (!labels) return null;
@@ -20495,14 +20707,14 @@ function mapLogEntryToSignal(entry2) {
20495
20707
  if (!req2) return null;
20496
20708
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
20497
20709
  const method = req2.requestMethod.toUpperCase();
20498
- const path81 = pathFromRequestUrl(req2.requestUrl);
20499
- if (path81 === null) return null;
20710
+ const path82 = pathFromRequestUrl(req2.requestUrl);
20711
+ if (path82 === null) return null;
20500
20712
  const timestamp = entry2.timestamp;
20501
20713
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
20502
20714
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
20503
20715
  return {
20504
20716
  targetKind: resourceType,
20505
- targetName: packFirebaseTargetName({ resourceName, method, path: path81 }),
20717
+ targetName: packFirebaseTargetName({ resourceName, method, path: path82 }),
20506
20718
  callCount: 1,
20507
20719
  errorCount: isError ? 1 : 0,
20508
20720
  lastObservedIso: timestamp
@@ -20527,9 +20739,9 @@ var import_types71 = require("@neat.is/types");
20527
20739
  function normalizeName(name) {
20528
20740
  return name.toLowerCase().replace(/[^a-z0-9]/g, "");
20529
20741
  }
20530
- function inferServiceName(graph, resourceName, method, path81) {
20742
+ function inferServiceName(graph, resourceName, method, path82) {
20531
20743
  const wantedMethod = method.toUpperCase();
20532
- const wantedPath = normalizePathTemplate(path81);
20744
+ const wantedPath = normalizePathTemplate(path82);
20533
20745
  const services = [];
20534
20746
  const owners = /* @__PURE__ */ new Set();
20535
20747
  graph.forEachNode((_id, attrs) => {
@@ -20746,7 +20958,7 @@ function mapEventToSignal(event) {
20746
20958
  if (Number.isNaN(observedAt.getTime())) return null;
20747
20959
  const statusCode = metadata?.statusCode;
20748
20960
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
20749
- const path81 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20961
+ const path82 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20750
20962
  return {
20751
20963
  targetKind: CLOUDFLARE_TARGET_KIND,
20752
20964
  targetName: scriptName,
@@ -20754,7 +20966,7 @@ function mapEventToSignal(event) {
20754
20966
  errorCount: isError ? 1 : 0,
20755
20967
  lastObservedIso: observedAt.toISOString(),
20756
20968
  method,
20757
- ...path81 ? { path: path81 } : {},
20969
+ ...path82 ? { path: path82 } : {},
20758
20970
  ...typeof statusCode === "number" ? { statusCode } : {},
20759
20971
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
20760
20972
  };
@@ -20800,8 +21012,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
20800
21012
  });
20801
21013
  return found;
20802
21014
  }
20803
- function findMatchingRouteNode(graph, serviceName, method, path81) {
20804
- const normalizedPath = normalizePathTemplate(path81);
21015
+ function findMatchingRouteNode(graph, serviceName, method, path82) {
21016
+ const normalizedPath = normalizePathTemplate(path82);
20805
21017
  let found = null;
20806
21018
  graph.forEachNode((id, attrs) => {
20807
21019
  if (found) return;
@@ -20818,10 +21030,10 @@ function createCloudflareResolveTarget(config, graph) {
20818
21030
  return (signal) => {
20819
21031
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
20820
21032
  const scriptName = signal.targetName;
20821
- const { method, path: path81 } = signal;
21033
+ const { method, path: path82 } = signal;
20822
21034
  const resolveRouteGrain = (serviceName, wholeFileId) => {
20823
- if (!method || !path81) return wholeFileId;
20824
- return findMatchingRouteNode(graph, serviceName, method, path81) ?? wholeFileId;
21035
+ if (!method || !path82) return wholeFileId;
21036
+ return findMatchingRouteNode(graph, serviceName, method, path82) ?? wholeFileId;
20825
21037
  };
20826
21038
  const mapping = config.workers?.[scriptName];
20827
21039
  if (mapping) {
@@ -21173,9 +21385,9 @@ function parseCloudRunTargetName(targetName) {
21173
21385
  const secondSep = rest.indexOf(FIELD_SEP2);
21174
21386
  if (secondSep === -1) return null;
21175
21387
  const method = rest.slice(0, secondSep);
21176
- const path81 = rest.slice(secondSep + 1);
21177
- if (!serviceName || !method || !path81) return null;
21178
- return { serviceName, method, path: path81 };
21388
+ const path82 = rest.slice(secondSep + 1);
21389
+ if (!serviceName || !method || !path82) return null;
21390
+ return { serviceName, method, path: path82 };
21179
21391
  }
21180
21392
 
21181
21393
  // src/connectors/cloud-run/map.ts
@@ -21204,14 +21416,14 @@ function mapLogEntryToSignal2(entry2) {
21204
21416
  if (!req2) return null;
21205
21417
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
21206
21418
  const method = req2.requestMethod.toUpperCase();
21207
- const path81 = pathFromRequestUrl2(req2.requestUrl);
21208
- if (path81 === null) return null;
21419
+ const path82 = pathFromRequestUrl2(req2.requestUrl);
21420
+ if (path82 === null) return null;
21209
21421
  const timestamp = entry2.timestamp;
21210
21422
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
21211
21423
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
21212
21424
  return {
21213
21425
  targetKind: CLOUD_RUN_TARGET_KIND,
21214
- targetName: packCloudRunTargetName({ serviceName, method, path: path81 }),
21426
+ targetName: packCloudRunTargetName({ serviceName, method, path: path82 }),
21215
21427
  callCount: 1,
21216
21428
  errorCount: isError ? 1 : 0,
21217
21429
  lastObservedIso: timestamp
@@ -21250,14 +21462,14 @@ function createCloudRunResolveTarget(graph, config) {
21250
21462
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
21251
21463
  const identity = parseCloudRunTargetName(signal.targetName);
21252
21464
  if (!identity) return null;
21253
- const { serviceName: gcpServiceName, method, path: path81 } = identity;
21254
- const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path81) ?? void 0 : void 0);
21465
+ const { serviceName: gcpServiceName, method, path: path82 } = identity;
21466
+ const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path82) ?? void 0 : void 0);
21255
21467
  if (mappedService) {
21256
21468
  const routeNodeId = findMatchingRouteNode2(
21257
21469
  graph,
21258
21470
  mappedService,
21259
21471
  method,
21260
- normalizePathTemplate(path81)
21472
+ normalizePathTemplate(path82)
21261
21473
  );
21262
21474
  if (routeNodeId) {
21263
21475
  return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types82.EdgeType.CALLS };
@@ -21393,9 +21605,9 @@ function parseGcpLbTargetName(targetName) {
21393
21605
  const secondSep = rest.indexOf(FIELD_SEP3);
21394
21606
  if (secondSep === -1) return null;
21395
21607
  const method = rest.slice(0, secondSep);
21396
- const path81 = rest.slice(secondSep + 1);
21397
- if (!backendServiceName || !method || !path81) return null;
21398
- return { backendServiceName, method, path: path81 };
21608
+ const path82 = rest.slice(secondSep + 1);
21609
+ if (!backendServiceName || !method || !path82) return null;
21610
+ return { backendServiceName, method, path: path82 };
21399
21611
  }
21400
21612
 
21401
21613
  // src/connectors/gcp-lb/map.ts
@@ -21424,14 +21636,14 @@ function mapLogEntryToSignal3(entry2) {
21424
21636
  if (!req2) return null;
21425
21637
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
21426
21638
  const method = req2.requestMethod.toUpperCase();
21427
- const path81 = pathFromRequestUrl3(req2.requestUrl);
21428
- if (path81 === null) return null;
21639
+ const path82 = pathFromRequestUrl3(req2.requestUrl);
21640
+ if (path82 === null) return null;
21429
21641
  const timestamp = entry2.timestamp;
21430
21642
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
21431
21643
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD5;
21432
21644
  return {
21433
21645
  targetKind: GCP_LB_TARGET_KIND,
21434
- targetName: packGcpLbTargetName({ backendServiceName, method, path: path81 }),
21646
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path82 }),
21435
21647
  callCount: 1,
21436
21648
  errorCount: isError ? 1 : 0,
21437
21649
  lastObservedIso: timestamp
@@ -21470,14 +21682,14 @@ function createGcpLbResolveTarget(graph, config) {
21470
21682
  if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
21471
21683
  const identity = parseGcpLbTargetName(signal.targetName);
21472
21684
  if (!identity) return null;
21473
- const { backendServiceName, method, path: path81 } = identity;
21474
- const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path81) ?? void 0 : void 0);
21685
+ const { backendServiceName, method, path: path82 } = identity;
21686
+ const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path82) ?? void 0 : void 0);
21475
21687
  if (mappedService) {
21476
21688
  const routeNodeId = findMatchingRouteNode3(
21477
21689
  graph,
21478
21690
  mappedService,
21479
21691
  method,
21480
- normalizePathTemplate(path81)
21692
+ normalizePathTemplate(path82)
21481
21693
  );
21482
21694
  if (routeNodeId) {
21483
21695
  return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types86.EdgeType.CALLS };
@@ -22795,16 +23007,18 @@ function serializeGraph(graph) {
22795
23007
  });
22796
23008
  return { nodes, edges };
22797
23009
  }
22798
- function projectFromReq(req2, singleProject) {
23010
+ function projectFromReq(req2, singleProject, registry) {
22799
23011
  const params = req2.params;
22800
23012
  const named2 = params.project;
22801
23013
  if (singleProject) {
22802
23014
  return named2 === void 0 || named2 === DEFAULT_PROJECT ? singleProject : named2;
22803
23015
  }
22804
- return named2 ?? DEFAULT_PROJECT;
23016
+ if (named2 !== void 0) return named2;
23017
+ const hosted = registry?.list() ?? [];
23018
+ return hosted.length === 1 ? hosted[0] : DEFAULT_PROJECT;
22805
23019
  }
22806
23020
  function resolveProject(registry, req2, reply, bootstrap, singleProject) {
22807
- const name = projectFromReq(req2, singleProject);
23021
+ const name = projectFromReq(req2, singleProject, registry);
22808
23022
  const ctx = registry.get(name);
22809
23023
  if (!ctx) {
22810
23024
  const phase = bootstrap?.status(name);
@@ -22816,10 +23030,13 @@ function resolveProject(registry, req2, reply, bootstrap, singleProject) {
22816
23030
  void reply.code(503).send({ ready: false, project: name, status: "broken" });
22817
23031
  return null;
22818
23032
  }
23033
+ const unnamed = req2.params.project === void 0;
23034
+ const hosted = registry.list();
23035
+ 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).";
22819
23036
  void reply.code(404).send({
22820
23037
  error: "project not found",
22821
23038
  project: name,
22822
- hint: "This daemon does not host that project. GET /projects lists what it serves (see hostedHere)."
23039
+ hint
22823
23040
  });
22824
23041
  return null;
22825
23042
  }
@@ -23732,7 +23949,7 @@ async function reconcileFrontierSurfaces(graph, errorsPath) {
23732
23949
  // src/connectors/hosted.ts
23733
23950
  init_cjs_shims();
23734
23951
  var import_promises = require("fs/promises");
23735
- var import_node_path75 = require("path");
23952
+ var import_node_path76 = require("path");
23736
23953
 
23737
23954
  // src/connectors/railway/target-ref.ts
23738
23955
  init_cjs_shims();
@@ -23767,13 +23984,13 @@ function decodeRailwayTargetRef(ref) {
23767
23984
  var CREDENTIAL_REFRESH_SKEW_MS = 6e4;
23768
23985
  var CP_REQUEST_TIMEOUT_MS = 1e4;
23769
23986
  var DEFAULT_RELIST_INTERVAL_MS = 6e4;
23770
- async function cpGet(path81, deps) {
23987
+ async function cpGet(path82, deps) {
23771
23988
  const f = deps.fetchImpl ?? fetch;
23772
- const res = await f(`${deps.cpUrl.replace(/\/+$/, "")}${path81}`, {
23989
+ const res = await f(`${deps.cpUrl.replace(/\/+$/, "")}${path82}`, {
23773
23990
  headers: { authorization: `Bearer ${deps.daemonToken}`, accept: "application/json" },
23774
23991
  signal: AbortSignal.timeout(CP_REQUEST_TIMEOUT_MS)
23775
23992
  });
23776
- if (!res.ok) throw new Error(`control plane ${path81} \u2192 HTTP ${res.status}`);
23993
+ if (!res.ok) throw new Error(`control plane ${path82} \u2192 HTTP ${res.status}`);
23777
23994
  return await res.json();
23778
23995
  }
23779
23996
  function credentialRecord(provider, cred) {
@@ -23834,7 +24051,7 @@ function parseFirebaseServiceMap(raw) {
23834
24051
  }
23835
24052
  }
23836
24053
  function pushHandlesPath(projectDir) {
23837
- return (0, import_node_path75.join)(projectDir, "neat-out", "connectors-hosted.json");
24054
+ return (0, import_node_path76.join)(projectDir, "neat-out", "connectors-hosted.json");
23838
24055
  }
23839
24056
  async function readPushHandles(projectDir) {
23840
24057
  try {
@@ -23845,14 +24062,14 @@ async function readPushHandles(projectDir) {
23845
24062
  }
23846
24063
  }
23847
24064
  async function writePushHandle(projectDir, provider, handle) {
23848
- const path81 = pushHandlesPath(projectDir);
24065
+ const path82 = pushHandlesPath(projectDir);
23849
24066
  const handles = await readPushHandles(projectDir);
23850
24067
  handles[provider] = handle;
23851
- await (0, import_promises.mkdir)((0, import_node_path75.dirname)(path81), { recursive: true });
23852
- const tmp = `${path81}.${process.pid}.tmp`;
24068
+ await (0, import_promises.mkdir)((0, import_node_path76.dirname)(path82), { recursive: true });
24069
+ const tmp = `${path82}.${process.pid}.tmp`;
23853
24070
  await (0, import_promises.writeFile)(tmp, `${JSON.stringify(handles, null, 2)}
23854
24071
  `, "utf8");
23855
- await (0, import_promises.rename)(tmp, path81);
24072
+ await (0, import_promises.rename)(tmp, path82);
23856
24073
  }
23857
24074
  function pushCredentialRecord(provider, cred, deps) {
23858
24075
  switch (provider) {
@@ -24073,7 +24290,7 @@ async function maybeStartHostedConnectors(input) {
24073
24290
  // src/connectors/hosted-repos.ts
24074
24291
  init_cjs_shims();
24075
24292
  var import_node_os5 = __toESM(require("os"), 1);
24076
- var import_node_path76 = __toESM(require("path"), 1);
24293
+ var import_node_path77 = __toESM(require("path"), 1);
24077
24294
  var import_promises2 = require("fs/promises");
24078
24295
  var CP_REQUEST_TIMEOUT_MS2 = 1e4;
24079
24296
  var DEFAULT_SYNC_INTERVAL_MS = 5 * 6e4;
@@ -24102,7 +24319,7 @@ async function cpPostStatus(deps, owner, name, body) {
24102
24319
  if (!res.ok && res.status !== 404) throw new Error(`control plane status POST \u2192 HTTP ${res.status}`);
24103
24320
  }
24104
24321
  var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
24105
- const [{ default: git }, httpMod, fs41] = await Promise.all([
24322
+ const [{ default: git }, httpMod, fs42] = await Promise.all([
24106
24323
  import("isomorphic-git"),
24107
24324
  import("isomorphic-git/http/node"),
24108
24325
  import("fs")
@@ -24113,7 +24330,7 @@ var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
24113
24330
  const username = parsed.password ? parsed.username : "x-access-token";
24114
24331
  const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
24115
24332
  const clone = git.clone({
24116
- fs: fs41,
24333
+ fs: fs42,
24117
24334
  http,
24118
24335
  dir: destDir,
24119
24336
  url: cleanUrl,
@@ -24144,7 +24361,7 @@ async function syncOneRepo(r, input) {
24144
24361
  const tmpRoot = input.tmpRoot ?? import_node_os5.default.tmpdir();
24145
24362
  let dir;
24146
24363
  try {
24147
- dir = await (0, import_promises2.mkdtemp)(import_node_path76.default.join(tmpRoot, "neat-repo-"));
24364
+ dir = await (0, import_promises2.mkdtemp)(import_node_path77.default.join(tmpRoot, "neat-repo-"));
24148
24365
  await cpPostStatus(deps, r.owner, r.name, { syncStatus: "syncing", detail: "cloning" }).catch(() => {
24149
24366
  });
24150
24367
  await cloneRepo(r.cloneUrl, r.defaultBranch, dir);
@@ -24281,9 +24498,9 @@ function makeK8sFetchImpl(transport) {
24281
24498
  req2.end();
24282
24499
  }));
24283
24500
  }
24284
- async function listResource(transport, namespace, path81, opts = {}) {
24501
+ async function listResource(transport, namespace, path82, opts = {}) {
24285
24502
  const base = opts.apiUrl ?? transport.server;
24286
- const url = `${base.replace(/\/$/, "")}${path81}`;
24503
+ const url = `${base.replace(/\/$/, "")}${path82}`;
24287
24504
  const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
24288
24505
  const res = await junctionFetch(
24289
24506
  url,
@@ -24293,7 +24510,7 @@ async function listResource(transport, namespace, path81, opts = {}) {
24293
24510
  { provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
24294
24511
  );
24295
24512
  if (!res.ok) {
24296
- throw new Error(`kubernetes ${path81} failed: ${res.status} ${res.statusText}`);
24513
+ throw new Error(`kubernetes ${path82} failed: ${res.status} ${res.statusText}`);
24297
24514
  }
24298
24515
  const json = await res.json();
24299
24516
  return Array.isArray(json.items) ? json.items : [];
@@ -24314,14 +24531,14 @@ async function fetchPods(transport, namespace, opts = {}) {
24314
24531
 
24315
24532
  // src/connectors/kubernetes/kubeconfig.ts
24316
24533
  init_cjs_shims();
24317
- var import_node_fs39 = require("fs");
24534
+ var import_node_fs40 = require("fs");
24318
24535
  var import_yaml4 = require("yaml");
24319
24536
  function pemFrom(dataField, pathField) {
24320
24537
  if (typeof dataField === "string" && dataField.length > 0) {
24321
24538
  return Buffer.from(dataField, "base64").toString("utf8");
24322
24539
  }
24323
24540
  if (typeof pathField === "string" && pathField.length > 0) {
24324
- return (0, import_node_fs39.readFileSync)(pathField, "utf8");
24541
+ return (0, import_node_fs40.readFileSync)(pathField, "utf8");
24325
24542
  }
24326
24543
  return void 0;
24327
24544
  }
@@ -24332,7 +24549,7 @@ function named(list, name) {
24332
24549
  }
24333
24550
  function parseKubeconfig(kubeconfig) {
24334
24551
  const looksInline = /\n/.test(kubeconfig) || /(^|\s)clusters\s*:/.test(kubeconfig);
24335
- const text = looksInline ? kubeconfig : (0, import_node_fs39.readFileSync)(kubeconfig, "utf8");
24552
+ const text = looksInline ? kubeconfig : (0, import_node_fs40.readFileSync)(kubeconfig, "utf8");
24336
24553
  let doc;
24337
24554
  try {
24338
24555
  doc = (0, import_yaml4.parse)(text);
@@ -24597,14 +24814,14 @@ function createK8sResolveTarget(graph) {
24597
24814
  init_cjs_shims();
24598
24815
  var import_promises3 = require("fs/promises");
24599
24816
  var import_node_os6 = __toESM(require("os"), 1);
24600
- var import_node_path77 = __toESM(require("path"), 1);
24817
+ var import_node_path78 = __toESM(require("path"), 1);
24601
24818
  function defaultHome() {
24602
24819
  const override = process.env.NEAT_HOME;
24603
- if (override && override.length > 0) return import_node_path77.default.resolve(override);
24604
- return import_node_path77.default.join(import_node_os6.default.homedir(), ".neat");
24820
+ if (override && override.length > 0) return import_node_path78.default.resolve(override);
24821
+ return import_node_path78.default.join(import_node_os6.default.homedir(), ".neat");
24605
24822
  }
24606
24823
  function k8sSubstrateConfigPath(home = defaultHome()) {
24607
- return import_node_path77.default.join(home, "k8s.json");
24824
+ return import_node_path78.default.join(home, "k8s.json");
24608
24825
  }
24609
24826
  async function readK8sSubstrateConfig(home = defaultHome()) {
24610
24827
  let raw;
@@ -24703,8 +24920,8 @@ init_auth();
24703
24920
 
24704
24921
  // src/unrouted.ts
24705
24922
  init_cjs_shims();
24706
- var import_node_fs40 = require("fs");
24707
- var import_node_path78 = __toESM(require("path"), 1);
24923
+ var import_node_fs41 = require("fs");
24924
+ var import_node_path79 = __toESM(require("path"), 1);
24708
24925
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
24709
24926
  return {
24710
24927
  timestamp: now.toISOString(),
@@ -24714,34 +24931,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
24714
24931
  };
24715
24932
  }
24716
24933
  async function appendUnroutedSpan(neatHome4, record) {
24717
- const target = import_node_path78.default.join(neatHome4, "errors.ndjson");
24718
- await import_node_fs40.promises.mkdir(neatHome4, { recursive: true });
24719
- await import_node_fs40.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
24934
+ const target = import_node_path79.default.join(neatHome4, "errors.ndjson");
24935
+ await import_node_fs41.promises.mkdir(neatHome4, { recursive: true });
24936
+ await import_node_fs41.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
24720
24937
  }
24721
24938
  function unroutedErrorsPath(neatHome4) {
24722
- return import_node_path78.default.join(neatHome4, "errors.ndjson");
24939
+ return import_node_path79.default.join(neatHome4, "errors.ndjson");
24723
24940
  }
24724
24941
 
24725
24942
  // src/daemon.ts
24726
24943
  var import_types107 = require("@neat.is/types");
24727
24944
  function daemonJsonPath(scanPath) {
24728
- return import_node_path79.default.join(scanPath, "neat-out", "daemon.json");
24945
+ return import_node_path80.default.join(scanPath, "neat-out", "daemon.json");
24729
24946
  }
24730
24947
  function daemonsDiscoveryDir(home) {
24731
24948
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
24732
- return import_node_path79.default.join(base, "daemons");
24949
+ return import_node_path80.default.join(base, "daemons");
24733
24950
  }
24734
24951
  function daemonDiscoveryPath(project, home) {
24735
- return import_node_path79.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
24952
+ return import_node_path80.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
24736
24953
  }
24737
24954
  function sanitizeDiscoveryName(project) {
24738
24955
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
24739
24956
  }
24740
24957
  function neatHomeFromEnv() {
24741
24958
  const env = process.env.NEAT_HOME;
24742
- if (env && env.length > 0) return import_node_path79.default.resolve(env);
24959
+ if (env && env.length > 0) return import_node_path80.default.resolve(env);
24743
24960
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
24744
- return import_node_path79.default.join(home, ".neat");
24961
+ return import_node_path80.default.join(home, ".neat");
24745
24962
  }
24746
24963
  function resolveNeatVersion() {
24747
24964
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -24773,7 +24990,7 @@ async function clearDaemonRecord(record, home) {
24773
24990
  } catch {
24774
24991
  }
24775
24992
  try {
24776
- await import_node_fs41.promises.unlink(daemonDiscoveryPath(record.project, home));
24993
+ await import_node_fs42.promises.unlink(daemonDiscoveryPath(record.project, home));
24777
24994
  } catch {
24778
24995
  }
24779
24996
  }
@@ -24782,12 +24999,12 @@ function reconcileDaemonRecordSync(record, home) {
24782
24999
  const stopped = { ...record, status: "stopped" };
24783
25000
  const target = daemonJsonPath(record.projectPath);
24784
25001
  const tmp = `${target}.${process.pid}.tmp`;
24785
- (0, import_node_fs41.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
24786
- (0, import_node_fs41.renameSync)(tmp, target);
25002
+ (0, import_node_fs42.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
25003
+ (0, import_node_fs42.renameSync)(tmp, target);
24787
25004
  } catch {
24788
25005
  }
24789
25006
  try {
24790
- (0, import_node_fs41.unlinkSync)(daemonDiscoveryPath(record.project, home));
25007
+ (0, import_node_fs42.unlinkSync)(daemonDiscoveryPath(record.project, home));
24791
25008
  } catch {
24792
25009
  }
24793
25010
  }
@@ -24822,11 +25039,11 @@ function teardownSlot(slot) {
24822
25039
  }
24823
25040
  }
24824
25041
  function neatHomeFor(opts) {
24825
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path79.default.resolve(opts.neatHome);
25042
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path80.default.resolve(opts.neatHome);
24826
25043
  const env = process.env.NEAT_HOME;
24827
- if (env && env.length > 0) return import_node_path79.default.resolve(env);
25044
+ if (env && env.length > 0) return import_node_path80.default.resolve(env);
24828
25045
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
24829
- return import_node_path79.default.join(home, ".neat");
25046
+ return import_node_path80.default.join(home, ".neat");
24830
25047
  }
24831
25048
  function routeSpanToProject(serviceName, projects) {
24832
25049
  if (!serviceName) return DEFAULT_PROJECT;
@@ -24878,9 +25095,9 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
24878
25095
  );
24879
25096
  }
24880
25097
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
24881
- const paths = pathsForProject(entry2.name, import_node_path79.default.join(entry2.path, "neat-out"));
25098
+ const paths = pathsForProject(entry2.name, import_node_path80.default.join(entry2.path, "neat-out"));
24882
25099
  try {
24883
- const stat = await import_node_fs41.promises.stat(entry2.path);
25100
+ const stat = await import_node_fs42.promises.stat(entry2.path);
24884
25101
  if (!stat.isDirectory()) {
24885
25102
  throw new Error(`registered path ${entry2.path} is not a directory`);
24886
25103
  }
@@ -25032,7 +25249,7 @@ async function startDaemon(opts = {}) {
25032
25249
  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;
25033
25250
  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;
25034
25251
  const singleProject = projectArg;
25035
- const singleProjectPath = singleProject && projectPathArg ? import_node_path79.default.resolve(projectPathArg) : null;
25252
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path80.default.resolve(projectPathArg) : null;
25036
25253
  if (singleProject && !singleProjectPath) {
25037
25254
  throw new Error(
25038
25255
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -25040,14 +25257,14 @@ async function startDaemon(opts = {}) {
25040
25257
  }
25041
25258
  if (!singleProject) {
25042
25259
  try {
25043
- await import_node_fs41.promises.access(regPath);
25260
+ await import_node_fs42.promises.access(regPath);
25044
25261
  } catch {
25045
25262
  throw new Error(
25046
25263
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
25047
25264
  );
25048
25265
  }
25049
25266
  }
25050
- const pidPath = import_node_path79.default.join(home, "neatd.pid");
25267
+ const pidPath = import_node_path80.default.join(home, "neatd.pid");
25051
25268
  await writeAtomically(pidPath, `${process.pid}
25052
25269
  `);
25053
25270
  const slots = /* @__PURE__ */ new Map();
@@ -25259,7 +25476,7 @@ async function startDaemon(opts = {}) {
25259
25476
  }
25260
25477
  if (restApp) await restApp.close().catch(() => {
25261
25478
  });
25262
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25479
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25263
25480
  });
25264
25481
  throw new Error(
25265
25482
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -25437,7 +25654,7 @@ async function startDaemon(opts = {}) {
25437
25654
  });
25438
25655
  if (otlpApp) await otlpApp.close().catch(() => {
25439
25656
  });
25440
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25657
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25441
25658
  });
25442
25659
  throw new Error(
25443
25660
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -25472,7 +25689,7 @@ async function startDaemon(opts = {}) {
25472
25689
  });
25473
25690
  if (otlpApp) await otlpApp.close().catch(() => {
25474
25691
  });
25475
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25692
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25476
25693
  });
25477
25694
  throw new Error(
25478
25695
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -25519,9 +25736,9 @@ async function startDaemon(opts = {}) {
25519
25736
  let registryWatcher = null;
25520
25737
  let reloadTimer = null;
25521
25738
  if (!singleProject) try {
25522
- const regDir = import_node_path79.default.dirname(regPath);
25523
- const regBase = import_node_path79.default.basename(regPath);
25524
- registryWatcher = (0, import_node_fs41.watch)(regDir, (_eventType, filename) => {
25739
+ const regDir = import_node_path80.default.dirname(regPath);
25740
+ const regBase = import_node_path80.default.basename(regPath);
25741
+ registryWatcher = (0, import_node_fs42.watch)(regDir, (_eventType, filename) => {
25525
25742
  if (filename !== null && filename !== regBase) return;
25526
25743
  if (reloadTimer) clearTimeout(reloadTimer);
25527
25744
  reloadTimer = setTimeout(() => {
@@ -25570,7 +25787,7 @@ async function startDaemon(opts = {}) {
25570
25787
  if (daemonRecord) {
25571
25788
  await clearDaemonRecord(daemonRecord, home);
25572
25789
  }
25573
- await import_node_fs41.promises.unlink(pidPath).catch(() => {
25790
+ await import_node_fs42.promises.unlink(pidPath).catch(() => {
25574
25791
  });
25575
25792
  };
25576
25793
  return {
@@ -25593,9 +25810,9 @@ init_auth();
25593
25810
  // src/web-spawn.ts
25594
25811
  init_cjs_shims();
25595
25812
  var import_node_child_process2 = require("child_process");
25596
- var import_node_fs42 = require("fs");
25813
+ var import_node_fs43 = require("fs");
25597
25814
  var import_node_net = __toESM(require("net"), 1);
25598
- var import_node_path80 = __toESM(require("path"), 1);
25815
+ var import_node_path81 = __toESM(require("path"), 1);
25599
25816
  var DEFAULT_WEB_PORT = 6328;
25600
25817
  var DEFAULT_REST_PORT = 8080;
25601
25818
  function asValidPort(value) {
@@ -25604,11 +25821,11 @@ function asValidPort(value) {
25604
25821
  }
25605
25822
  function projectRoot() {
25606
25823
  const fromEnv = process.env.NEAT_SCAN_PATH;
25607
- return import_node_path80.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
25824
+ return import_node_path81.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
25608
25825
  }
25609
25826
  async function readDaemonPorts(root) {
25610
25827
  try {
25611
- const raw = await import_node_fs42.promises.readFile(import_node_path80.default.join(root, "neat-out", "daemon.json"), "utf8");
25828
+ const raw = await import_node_fs43.promises.readFile(import_node_path81.default.join(root, "neat-out", "daemon.json"), "utf8");
25612
25829
  const parsed = JSON.parse(raw);
25613
25830
  const ports = parsed?.ports ?? {};
25614
25831
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -25653,10 +25870,10 @@ function resolveWebPackageDir() {
25653
25870
  eval("require")
25654
25871
  );
25655
25872
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
25656
- return import_node_path80.default.dirname(pkgJsonPath);
25873
+ return import_node_path81.default.dirname(pkgJsonPath);
25657
25874
  }
25658
25875
  function resolveStandaloneServerEntry(webDir) {
25659
- return import_node_path80.default.join(webDir, ".next/standalone/packages/web/server.js");
25876
+ return import_node_path81.default.join(webDir, ".next/standalone/packages/web/server.js");
25660
25877
  }
25661
25878
  async function pickInternalPort() {
25662
25879
  return new Promise((resolve, reject) => {
@@ -25709,7 +25926,7 @@ async function spawnWebUI(restPort, opts = {}) {
25709
25926
  NEAT_API_URL: apiUrl
25710
25927
  };
25711
25928
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
25712
- cwd: import_node_path80.default.dirname(serverEntry),
25929
+ cwd: import_node_path81.default.dirname(serverEntry),
25713
25930
  env,
25714
25931
  stdio: ["ignore", "inherit", "inherit"],
25715
25932
  detached: false
@@ -25885,14 +26102,14 @@ function localVersion() {
25885
26102
  }
25886
26103
  function neatHome3() {
25887
26104
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
25888
- return import_node_path81.default.resolve(process.env.NEAT_HOME);
26105
+ return import_node_path82.default.resolve(process.env.NEAT_HOME);
25889
26106
  }
25890
26107
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
25891
- return import_node_path81.default.join(home, ".neat");
26108
+ return import_node_path82.default.join(home, ".neat");
25892
26109
  }
25893
26110
  async function readPid() {
25894
26111
  try {
25895
- const raw = await import_node_fs43.promises.readFile(import_node_path81.default.join(neatHome3(), "neatd.pid"), "utf8");
26112
+ const raw = await import_node_fs44.promises.readFile(import_node_path82.default.join(neatHome3(), "neatd.pid"), "utf8");
25896
26113
  const n = Number.parseInt(raw.trim(), 10);
25897
26114
  return Number.isFinite(n) ? n : null;
25898
26115
  } catch {