@neat.is/core 0.4.12 → 0.4.14

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/server.cjs CHANGED
@@ -56,9 +56,9 @@ function mountBearerAuth(app, opts) {
56
56
  const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
57
57
  const publicRead = opts.publicRead === true;
58
58
  app.addHook("preHandler", (req, reply, done) => {
59
- const path40 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
59
+ const path42 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
60
60
  for (const suffix of suffixes) {
61
- if (path40 === suffix || path40.endsWith(suffix)) {
61
+ if (path42 === suffix || path42.endsWith(suffix)) {
62
62
  done();
63
63
  return;
64
64
  }
@@ -132,7 +132,9 @@ __export(otel_grpc_exports, {
132
132
  });
133
133
  function bytesToHex(buf) {
134
134
  if (!buf) return "";
135
- return Buffer.isBuffer(buf) ? buf.toString("hex") : "";
135
+ if (Buffer.isBuffer(buf)) return buf.toString("hex");
136
+ if (buf instanceof Uint8Array) return Buffer.from(buf).toString("hex");
137
+ return "";
136
138
  }
137
139
  function nanosToString(n) {
138
140
  if (n === void 0 || n === null) return "0";
@@ -184,8 +186,8 @@ function reshapeGrpcRequest(req) {
184
186
  };
185
187
  }
186
188
  function resolveProtoRoot() {
187
- const here = import_node_path36.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
188
- return import_node_path36.default.resolve(here, "..", "proto");
189
+ const here = import_node_path38.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
190
+ return import_node_path38.default.resolve(here, "..", "proto");
189
191
  }
190
192
  function loadTraceService() {
191
193
  const protoRoot = resolveProtoRoot();
@@ -253,13 +255,13 @@ async function startOtelGrpcReceiver(opts) {
253
255
  })
254
256
  };
255
257
  }
256
- var import_node_url, import_node_path36, import_node_crypto2, grpc, protoLoader;
258
+ var import_node_url, import_node_path38, import_node_crypto2, grpc, protoLoader;
257
259
  var init_otel_grpc = __esm({
258
260
  "src/otel-grpc.ts"() {
259
261
  "use strict";
260
262
  init_cjs_shims();
261
263
  import_node_url = require("url");
262
- import_node_path36 = __toESM(require("path"), 1);
264
+ import_node_path38 = __toESM(require("path"), 1);
263
265
  import_node_crypto2 = require("crypto");
264
266
  grpc = __toESM(require("@grpc/grpc-js"), 1);
265
267
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -369,10 +371,10 @@ function parseOtlpRequest(body) {
369
371
  return out;
370
372
  }
371
373
  function loadProtoRoot() {
372
- const here = import_node_path37.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
373
- const protoRoot = import_node_path37.default.resolve(here, "..", "proto");
374
+ const here = import_node_path39.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
375
+ const protoRoot = import_node_path39.default.resolve(here, "..", "proto");
374
376
  const root = new import_protobufjs.default.Root();
375
- root.resolvePath = (_origin, target) => import_node_path37.default.resolve(protoRoot, target);
377
+ root.resolvePath = (_origin, target) => import_node_path39.default.resolve(protoRoot, target);
376
378
  root.loadSync(
377
379
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
378
380
  { keepCase: true }
@@ -405,7 +407,10 @@ function encodeProtobufResponseBody() {
405
407
  }
406
408
  async function decodeProtobufBody(buf) {
407
409
  const Type = loadProtobufDecoder();
408
- const decoded = Type.decode(buf).toJSON();
410
+ const decoded = Type.toObject(Type.decode(buf), {
411
+ longs: String,
412
+ enums: Number
413
+ });
409
414
  const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
410
415
  return reshapeGrpcRequest2(decoded);
411
416
  }
@@ -565,12 +570,12 @@ async function buildOtelReceiver(opts) {
565
570
  };
566
571
  return decorated;
567
572
  }
568
- var import_node_path37, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
573
+ var import_node_path39, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
569
574
  var init_otel = __esm({
570
575
  "src/otel.ts"() {
571
576
  "use strict";
572
577
  init_cjs_shims();
573
- import_node_path37 = __toESM(require("path"), 1);
578
+ import_node_path39 = __toESM(require("path"), 1);
574
579
  import_node_url2 = require("url");
575
580
  import_fastify2 = __toESM(require("fastify"), 1);
576
581
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -586,7 +591,7 @@ var init_otel = __esm({
586
591
 
587
592
  // src/server.ts
588
593
  init_cjs_shims();
589
- var import_node_path39 = __toESM(require("path"), 1);
594
+ var import_node_path41 = __toESM(require("path"), 1);
590
595
 
591
596
  // src/graph.ts
592
597
  init_cjs_shims();
@@ -610,7 +615,7 @@ function getGraph(project = DEFAULT_PROJECT) {
610
615
  init_cjs_shims();
611
616
  var import_fastify = __toESM(require("fastify"), 1);
612
617
  var import_cors = __toESM(require("@fastify/cors"), 1);
613
- var import_types23 = require("@neat.is/types");
618
+ var import_types24 = require("@neat.is/types");
614
619
 
615
620
  // src/extend/index.ts
616
621
  init_cjs_shims();
@@ -1353,19 +1358,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1353
1358
  function longestIncomingWalk(graph, start, maxDepth) {
1354
1359
  let best = { path: [start], edges: [] };
1355
1360
  const visited = /* @__PURE__ */ new Set([start]);
1356
- function step(node, path40, edges) {
1357
- if (path40.length > best.path.length) {
1358
- best = { path: [...path40], edges: [...edges] };
1361
+ function step(node, path42, edges) {
1362
+ if (path42.length > best.path.length) {
1363
+ best = { path: [...path42], edges: [...edges] };
1359
1364
  }
1360
- if (path40.length - 1 >= maxDepth) return;
1365
+ if (path42.length - 1 >= maxDepth) return;
1361
1366
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1362
1367
  for (const [srcId, edge] of incoming) {
1363
1368
  if (visited.has(srcId)) continue;
1364
1369
  visited.add(srcId);
1365
- path40.push(srcId);
1370
+ path42.push(srcId);
1366
1371
  edges.push(edge);
1367
- step(srcId, path40, edges);
1368
- path40.pop();
1372
+ step(srcId, path42, edges);
1373
+ path42.pop();
1369
1374
  edges.pop();
1370
1375
  visited.delete(srcId);
1371
1376
  }
@@ -3564,10 +3569,8 @@ async function addServiceAliases(graph, scanPath, services) {
3564
3569
  await collectK8sAliases(graph, scanPath, byName);
3565
3570
  }
3566
3571
 
3567
- // src/extract/databases/index.ts
3572
+ // src/extract/files.ts
3568
3573
  init_cjs_shims();
3569
- var import_node_path20 = __toESM(require("path"), 1);
3570
- var import_types9 = require("@neat.is/types");
3571
3574
 
3572
3575
  // src/extract/calls/shared.ts
3573
3576
  init_cjs_shims();
@@ -3666,11 +3669,333 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
3666
3669
  return { fileNodeId, nodesAdded, edgesAdded };
3667
3670
  }
3668
3671
 
3672
+ // src/extract/files.ts
3673
+ var import_node_path13 = __toESM(require("path"), 1);
3674
+ async function addFiles(graph, services) {
3675
+ let nodesAdded = 0;
3676
+ let edgesAdded = 0;
3677
+ for (const service of services) {
3678
+ const filePaths = await walkSourceFiles(service.dir);
3679
+ for (const filePath of filePaths) {
3680
+ const relPath = toPosix2(import_node_path13.default.relative(service.dir, filePath));
3681
+ const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
3682
+ graph,
3683
+ service.pkg.name,
3684
+ service.node.id,
3685
+ relPath
3686
+ );
3687
+ nodesAdded += n;
3688
+ edgesAdded += e;
3689
+ }
3690
+ }
3691
+ return { nodesAdded, edgesAdded };
3692
+ }
3693
+
3694
+ // src/extract/imports.ts
3695
+ init_cjs_shims();
3696
+ var import_node_path14 = __toESM(require("path"), 1);
3697
+ var import_node_fs13 = require("fs");
3698
+ var import_tree_sitter = __toESM(require("tree-sitter"), 1);
3699
+ var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
3700
+ var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
3701
+ var import_types9 = require("@neat.is/types");
3702
+ var PARSE_CHUNK = 16384;
3703
+ function parseSource(parser, source) {
3704
+ return parser.parse(
3705
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK)
3706
+ );
3707
+ }
3708
+ function makeJsParser() {
3709
+ const p = new import_tree_sitter.default();
3710
+ p.setLanguage(import_tree_sitter_javascript.default);
3711
+ return p;
3712
+ }
3713
+ function makePyParser() {
3714
+ const p = new import_tree_sitter.default();
3715
+ p.setLanguage(import_tree_sitter_python.default);
3716
+ return p;
3717
+ }
3718
+ function stringLiteralText(node) {
3719
+ for (let i = 0; i < node.childCount; i++) {
3720
+ const child = node.child(i);
3721
+ if (child?.type === "string_fragment") return child.text;
3722
+ }
3723
+ const raw = node.text;
3724
+ if (raw.length >= 2) return raw.slice(1, -1);
3725
+ return raw.length === 0 ? null : "";
3726
+ }
3727
+ function clipSnippet(text) {
3728
+ const oneLine = text.split("\n")[0] ?? text;
3729
+ return oneLine.length > 120 ? oneLine.slice(0, 120) : oneLine;
3730
+ }
3731
+ function collectJsImports(node, out) {
3732
+ if (node.type === "import_statement") {
3733
+ const source = node.childForFieldName("source");
3734
+ if (source) {
3735
+ const specifier = stringLiteralText(source);
3736
+ if (specifier) {
3737
+ out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
3738
+ }
3739
+ }
3740
+ return;
3741
+ }
3742
+ if (node.type === "call_expression") {
3743
+ const fn = node.childForFieldName("function");
3744
+ if (fn?.type === "identifier" && fn.text === "require") {
3745
+ const args = node.childForFieldName("arguments");
3746
+ const firstArg = args?.namedChild(0);
3747
+ if (firstArg?.type === "string") {
3748
+ const specifier = stringLiteralText(firstArg);
3749
+ if (specifier) {
3750
+ out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
3751
+ }
3752
+ }
3753
+ }
3754
+ }
3755
+ for (let i = 0; i < node.namedChildCount; i++) {
3756
+ const child = node.namedChild(i);
3757
+ if (child) collectJsImports(child, out);
3758
+ }
3759
+ }
3760
+ function collectPyImports(node, out) {
3761
+ if (node.type === "import_from_statement") {
3762
+ let level = 0;
3763
+ let modulePath = "";
3764
+ let pastFrom = false;
3765
+ for (let i = 0; i < node.childCount; i++) {
3766
+ const child = node.child(i);
3767
+ if (!child) continue;
3768
+ if (!pastFrom) {
3769
+ if (child.type === "from") pastFrom = true;
3770
+ continue;
3771
+ }
3772
+ if (child.type === "import") break;
3773
+ if (child.type === "relative_import") {
3774
+ for (let j = 0; j < child.childCount; j++) {
3775
+ const rc = child.child(j);
3776
+ if (!rc) continue;
3777
+ if (rc.type === "import_prefix") {
3778
+ for (let k = 0; k < rc.childCount; k++) {
3779
+ if (rc.child(k)?.type === ".") level++;
3780
+ }
3781
+ } else if (rc.type === "dotted_name") modulePath = rc.text;
3782
+ }
3783
+ break;
3784
+ }
3785
+ if (child.type === "dotted_name") {
3786
+ modulePath = child.text;
3787
+ break;
3788
+ }
3789
+ }
3790
+ if (level > 0 || modulePath) {
3791
+ out.push({ modulePath, level, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
3792
+ }
3793
+ }
3794
+ for (let i = 0; i < node.namedChildCount; i++) {
3795
+ const child = node.namedChild(i);
3796
+ if (child) collectPyImports(child, out);
3797
+ }
3798
+ }
3799
+ async function fileExists2(p) {
3800
+ try {
3801
+ await import_node_fs13.promises.access(p);
3802
+ return true;
3803
+ } catch {
3804
+ return false;
3805
+ }
3806
+ }
3807
+ function isWithinServiceDir(candidate, serviceDir) {
3808
+ const rel = import_node_path14.default.relative(serviceDir, candidate);
3809
+ return rel !== "" && !rel.startsWith("..") && !import_node_path14.default.isAbsolute(rel);
3810
+ }
3811
+ var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
3812
+ var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
3813
+ async function firstExistingCandidate(base, serviceDir) {
3814
+ for (const ext of JS_EXTENSIONS) {
3815
+ const candidate = base + ext;
3816
+ if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
3817
+ return toPosix2(import_node_path14.default.relative(serviceDir, candidate));
3818
+ }
3819
+ }
3820
+ for (const indexFile of JS_INDEX_FILES) {
3821
+ const candidate = import_node_path14.default.join(base, indexFile);
3822
+ if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
3823
+ return toPosix2(import_node_path14.default.relative(serviceDir, candidate));
3824
+ }
3825
+ }
3826
+ return null;
3827
+ }
3828
+ async function loadTsPathConfig(serviceDir) {
3829
+ const tsconfigPath = import_node_path14.default.join(serviceDir, "tsconfig.json");
3830
+ let raw;
3831
+ try {
3832
+ raw = await import_node_fs13.promises.readFile(tsconfigPath, "utf8");
3833
+ } catch {
3834
+ return null;
3835
+ }
3836
+ try {
3837
+ const parsed = JSON.parse(raw);
3838
+ const paths = parsed.compilerOptions?.paths;
3839
+ if (!paths || Object.keys(paths).length === 0) return null;
3840
+ const baseUrl = parsed.compilerOptions?.baseUrl;
3841
+ return { paths, baseDir: baseUrl ? import_node_path14.default.resolve(serviceDir, baseUrl) : serviceDir };
3842
+ } catch (err) {
3843
+ recordExtractionError("import alias resolution", tsconfigPath, err);
3844
+ return null;
3845
+ }
3846
+ }
3847
+ async function resolveTsAlias(specifier, config, serviceDir) {
3848
+ for (const [pattern, targets] of Object.entries(config.paths)) {
3849
+ let suffix = null;
3850
+ if (pattern === specifier) {
3851
+ suffix = "";
3852
+ } else if (pattern.endsWith("/*")) {
3853
+ const prefix = pattern.slice(0, -1);
3854
+ if (specifier.startsWith(prefix)) suffix = specifier.slice(prefix.length);
3855
+ }
3856
+ if (suffix === null) continue;
3857
+ for (const target of targets) {
3858
+ const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
3859
+ const resolvedBase = import_node_path14.default.resolve(config.baseDir, targetBase, suffix);
3860
+ const hit = await firstExistingCandidate(resolvedBase, serviceDir);
3861
+ if (hit) return hit;
3862
+ if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists2(resolvedBase)) {
3863
+ return toPosix2(import_node_path14.default.relative(serviceDir, resolvedBase));
3864
+ }
3865
+ }
3866
+ }
3867
+ return null;
3868
+ }
3869
+ async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3870
+ if (!specifier) return null;
3871
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
3872
+ const base = import_node_path14.default.resolve(importerDir, specifier);
3873
+ const ext = import_node_path14.default.extname(specifier);
3874
+ if (ext) {
3875
+ if (ext === ".js" || ext === ".jsx") {
3876
+ const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
3877
+ const tsSibling = base.slice(0, -ext.length) + tsExt;
3878
+ if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists2(tsSibling)) {
3879
+ return toPosix2(import_node_path14.default.relative(serviceDir, tsSibling));
3880
+ }
3881
+ }
3882
+ if (isWithinServiceDir(base, serviceDir) && await fileExists2(base)) {
3883
+ return toPosix2(import_node_path14.default.relative(serviceDir, base));
3884
+ }
3885
+ return null;
3886
+ }
3887
+ return firstExistingCandidate(base, serviceDir);
3888
+ }
3889
+ if (tsPaths) return resolveTsAlias(specifier, tsPaths, serviceDir);
3890
+ return null;
3891
+ }
3892
+ async function resolvePyImport(imp, importerPath, serviceDir) {
3893
+ if (!imp.modulePath) return null;
3894
+ const relPath = imp.modulePath.split(".").join("/");
3895
+ let baseDir;
3896
+ if (imp.level > 0) {
3897
+ baseDir = import_node_path14.default.dirname(importerPath);
3898
+ for (let i = 1; i < imp.level; i++) baseDir = import_node_path14.default.dirname(baseDir);
3899
+ } else {
3900
+ baseDir = serviceDir;
3901
+ }
3902
+ const candidates = [import_node_path14.default.join(baseDir, `${relPath}.py`), import_node_path14.default.join(baseDir, relPath, "__init__.py")];
3903
+ for (const candidate of candidates) {
3904
+ if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
3905
+ return toPosix2(import_node_path14.default.relative(serviceDir, candidate));
3906
+ }
3907
+ }
3908
+ return null;
3909
+ }
3910
+ function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
3911
+ const importeeFileId = (0, import_types9.fileId)(serviceName, importeeRelPath);
3912
+ if (!graph.hasNode(importeeFileId)) return 0;
3913
+ const edgeId = (0, import_types9.extractedEdgeId)(importerFileId, importeeFileId, import_types9.EdgeType.IMPORTS);
3914
+ if (graph.hasEdge(edgeId)) return 0;
3915
+ const edge = {
3916
+ id: edgeId,
3917
+ source: importerFileId,
3918
+ target: importeeFileId,
3919
+ type: import_types9.EdgeType.IMPORTS,
3920
+ provenance: import_types9.Provenance.EXTRACTED,
3921
+ confidence: (0, import_types9.confidenceForExtracted)("structural"),
3922
+ evidence: { file: importerRelPath, line, snippet: snippet2 }
3923
+ };
3924
+ graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
3925
+ return 1;
3926
+ }
3927
+ async function addImports(graph, services) {
3928
+ const jsParser = makeJsParser();
3929
+ const pyParser = makePyParser();
3930
+ let edgesAdded = 0;
3931
+ for (const service of services) {
3932
+ const tsPaths = await loadTsPathConfig(service.dir);
3933
+ const files = await loadSourceFiles(service.dir);
3934
+ for (const file of files) {
3935
+ if (isTestPath(file.path)) continue;
3936
+ const relFile = toPosix2(import_node_path14.default.relative(service.dir, file.path));
3937
+ const importerFileId = (0, import_types9.fileId)(service.pkg.name, relFile);
3938
+ const isPython = import_node_path14.default.extname(file.path) === ".py";
3939
+ if (isPython) {
3940
+ let pyImports = [];
3941
+ try {
3942
+ const tree = parseSource(pyParser, file.content);
3943
+ collectPyImports(tree.rootNode, pyImports);
3944
+ } catch (err) {
3945
+ recordExtractionError("import extraction", file.path, err);
3946
+ continue;
3947
+ }
3948
+ for (const imp of pyImports) {
3949
+ const resolved = await resolvePyImport(imp, file.path, service.dir);
3950
+ if (!resolved) continue;
3951
+ edgesAdded += emitImportEdge(
3952
+ graph,
3953
+ service.pkg.name,
3954
+ importerFileId,
3955
+ relFile,
3956
+ resolved,
3957
+ imp.line,
3958
+ imp.snippet
3959
+ );
3960
+ }
3961
+ continue;
3962
+ }
3963
+ let jsImports = [];
3964
+ try {
3965
+ const tree = parseSource(jsParser, file.content);
3966
+ collectJsImports(tree.rootNode, jsImports);
3967
+ } catch (err) {
3968
+ recordExtractionError("import extraction", file.path, err);
3969
+ continue;
3970
+ }
3971
+ for (const imp of jsImports) {
3972
+ const resolved = await resolveJsImport(imp.specifier, import_node_path14.default.dirname(file.path), service.dir, tsPaths);
3973
+ if (!resolved) continue;
3974
+ edgesAdded += emitImportEdge(
3975
+ graph,
3976
+ service.pkg.name,
3977
+ importerFileId,
3978
+ relFile,
3979
+ resolved,
3980
+ imp.line,
3981
+ imp.snippet
3982
+ );
3983
+ }
3984
+ }
3985
+ }
3986
+ return { nodesAdded: 0, edgesAdded };
3987
+ }
3988
+
3989
+ // src/extract/databases/index.ts
3990
+ init_cjs_shims();
3991
+ var import_node_path22 = __toESM(require("path"), 1);
3992
+ var import_types10 = require("@neat.is/types");
3993
+
3669
3994
  // src/extract/databases/db-config-yaml.ts
3670
3995
  init_cjs_shims();
3671
- var import_node_path13 = __toESM(require("path"), 1);
3996
+ var import_node_path15 = __toESM(require("path"), 1);
3672
3997
  async function parse(serviceDir) {
3673
- const yamlPath = import_node_path13.default.join(serviceDir, "db-config.yaml");
3998
+ const yamlPath = import_node_path15.default.join(serviceDir, "db-config.yaml");
3674
3999
  if (!await exists2(yamlPath)) return [];
3675
4000
  const raw = await readYaml(yamlPath);
3676
4001
  return [
@@ -3688,13 +4013,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
3688
4013
 
3689
4014
  // src/extract/databases/dotenv.ts
3690
4015
  init_cjs_shims();
3691
- var import_node_fs14 = require("fs");
3692
- var import_node_path15 = __toESM(require("path"), 1);
4016
+ var import_node_fs15 = require("fs");
4017
+ var import_node_path17 = __toESM(require("path"), 1);
3693
4018
 
3694
4019
  // src/extract/databases/shared.ts
3695
4020
  init_cjs_shims();
3696
- var import_node_fs13 = require("fs");
3697
- var import_node_path14 = __toESM(require("path"), 1);
4021
+ var import_node_fs14 = require("fs");
4022
+ var import_node_path16 = __toESM(require("path"), 1);
3698
4023
  function schemeToEngine(scheme) {
3699
4024
  const s = scheme.toLowerCase().split("+")[0];
3700
4025
  switch (s) {
@@ -3733,14 +4058,14 @@ function parseConnectionString(url) {
3733
4058
  }
3734
4059
  async function readIfExists(filePath) {
3735
4060
  try {
3736
- return await import_node_fs13.promises.readFile(filePath, "utf8");
4061
+ return await import_node_fs14.promises.readFile(filePath, "utf8");
3737
4062
  } catch {
3738
4063
  return null;
3739
4064
  }
3740
4065
  }
3741
4066
  async function findFirst(serviceDir, candidates) {
3742
4067
  for (const rel of candidates) {
3743
- const abs = import_node_path14.default.join(serviceDir, rel);
4068
+ const abs = import_node_path16.default.join(serviceDir, rel);
3744
4069
  const content = await readIfExists(abs);
3745
4070
  if (content !== null) return abs;
3746
4071
  }
@@ -3791,15 +4116,15 @@ function parseDotenvLine(line) {
3791
4116
  return { key, value };
3792
4117
  }
3793
4118
  async function parse2(serviceDir) {
3794
- const entries = await import_node_fs14.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
4119
+ const entries = await import_node_fs15.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
3795
4120
  const configs = [];
3796
4121
  const seen = /* @__PURE__ */ new Set();
3797
4122
  for (const entry of entries) {
3798
4123
  if (!entry.isFile()) continue;
3799
4124
  const match = isConfigFile(entry.name);
3800
4125
  if (!match.match || match.fileType !== "env") continue;
3801
- const filePath = import_node_path15.default.join(serviceDir, entry.name);
3802
- const content = await import_node_fs14.promises.readFile(filePath, "utf8");
4126
+ const filePath = import_node_path17.default.join(serviceDir, entry.name);
4127
+ const content = await import_node_fs15.promises.readFile(filePath, "utf8");
3803
4128
  for (const line of content.split("\n")) {
3804
4129
  const parsed = parseDotenvLine(line);
3805
4130
  if (!parsed) continue;
@@ -3818,9 +4143,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
3818
4143
 
3819
4144
  // src/extract/databases/prisma.ts
3820
4145
  init_cjs_shims();
3821
- var import_node_path16 = __toESM(require("path"), 1);
4146
+ var import_node_path18 = __toESM(require("path"), 1);
3822
4147
  async function parse3(serviceDir) {
3823
- const schemaPath = import_node_path16.default.join(serviceDir, "prisma", "schema.prisma");
4148
+ const schemaPath = import_node_path18.default.join(serviceDir, "prisma", "schema.prisma");
3824
4149
  const content = await readIfExists(schemaPath);
3825
4150
  if (!content) return [];
3826
4151
  const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
@@ -3952,10 +4277,10 @@ var knexParser = { name: "knex", parse: parse5 };
3952
4277
 
3953
4278
  // src/extract/databases/ormconfig.ts
3954
4279
  init_cjs_shims();
3955
- var import_node_path17 = __toESM(require("path"), 1);
4280
+ var import_node_path19 = __toESM(require("path"), 1);
3956
4281
  async function parse6(serviceDir) {
3957
4282
  for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
3958
- const abs = import_node_path17.default.join(serviceDir, candidate);
4283
+ const abs = import_node_path19.default.join(serviceDir, candidate);
3959
4284
  if (!await exists2(abs)) continue;
3960
4285
  const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
3961
4286
  const entries = Array.isArray(raw) ? raw : [raw];
@@ -4015,9 +4340,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
4015
4340
 
4016
4341
  // src/extract/databases/sequelize.ts
4017
4342
  init_cjs_shims();
4018
- var import_node_path18 = __toESM(require("path"), 1);
4343
+ var import_node_path20 = __toESM(require("path"), 1);
4019
4344
  async function parse8(serviceDir) {
4020
- const configPath = import_node_path18.default.join(serviceDir, "config", "config.json");
4345
+ const configPath = import_node_path20.default.join(serviceDir, "config", "config.json");
4021
4346
  if (!await exists2(configPath)) return [];
4022
4347
  const raw = await readJson(configPath);
4023
4348
  const out = [];
@@ -4044,7 +4369,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
4044
4369
 
4045
4370
  // src/extract/databases/docker-compose.ts
4046
4371
  init_cjs_shims();
4047
- var import_node_path19 = __toESM(require("path"), 1);
4372
+ var import_node_path21 = __toESM(require("path"), 1);
4048
4373
  function portFromService(svc) {
4049
4374
  for (const raw of svc.ports ?? []) {
4050
4375
  const str = String(raw);
@@ -4071,7 +4396,7 @@ function databaseFromEnv(svc) {
4071
4396
  }
4072
4397
  async function parse9(serviceDir) {
4073
4398
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
4074
- const abs = import_node_path19.default.join(serviceDir, name);
4399
+ const abs = import_node_path21.default.join(serviceDir, name);
4075
4400
  if (!await exists2(abs)) continue;
4076
4401
  const raw = await readYaml(abs);
4077
4402
  if (!raw?.services) return [];
@@ -4112,8 +4437,8 @@ function compatibleDriversFor(engine) {
4112
4437
  }
4113
4438
  function toDatabaseNode(config) {
4114
4439
  return {
4115
- id: (0, import_types9.databaseId)(config.host),
4116
- type: import_types9.NodeType.DatabaseNode,
4440
+ id: (0, import_types10.databaseId)(config.host),
4441
+ type: import_types10.NodeType.DatabaseNode,
4117
4442
  name: config.database || config.host,
4118
4443
  engine: config.engine,
4119
4444
  engineVersion: config.engineVersion,
@@ -4243,7 +4568,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
4243
4568
  discoveredVia: mergedDiscoveredVia
4244
4569
  });
4245
4570
  }
4246
- const relConfigFile = toPosix2(import_node_path20.default.relative(service.dir, config.sourceFile));
4571
+ const relConfigFile = toPosix2(import_node_path22.default.relative(service.dir, config.sourceFile));
4247
4572
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4248
4573
  graph,
4249
4574
  service.pkg.name,
@@ -4252,14 +4577,14 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
4252
4577
  );
4253
4578
  nodesAdded += fn;
4254
4579
  edgesAdded += fe;
4255
- const evidenceFile = toPosix2(import_node_path20.default.relative(scanPath, config.sourceFile));
4580
+ const evidenceFile = toPosix2(import_node_path22.default.relative(scanPath, config.sourceFile));
4256
4581
  const edge = {
4257
- id: (0, import_types5.extractedEdgeId)(fileNodeId, dbNode.id, import_types9.EdgeType.CONNECTS_TO),
4582
+ id: (0, import_types5.extractedEdgeId)(fileNodeId, dbNode.id, import_types10.EdgeType.CONNECTS_TO),
4258
4583
  source: fileNodeId,
4259
4584
  target: dbNode.id,
4260
- type: import_types9.EdgeType.CONNECTS_TO,
4261
- provenance: import_types9.Provenance.EXTRACTED,
4262
- confidence: (0, import_types9.confidenceForExtracted)("structural"),
4585
+ type: import_types10.EdgeType.CONNECTS_TO,
4586
+ provenance: import_types10.Provenance.EXTRACTED,
4587
+ confidence: (0, import_types10.confidenceForExtracted)("structural"),
4263
4588
  evidence: { file: evidenceFile }
4264
4589
  };
4265
4590
  if (!graph.hasEdge(edge.id)) {
@@ -4286,15 +4611,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
4286
4611
 
4287
4612
  // src/extract/configs.ts
4288
4613
  init_cjs_shims();
4289
- var import_node_fs15 = require("fs");
4290
- var import_node_path21 = __toESM(require("path"), 1);
4291
- var import_types10 = require("@neat.is/types");
4614
+ var import_node_fs16 = require("fs");
4615
+ var import_node_path23 = __toESM(require("path"), 1);
4616
+ var import_types11 = require("@neat.is/types");
4292
4617
  async function walkConfigFiles(dir) {
4293
4618
  const out = [];
4294
4619
  async function walk(current) {
4295
- const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true });
4620
+ const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true });
4296
4621
  for (const entry of entries) {
4297
- const full = import_node_path21.default.join(current, entry.name);
4622
+ const full = import_node_path23.default.join(current, entry.name);
4298
4623
  if (entry.isDirectory()) {
4299
4624
  if (IGNORED_DIRS.has(entry.name)) continue;
4300
4625
  if (await isPythonVenvDir(full)) continue;
@@ -4313,19 +4638,19 @@ async function addConfigNodes(graph, services, scanPath) {
4313
4638
  for (const service of services) {
4314
4639
  const configFiles = await walkConfigFiles(service.dir);
4315
4640
  for (const file of configFiles) {
4316
- const relPath = import_node_path21.default.relative(scanPath, file);
4641
+ const relPath = import_node_path23.default.relative(scanPath, file);
4317
4642
  const node = {
4318
- id: (0, import_types10.configId)(relPath),
4319
- type: import_types10.NodeType.ConfigNode,
4320
- name: import_node_path21.default.basename(file),
4643
+ id: (0, import_types11.configId)(relPath),
4644
+ type: import_types11.NodeType.ConfigNode,
4645
+ name: import_node_path23.default.basename(file),
4321
4646
  path: relPath,
4322
- fileType: isConfigFile(import_node_path21.default.basename(file)).fileType
4647
+ fileType: isConfigFile(import_node_path23.default.basename(file)).fileType
4323
4648
  };
4324
4649
  if (!graph.hasNode(node.id)) {
4325
4650
  graph.addNode(node.id, node);
4326
4651
  nodesAdded++;
4327
4652
  }
4328
- const relToService = toPosix2(import_node_path21.default.relative(service.dir, file));
4653
+ const relToService = toPosix2(import_node_path23.default.relative(service.dir, file));
4329
4654
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4330
4655
  graph,
4331
4656
  service.pkg.name,
@@ -4335,13 +4660,13 @@ async function addConfigNodes(graph, services, scanPath) {
4335
4660
  nodesAdded += fn;
4336
4661
  edgesAdded += fe;
4337
4662
  const edge = {
4338
- id: (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types10.EdgeType.CONFIGURED_BY),
4663
+ id: (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types11.EdgeType.CONFIGURED_BY),
4339
4664
  source: fileNodeId,
4340
4665
  target: node.id,
4341
- type: import_types10.EdgeType.CONFIGURED_BY,
4342
- provenance: import_types10.Provenance.EXTRACTED,
4343
- confidence: (0, import_types10.confidenceForExtracted)("structural"),
4344
- evidence: { file: relPath.split(import_node_path21.default.sep).join("/") }
4666
+ type: import_types11.EdgeType.CONFIGURED_BY,
4667
+ provenance: import_types11.Provenance.EXTRACTED,
4668
+ confidence: (0, import_types11.confidenceForExtracted)("structural"),
4669
+ evidence: { file: relPath.split(import_node_path23.default.sep).join("/") }
4345
4670
  };
4346
4671
  if (!graph.hasEdge(edge.id)) {
4347
4672
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -4354,15 +4679,15 @@ async function addConfigNodes(graph, services, scanPath) {
4354
4679
 
4355
4680
  // src/extract/calls/index.ts
4356
4681
  init_cjs_shims();
4357
- var import_types16 = require("@neat.is/types");
4682
+ var import_types17 = require("@neat.is/types");
4358
4683
 
4359
4684
  // src/extract/calls/http.ts
4360
4685
  init_cjs_shims();
4361
- var import_node_path22 = __toESM(require("path"), 1);
4362
- var import_tree_sitter = __toESM(require("tree-sitter"), 1);
4363
- var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
4364
- var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
4365
- var import_types11 = require("@neat.is/types");
4686
+ var import_node_path24 = __toESM(require("path"), 1);
4687
+ var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
4688
+ var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
4689
+ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
4690
+ var import_types12 = require("@neat.is/types");
4366
4691
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
4367
4692
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
4368
4693
  function isInsideJsxExternalLink(node) {
@@ -4390,14 +4715,14 @@ function collectStringLiterals(node, out) {
4390
4715
  if (child) collectStringLiterals(child, out);
4391
4716
  }
4392
4717
  }
4393
- var PARSE_CHUNK = 16384;
4394
- function parseSource(parser, source) {
4718
+ var PARSE_CHUNK2 = 16384;
4719
+ function parseSource2(parser, source) {
4395
4720
  return parser.parse(
4396
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK)
4721
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK2)
4397
4722
  );
4398
4723
  }
4399
4724
  function callsFromSource(source, parser, knownHosts) {
4400
- const tree = parseSource(parser, source);
4725
+ const tree = parseSource2(parser, source);
4401
4726
  const literals = [];
4402
4727
  collectStringLiterals(tree.rootNode, literals);
4403
4728
  const out = [];
@@ -4411,25 +4736,25 @@ function callsFromSource(source, parser, knownHosts) {
4411
4736
  }
4412
4737
  return out;
4413
4738
  }
4414
- function makeJsParser() {
4415
- const p = new import_tree_sitter.default();
4416
- p.setLanguage(import_tree_sitter_javascript.default);
4739
+ function makeJsParser2() {
4740
+ const p = new import_tree_sitter2.default();
4741
+ p.setLanguage(import_tree_sitter_javascript2.default);
4417
4742
  return p;
4418
4743
  }
4419
- function makePyParser() {
4420
- const p = new import_tree_sitter.default();
4421
- p.setLanguage(import_tree_sitter_python.default);
4744
+ function makePyParser2() {
4745
+ const p = new import_tree_sitter2.default();
4746
+ p.setLanguage(import_tree_sitter_python2.default);
4422
4747
  return p;
4423
4748
  }
4424
4749
  async function addHttpCallEdges(graph, services) {
4425
- const jsParser = makeJsParser();
4426
- const pyParser = makePyParser();
4750
+ const jsParser = makeJsParser2();
4751
+ const pyParser = makePyParser2();
4427
4752
  const knownHosts = /* @__PURE__ */ new Set();
4428
4753
  const hostToNodeId = /* @__PURE__ */ new Map();
4429
4754
  for (const service of services) {
4430
- knownHosts.add(import_node_path22.default.basename(service.dir));
4755
+ knownHosts.add(import_node_path24.default.basename(service.dir));
4431
4756
  knownHosts.add(service.pkg.name);
4432
- hostToNodeId.set(import_node_path22.default.basename(service.dir), service.node.id);
4757
+ hostToNodeId.set(import_node_path24.default.basename(service.dir), service.node.id);
4433
4758
  hostToNodeId.set(service.pkg.name, service.node.id);
4434
4759
  }
4435
4760
  let nodesAdded = 0;
@@ -4439,7 +4764,7 @@ async function addHttpCallEdges(graph, services) {
4439
4764
  const seen = /* @__PURE__ */ new Set();
4440
4765
  for (const file of files) {
4441
4766
  if (isTestPath(file.path)) continue;
4442
- const parser = import_node_path22.default.extname(file.path) === ".py" ? pyParser : jsParser;
4767
+ const parser = import_node_path24.default.extname(file.path) === ".py" ? pyParser : jsParser;
4443
4768
  let sites;
4444
4769
  try {
4445
4770
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -4448,14 +4773,14 @@ async function addHttpCallEdges(graph, services) {
4448
4773
  continue;
4449
4774
  }
4450
4775
  if (sites.length === 0) continue;
4451
- const relFile = toPosix2(import_node_path22.default.relative(service.dir, file.path));
4776
+ const relFile = toPosix2(import_node_path24.default.relative(service.dir, file.path));
4452
4777
  for (const site of sites) {
4453
4778
  const targetId = hostToNodeId.get(site.host);
4454
4779
  if (!targetId || targetId === service.node.id) continue;
4455
4780
  const dedupKey = `${relFile}|${targetId}`;
4456
4781
  if (seen.has(dedupKey)) continue;
4457
4782
  seen.add(dedupKey);
4458
- const confidence = (0, import_types11.confidenceForExtracted)("hostname-shape-match");
4783
+ const confidence = (0, import_types12.confidenceForExtracted)("hostname-shape-match");
4459
4784
  const ev = {
4460
4785
  file: relFile,
4461
4786
  line: site.line,
@@ -4469,25 +4794,25 @@ async function addHttpCallEdges(graph, services) {
4469
4794
  );
4470
4795
  nodesAdded += n;
4471
4796
  edgesAdded += e;
4472
- if (!(0, import_types11.passesExtractedFloor)(confidence)) {
4797
+ if (!(0, import_types12.passesExtractedFloor)(confidence)) {
4473
4798
  noteExtractedDropped({
4474
4799
  source: fileNodeId,
4475
4800
  target: targetId,
4476
- type: import_types11.EdgeType.CALLS,
4801
+ type: import_types12.EdgeType.CALLS,
4477
4802
  confidence,
4478
4803
  confidenceKind: "hostname-shape-match",
4479
4804
  evidence: ev
4480
4805
  });
4481
4806
  continue;
4482
4807
  }
4483
- const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, targetId, import_types11.EdgeType.CALLS);
4808
+ const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, targetId, import_types12.EdgeType.CALLS);
4484
4809
  if (!graph.hasEdge(edgeId)) {
4485
4810
  const edge = {
4486
4811
  id: edgeId,
4487
4812
  source: fileNodeId,
4488
4813
  target: targetId,
4489
- type: import_types11.EdgeType.CALLS,
4490
- provenance: import_types11.Provenance.EXTRACTED,
4814
+ type: import_types12.EdgeType.CALLS,
4815
+ provenance: import_types12.Provenance.EXTRACTED,
4491
4816
  confidence,
4492
4817
  evidence: ev
4493
4818
  };
@@ -4502,8 +4827,8 @@ async function addHttpCallEdges(graph, services) {
4502
4827
 
4503
4828
  // src/extract/calls/kafka.ts
4504
4829
  init_cjs_shims();
4505
- var import_node_path23 = __toESM(require("path"), 1);
4506
- var import_types12 = require("@neat.is/types");
4830
+ var import_node_path25 = __toESM(require("path"), 1);
4831
+ var import_types13 = require("@neat.is/types");
4507
4832
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
4508
4833
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
4509
4834
  function findAll(re, text) {
@@ -4524,7 +4849,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4524
4849
  seen.add(key);
4525
4850
  const line = lineOf(file.content, topic);
4526
4851
  out.push({
4527
- infraId: (0, import_types12.infraId)("kafka-topic", topic),
4852
+ infraId: (0, import_types13.infraId)("kafka-topic", topic),
4528
4853
  name: topic,
4529
4854
  kind: "kafka-topic",
4530
4855
  edgeType,
@@ -4533,7 +4858,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4533
4858
  // tier (ADR-066).
4534
4859
  confidenceKind: "verified-call-site",
4535
4860
  evidence: {
4536
- file: import_node_path23.default.relative(serviceDir, file.path),
4861
+ file: import_node_path25.default.relative(serviceDir, file.path),
4537
4862
  line,
4538
4863
  snippet: snippet(file.content, line)
4539
4864
  }
@@ -4546,8 +4871,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4546
4871
 
4547
4872
  // src/extract/calls/redis.ts
4548
4873
  init_cjs_shims();
4549
- var import_node_path24 = __toESM(require("path"), 1);
4550
- var import_types13 = require("@neat.is/types");
4874
+ var import_node_path26 = __toESM(require("path"), 1);
4875
+ var import_types14 = require("@neat.is/types");
4551
4876
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
4552
4877
  function redisEndpointsFromFile(file, serviceDir) {
4553
4878
  const out = [];
@@ -4560,7 +4885,7 @@ function redisEndpointsFromFile(file, serviceDir) {
4560
4885
  seen.add(host);
4561
4886
  const line = lineOf(file.content, host);
4562
4887
  out.push({
4563
- infraId: (0, import_types13.infraId)("redis", host),
4888
+ infraId: (0, import_types14.infraId)("redis", host),
4564
4889
  name: host,
4565
4890
  kind: "redis",
4566
4891
  edgeType: "CALLS",
@@ -4569,7 +4894,7 @@ function redisEndpointsFromFile(file, serviceDir) {
4569
4894
  // support tier (ADR-066).
4570
4895
  confidenceKind: "url-with-structural-support",
4571
4896
  evidence: {
4572
- file: import_node_path24.default.relative(serviceDir, file.path),
4897
+ file: import_node_path26.default.relative(serviceDir, file.path),
4573
4898
  line,
4574
4899
  snippet: snippet(file.content, line)
4575
4900
  }
@@ -4580,8 +4905,8 @@ function redisEndpointsFromFile(file, serviceDir) {
4580
4905
 
4581
4906
  // src/extract/calls/aws.ts
4582
4907
  init_cjs_shims();
4583
- var import_node_path25 = __toESM(require("path"), 1);
4584
- var import_types14 = require("@neat.is/types");
4908
+ var import_node_path27 = __toESM(require("path"), 1);
4909
+ var import_types15 = require("@neat.is/types");
4585
4910
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
4586
4911
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
4587
4912
  function hasMarker(text, markers) {
@@ -4605,7 +4930,7 @@ function awsEndpointsFromFile(file, serviceDir) {
4605
4930
  seen.add(key);
4606
4931
  const line = lineOf(file.content, name);
4607
4932
  out.push({
4608
- infraId: (0, import_types14.infraId)(kind, name),
4933
+ infraId: (0, import_types15.infraId)(kind, name),
4609
4934
  name,
4610
4935
  kind,
4611
4936
  edgeType: "CALLS",
@@ -4614,7 +4939,7 @@ function awsEndpointsFromFile(file, serviceDir) {
4614
4939
  // (ADR-066).
4615
4940
  confidenceKind: "verified-call-site",
4616
4941
  evidence: {
4617
- file: import_node_path25.default.relative(serviceDir, file.path),
4942
+ file: import_node_path27.default.relative(serviceDir, file.path),
4618
4943
  line,
4619
4944
  snippet: snippet(file.content, line)
4620
4945
  }
@@ -4639,8 +4964,8 @@ function awsEndpointsFromFile(file, serviceDir) {
4639
4964
 
4640
4965
  // src/extract/calls/grpc.ts
4641
4966
  init_cjs_shims();
4642
- var import_node_path26 = __toESM(require("path"), 1);
4643
- var import_types15 = require("@neat.is/types");
4967
+ var import_node_path28 = __toESM(require("path"), 1);
4968
+ var import_types16 = require("@neat.is/types");
4644
4969
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
4645
4970
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
4646
4971
  var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
@@ -4689,7 +5014,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
4689
5014
  const { kind } = classified;
4690
5015
  const line = lineOf(file.content, m[0]);
4691
5016
  out.push({
4692
- infraId: (0, import_types15.infraId)(kind, name),
5017
+ infraId: (0, import_types16.infraId)(kind, name),
4693
5018
  name,
4694
5019
  kind,
4695
5020
  edgeType: "CALLS",
@@ -4698,7 +5023,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
4698
5023
  // tier (ADR-066).
4699
5024
  confidenceKind: "verified-call-site",
4700
5025
  evidence: {
4701
- file: import_node_path26.default.relative(serviceDir, file.path),
5026
+ file: import_node_path28.default.relative(serviceDir, file.path),
4702
5027
  line,
4703
5028
  snippet: snippet(file.content, line)
4704
5029
  }
@@ -4711,11 +5036,11 @@ function grpcEndpointsFromFile(file, serviceDir) {
4711
5036
  function edgeTypeFromEndpoint(ep) {
4712
5037
  switch (ep.edgeType) {
4713
5038
  case "PUBLISHES_TO":
4714
- return import_types16.EdgeType.PUBLISHES_TO;
5039
+ return import_types17.EdgeType.PUBLISHES_TO;
4715
5040
  case "CONSUMES_FROM":
4716
- return import_types16.EdgeType.CONSUMES_FROM;
5041
+ return import_types17.EdgeType.CONSUMES_FROM;
4717
5042
  default:
4718
- return import_types16.EdgeType.CALLS;
5043
+ return import_types17.EdgeType.CALLS;
4719
5044
  }
4720
5045
  }
4721
5046
  function isAwsKind(kind) {
@@ -4742,7 +5067,7 @@ async function addExternalEndpointEdges(graph, services) {
4742
5067
  if (!graph.hasNode(ep.infraId)) {
4743
5068
  const node = {
4744
5069
  id: ep.infraId,
4745
- type: import_types16.NodeType.InfraNode,
5070
+ type: import_types17.NodeType.InfraNode,
4746
5071
  name: ep.name,
4747
5072
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
4748
5073
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -4754,7 +5079,7 @@ async function addExternalEndpointEdges(graph, services) {
4754
5079
  nodesAdded++;
4755
5080
  }
4756
5081
  const edgeType = edgeTypeFromEndpoint(ep);
4757
- const confidence = (0, import_types16.confidenceForExtracted)(ep.confidenceKind);
5082
+ const confidence = (0, import_types17.confidenceForExtracted)(ep.confidenceKind);
4758
5083
  const relFile = toPosix2(ep.evidence.file);
4759
5084
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
4760
5085
  graph,
@@ -4764,7 +5089,7 @@ async function addExternalEndpointEdges(graph, services) {
4764
5089
  );
4765
5090
  nodesAdded += n;
4766
5091
  edgesAdded += e;
4767
- if (!(0, import_types16.passesExtractedFloor)(confidence)) {
5092
+ if (!(0, import_types17.passesExtractedFloor)(confidence)) {
4768
5093
  noteExtractedDropped({
4769
5094
  source: fileNodeId,
4770
5095
  target: ep.infraId,
@@ -4784,7 +5109,7 @@ async function addExternalEndpointEdges(graph, services) {
4784
5109
  source: fileNodeId,
4785
5110
  target: ep.infraId,
4786
5111
  type: edgeType,
4787
- provenance: import_types16.Provenance.EXTRACTED,
5112
+ provenance: import_types17.Provenance.EXTRACTED,
4788
5113
  confidence,
4789
5114
  evidence: ep.evidence
4790
5115
  };
@@ -4809,16 +5134,16 @@ init_cjs_shims();
4809
5134
 
4810
5135
  // src/extract/infra/docker-compose.ts
4811
5136
  init_cjs_shims();
4812
- var import_node_path27 = __toESM(require("path"), 1);
4813
- var import_types18 = require("@neat.is/types");
5137
+ var import_node_path29 = __toESM(require("path"), 1);
5138
+ var import_types19 = require("@neat.is/types");
4814
5139
 
4815
5140
  // src/extract/infra/shared.ts
4816
5141
  init_cjs_shims();
4817
- var import_types17 = require("@neat.is/types");
5142
+ var import_types18 = require("@neat.is/types");
4818
5143
  function makeInfraNode(kind, name, provider = "self", extras) {
4819
5144
  return {
4820
- id: (0, import_types17.infraId)(kind, name),
4821
- type: import_types17.NodeType.InfraNode,
5145
+ id: (0, import_types18.infraId)(kind, name),
5146
+ type: import_types18.NodeType.InfraNode,
4822
5147
  name,
4823
5148
  provider,
4824
5149
  kind,
@@ -4847,7 +5172,7 @@ function dependsOnList(value) {
4847
5172
  }
4848
5173
  function serviceNameToServiceNode(name, services) {
4849
5174
  for (const s of services) {
4850
- if (s.node.name === name || import_node_path27.default.basename(s.dir) === name) return s.node.id;
5175
+ if (s.node.name === name || import_node_path29.default.basename(s.dir) === name) return s.node.id;
4851
5176
  }
4852
5177
  return null;
4853
5178
  }
@@ -4856,7 +5181,7 @@ async function addComposeInfra(graph, scanPath, services) {
4856
5181
  let edgesAdded = 0;
4857
5182
  let composePath = null;
4858
5183
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
4859
- const abs = import_node_path27.default.join(scanPath, name);
5184
+ const abs = import_node_path29.default.join(scanPath, name);
4860
5185
  if (await exists2(abs)) {
4861
5186
  composePath = abs;
4862
5187
  break;
@@ -4869,13 +5194,13 @@ async function addComposeInfra(graph, scanPath, services) {
4869
5194
  } catch (err) {
4870
5195
  recordExtractionError(
4871
5196
  "infra docker-compose",
4872
- import_node_path27.default.relative(scanPath, composePath),
5197
+ import_node_path29.default.relative(scanPath, composePath),
4873
5198
  err
4874
5199
  );
4875
5200
  return { nodesAdded, edgesAdded };
4876
5201
  }
4877
5202
  if (!compose?.services) return { nodesAdded, edgesAdded };
4878
- const evidenceFile = import_node_path27.default.relative(scanPath, composePath).split(import_node_path27.default.sep).join("/");
5203
+ const evidenceFile = import_node_path29.default.relative(scanPath, composePath).split(import_node_path29.default.sep).join("/");
4879
5204
  const composeNameToNodeId = /* @__PURE__ */ new Map();
4880
5205
  for (const [composeName, svc] of Object.entries(compose.services)) {
4881
5206
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -4897,15 +5222,15 @@ async function addComposeInfra(graph, scanPath, services) {
4897
5222
  for (const dep of dependsOnList(svc.depends_on)) {
4898
5223
  const targetId = composeNameToNodeId.get(dep);
4899
5224
  if (!targetId) continue;
4900
- const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types18.EdgeType.DEPENDS_ON);
5225
+ const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types19.EdgeType.DEPENDS_ON);
4901
5226
  if (graph.hasEdge(edgeId)) continue;
4902
5227
  const edge = {
4903
5228
  id: edgeId,
4904
5229
  source: sourceId,
4905
5230
  target: targetId,
4906
- type: import_types18.EdgeType.DEPENDS_ON,
4907
- provenance: import_types18.Provenance.EXTRACTED,
4908
- confidence: (0, import_types18.confidenceForExtracted)("structural"),
5231
+ type: import_types19.EdgeType.DEPENDS_ON,
5232
+ provenance: import_types19.Provenance.EXTRACTED,
5233
+ confidence: (0, import_types19.confidenceForExtracted)("structural"),
4909
5234
  evidence: { file: evidenceFile }
4910
5235
  };
4911
5236
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -4917,9 +5242,9 @@ async function addComposeInfra(graph, scanPath, services) {
4917
5242
 
4918
5243
  // src/extract/infra/dockerfile.ts
4919
5244
  init_cjs_shims();
4920
- var import_node_path28 = __toESM(require("path"), 1);
4921
- var import_node_fs16 = require("fs");
4922
- var import_types19 = require("@neat.is/types");
5245
+ var import_node_path30 = __toESM(require("path"), 1);
5246
+ var import_node_fs17 = require("fs");
5247
+ var import_types20 = require("@neat.is/types");
4923
5248
  function runtimeImage(content) {
4924
5249
  const lines = content.split("\n");
4925
5250
  let last = null;
@@ -4938,15 +5263,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4938
5263
  let nodesAdded = 0;
4939
5264
  let edgesAdded = 0;
4940
5265
  for (const service of services) {
4941
- const dockerfilePath = import_node_path28.default.join(service.dir, "Dockerfile");
5266
+ const dockerfilePath = import_node_path30.default.join(service.dir, "Dockerfile");
4942
5267
  if (!await exists2(dockerfilePath)) continue;
4943
5268
  let content;
4944
5269
  try {
4945
- content = await import_node_fs16.promises.readFile(dockerfilePath, "utf8");
5270
+ content = await import_node_fs17.promises.readFile(dockerfilePath, "utf8");
4946
5271
  } catch (err) {
4947
5272
  recordExtractionError(
4948
5273
  "infra dockerfile",
4949
- import_node_path28.default.relative(scanPath, dockerfilePath),
5274
+ import_node_path30.default.relative(scanPath, dockerfilePath),
4950
5275
  err
4951
5276
  );
4952
5277
  continue;
@@ -4958,7 +5283,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4958
5283
  graph.addNode(node.id, node);
4959
5284
  nodesAdded++;
4960
5285
  }
4961
- const relDockerfile = toPosix2(import_node_path28.default.relative(service.dir, dockerfilePath));
5286
+ const relDockerfile = toPosix2(import_node_path30.default.relative(service.dir, dockerfilePath));
4962
5287
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4963
5288
  graph,
4964
5289
  service.pkg.name,
@@ -4967,17 +5292,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4967
5292
  );
4968
5293
  nodesAdded += fn;
4969
5294
  edgesAdded += fe;
4970
- const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types19.EdgeType.RUNS_ON);
5295
+ const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types20.EdgeType.RUNS_ON);
4971
5296
  if (!graph.hasEdge(edgeId)) {
4972
5297
  const edge = {
4973
5298
  id: edgeId,
4974
5299
  source: fileNodeId,
4975
5300
  target: node.id,
4976
- type: import_types19.EdgeType.RUNS_ON,
4977
- provenance: import_types19.Provenance.EXTRACTED,
4978
- confidence: (0, import_types19.confidenceForExtracted)("structural"),
5301
+ type: import_types20.EdgeType.RUNS_ON,
5302
+ provenance: import_types20.Provenance.EXTRACTED,
5303
+ confidence: (0, import_types20.confidenceForExtracted)("structural"),
4979
5304
  evidence: {
4980
- file: toPosix2(import_node_path28.default.relative(scanPath, dockerfilePath))
5305
+ file: toPosix2(import_node_path30.default.relative(scanPath, dockerfilePath))
4981
5306
  }
4982
5307
  };
4983
5308
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -4989,21 +5314,21 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4989
5314
 
4990
5315
  // src/extract/infra/terraform.ts
4991
5316
  init_cjs_shims();
4992
- var import_node_fs17 = require("fs");
4993
- var import_node_path29 = __toESM(require("path"), 1);
5317
+ var import_node_fs18 = require("fs");
5318
+ var import_node_path31 = __toESM(require("path"), 1);
4994
5319
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
4995
5320
  async function walkTfFiles(start, depth = 0, max = 5) {
4996
5321
  if (depth > max) return [];
4997
5322
  const out = [];
4998
- const entries = await import_node_fs17.promises.readdir(start, { withFileTypes: true }).catch(() => []);
5323
+ const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4999
5324
  for (const entry of entries) {
5000
5325
  if (entry.isDirectory()) {
5001
5326
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
5002
- const child = import_node_path29.default.join(start, entry.name);
5327
+ const child = import_node_path31.default.join(start, entry.name);
5003
5328
  if (await isPythonVenvDir(child)) continue;
5004
5329
  out.push(...await walkTfFiles(child, depth + 1, max));
5005
5330
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
5006
- out.push(import_node_path29.default.join(start, entry.name));
5331
+ out.push(import_node_path31.default.join(start, entry.name));
5007
5332
  }
5008
5333
  }
5009
5334
  return out;
@@ -5012,7 +5337,7 @@ async function addTerraformResources(graph, scanPath) {
5012
5337
  let nodesAdded = 0;
5013
5338
  const files = await walkTfFiles(scanPath);
5014
5339
  for (const file of files) {
5015
- const content = await import_node_fs17.promises.readFile(file, "utf8");
5340
+ const content = await import_node_fs18.promises.readFile(file, "utf8");
5016
5341
  RESOURCE_RE.lastIndex = 0;
5017
5342
  let m;
5018
5343
  while ((m = RESOURCE_RE.exec(content)) !== null) {
@@ -5030,8 +5355,8 @@ async function addTerraformResources(graph, scanPath) {
5030
5355
 
5031
5356
  // src/extract/infra/k8s.ts
5032
5357
  init_cjs_shims();
5033
- var import_node_fs18 = require("fs");
5034
- var import_node_path30 = __toESM(require("path"), 1);
5358
+ var import_node_fs19 = require("fs");
5359
+ var import_node_path32 = __toESM(require("path"), 1);
5035
5360
  var import_yaml3 = require("yaml");
5036
5361
  var K8S_KIND_TO_INFRA_KIND = {
5037
5362
  Service: "k8s-service",
@@ -5045,15 +5370,15 @@ var K8S_KIND_TO_INFRA_KIND = {
5045
5370
  async function walkYamlFiles2(start, depth = 0, max = 5) {
5046
5371
  if (depth > max) return [];
5047
5372
  const out = [];
5048
- const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
5373
+ const entries = await import_node_fs19.promises.readdir(start, { withFileTypes: true }).catch(() => []);
5049
5374
  for (const entry of entries) {
5050
5375
  if (entry.isDirectory()) {
5051
5376
  if (IGNORED_DIRS.has(entry.name)) continue;
5052
- const child = import_node_path30.default.join(start, entry.name);
5377
+ const child = import_node_path32.default.join(start, entry.name);
5053
5378
  if (await isPythonVenvDir(child)) continue;
5054
5379
  out.push(...await walkYamlFiles2(child, depth + 1, max));
5055
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path30.default.extname(entry.name))) {
5056
- out.push(import_node_path30.default.join(start, entry.name));
5380
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path32.default.extname(entry.name))) {
5381
+ out.push(import_node_path32.default.join(start, entry.name));
5057
5382
  }
5058
5383
  }
5059
5384
  return out;
@@ -5062,7 +5387,7 @@ async function addK8sResources(graph, scanPath) {
5062
5387
  let nodesAdded = 0;
5063
5388
  const files = await walkYamlFiles2(scanPath);
5064
5389
  for (const file of files) {
5065
- const content = await import_node_fs18.promises.readFile(file, "utf8");
5390
+ const content = await import_node_fs19.promises.readFile(file, "utf8");
5066
5391
  let docs;
5067
5392
  try {
5068
5393
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -5097,17 +5422,17 @@ async function addInfra(graph, scanPath, services) {
5097
5422
  }
5098
5423
 
5099
5424
  // src/extract/index.ts
5100
- var import_node_path32 = __toESM(require("path"), 1);
5425
+ var import_node_path34 = __toESM(require("path"), 1);
5101
5426
 
5102
5427
  // src/extract/retire.ts
5103
5428
  init_cjs_shims();
5104
- var import_node_fs19 = require("fs");
5105
- var import_node_path31 = __toESM(require("path"), 1);
5106
- var import_types20 = require("@neat.is/types");
5429
+ var import_node_fs20 = require("fs");
5430
+ var import_node_path33 = __toESM(require("path"), 1);
5431
+ var import_types21 = require("@neat.is/types");
5107
5432
  function dropOrphanedFileNodes(graph) {
5108
5433
  const orphans = [];
5109
5434
  graph.forEachNode((id, attrs) => {
5110
- if (attrs.type !== import_types20.NodeType.FileNode) return;
5435
+ if (attrs.type !== import_types21.NodeType.FileNode) return;
5111
5436
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
5112
5437
  orphans.push(id);
5113
5438
  }
@@ -5120,14 +5445,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
5120
5445
  const bases = [scanPath, ...serviceDirs];
5121
5446
  graph.forEachEdge((id, attrs) => {
5122
5447
  const edge = attrs;
5123
- if (edge.provenance !== import_types20.Provenance.EXTRACTED) return;
5448
+ if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
5124
5449
  const evidenceFile = edge.evidence?.file;
5125
5450
  if (!evidenceFile) return;
5126
- if (import_node_path31.default.isAbsolute(evidenceFile)) {
5127
- if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
5451
+ if (import_node_path33.default.isAbsolute(evidenceFile)) {
5452
+ if (!(0, import_node_fs20.existsSync)(evidenceFile)) toDrop.push(id);
5128
5453
  return;
5129
5454
  }
5130
- const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path31.default.join(base, evidenceFile)));
5455
+ const found = bases.some((base) => (0, import_node_fs20.existsSync)(import_node_path33.default.join(base, evidenceFile)));
5131
5456
  if (!found) toDrop.push(id);
5132
5457
  });
5133
5458
  for (const id of toDrop) graph.dropEdge(id);
@@ -5142,6 +5467,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
5142
5467
  const services = await discoverServices(scanPath);
5143
5468
  const phase1Nodes = addServiceNodes(graph, services);
5144
5469
  await addServiceAliases(graph, scanPath, services);
5470
+ const fileEnum = await addFiles(graph, services);
5471
+ const importGraph = await addImports(graph, services);
5145
5472
  const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
5146
5473
  const phase3 = await addConfigNodes(graph, services, scanPath);
5147
5474
  const phase4 = await addCallEdges(graph, services);
@@ -5165,7 +5492,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
5165
5492
  }
5166
5493
  const droppedEntries = drainDroppedExtracted();
5167
5494
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
5168
- const rejectedPath = import_node_path32.default.join(import_node_path32.default.dirname(opts.errorsPath), "rejected.ndjson");
5495
+ const rejectedPath = import_node_path34.default.join(import_node_path34.default.dirname(opts.errorsPath), "rejected.ndjson");
5169
5496
  try {
5170
5497
  await writeRejectedExtracted(droppedEntries, rejectedPath);
5171
5498
  } catch (err) {
@@ -5175,8 +5502,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
5175
5502
  }
5176
5503
  }
5177
5504
  const result = {
5178
- nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
5179
- edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
5505
+ nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
5506
+ edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
5180
5507
  frontiersPromoted,
5181
5508
  extractionErrors: errorEntries.length,
5182
5509
  errorEntries,
@@ -5199,7 +5526,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
5199
5526
 
5200
5527
  // src/diff.ts
5201
5528
  init_cjs_shims();
5202
- var import_node_fs20 = require("fs");
5529
+ var import_node_fs21 = require("fs");
5203
5530
  async function loadSnapshotForDiff(target) {
5204
5531
  if (/^https?:\/\//i.test(target)) {
5205
5532
  const res = await fetch(target);
@@ -5208,7 +5535,7 @@ async function loadSnapshotForDiff(target) {
5208
5535
  }
5209
5536
  return await res.json();
5210
5537
  }
5211
- const raw = await import_node_fs20.promises.readFile(target, "utf8");
5538
+ const raw = await import_node_fs21.promises.readFile(target, "utf8");
5212
5539
  return JSON.parse(raw);
5213
5540
  }
5214
5541
  function indexEntries(entries) {
@@ -5276,9 +5603,9 @@ function canonicalJson(value) {
5276
5603
 
5277
5604
  // src/persist.ts
5278
5605
  init_cjs_shims();
5279
- var import_node_fs21 = require("fs");
5280
- var import_node_path33 = __toESM(require("path"), 1);
5281
- var import_types21 = require("@neat.is/types");
5606
+ var import_node_fs22 = require("fs");
5607
+ var import_node_path35 = __toESM(require("path"), 1);
5608
+ var import_types22 = require("@neat.is/types");
5282
5609
  var SCHEMA_VERSION = 4;
5283
5610
  function migrateV1ToV2(payload) {
5284
5611
  const nodes = payload.graph.nodes;
@@ -5300,12 +5627,12 @@ function migrateV2ToV3(payload) {
5300
5627
  for (const edge of edges) {
5301
5628
  const attrs = edge.attributes;
5302
5629
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
5303
- attrs.provenance = import_types21.Provenance.OBSERVED;
5630
+ attrs.provenance = import_types22.Provenance.OBSERVED;
5304
5631
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
5305
5632
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
5306
5633
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
5307
5634
  if (type && source && target) {
5308
- const newId = (0, import_types21.observedEdgeId)(source, target, type);
5635
+ const newId = (0, import_types22.observedEdgeId)(source, target, type);
5309
5636
  attrs.id = newId;
5310
5637
  if (edge.key) edge.key = newId;
5311
5638
  }
@@ -5314,7 +5641,7 @@ function migrateV2ToV3(payload) {
5314
5641
  return { ...payload, schemaVersion: 3 };
5315
5642
  }
5316
5643
  async function ensureDir(filePath) {
5317
- await import_node_fs21.promises.mkdir(import_node_path33.default.dirname(filePath), { recursive: true });
5644
+ await import_node_fs22.promises.mkdir(import_node_path35.default.dirname(filePath), { recursive: true });
5318
5645
  }
5319
5646
  async function saveGraphToDisk(graph, outPath) {
5320
5647
  await ensureDir(outPath);
@@ -5324,13 +5651,13 @@ async function saveGraphToDisk(graph, outPath) {
5324
5651
  graph: graph.export()
5325
5652
  };
5326
5653
  const tmp = `${outPath}.tmp`;
5327
- await import_node_fs21.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
5328
- await import_node_fs21.promises.rename(tmp, outPath);
5654
+ await import_node_fs22.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
5655
+ await import_node_fs22.promises.rename(tmp, outPath);
5329
5656
  }
5330
5657
  async function loadGraphFromDisk(graph, outPath) {
5331
5658
  let raw;
5332
5659
  try {
5333
- raw = await import_node_fs21.promises.readFile(outPath, "utf8");
5660
+ raw = await import_node_fs22.promises.readFile(outPath, "utf8");
5334
5661
  } catch (err) {
5335
5662
  if (err.code === "ENOENT") return;
5336
5663
  throw err;
@@ -5389,23 +5716,23 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
5389
5716
 
5390
5717
  // src/projects.ts
5391
5718
  init_cjs_shims();
5392
- var import_node_path34 = __toESM(require("path"), 1);
5719
+ var import_node_path36 = __toESM(require("path"), 1);
5393
5720
  function pathsForProject(project, baseDir) {
5394
5721
  if (project === DEFAULT_PROJECT) {
5395
5722
  return {
5396
- snapshotPath: import_node_path34.default.join(baseDir, "graph.json"),
5397
- errorsPath: import_node_path34.default.join(baseDir, "errors.ndjson"),
5398
- staleEventsPath: import_node_path34.default.join(baseDir, "stale-events.ndjson"),
5399
- embeddingsCachePath: import_node_path34.default.join(baseDir, "embeddings.json"),
5400
- policyViolationsPath: import_node_path34.default.join(baseDir, "policy-violations.ndjson")
5723
+ snapshotPath: import_node_path36.default.join(baseDir, "graph.json"),
5724
+ errorsPath: import_node_path36.default.join(baseDir, "errors.ndjson"),
5725
+ staleEventsPath: import_node_path36.default.join(baseDir, "stale-events.ndjson"),
5726
+ embeddingsCachePath: import_node_path36.default.join(baseDir, "embeddings.json"),
5727
+ policyViolationsPath: import_node_path36.default.join(baseDir, "policy-violations.ndjson")
5401
5728
  };
5402
5729
  }
5403
5730
  return {
5404
- snapshotPath: import_node_path34.default.join(baseDir, `${project}.json`),
5405
- errorsPath: import_node_path34.default.join(baseDir, `errors.${project}.ndjson`),
5406
- staleEventsPath: import_node_path34.default.join(baseDir, `stale-events.${project}.ndjson`),
5407
- embeddingsCachePath: import_node_path34.default.join(baseDir, `embeddings.${project}.json`),
5408
- policyViolationsPath: import_node_path34.default.join(baseDir, `policy-violations.${project}.ndjson`)
5731
+ snapshotPath: import_node_path36.default.join(baseDir, `${project}.json`),
5732
+ errorsPath: import_node_path36.default.join(baseDir, `errors.${project}.ndjson`),
5733
+ staleEventsPath: import_node_path36.default.join(baseDir, `stale-events.${project}.ndjson`),
5734
+ embeddingsCachePath: import_node_path36.default.join(baseDir, `embeddings.${project}.json`),
5735
+ policyViolationsPath: import_node_path36.default.join(baseDir, `policy-violations.${project}.ndjson`)
5409
5736
  };
5410
5737
  }
5411
5738
  var Projects = class {
@@ -5445,23 +5772,23 @@ function parseExtraProjects(raw) {
5445
5772
 
5446
5773
  // src/registry.ts
5447
5774
  init_cjs_shims();
5448
- var import_node_fs22 = require("fs");
5775
+ var import_node_fs23 = require("fs");
5449
5776
  var import_node_os3 = __toESM(require("os"), 1);
5450
- var import_node_path35 = __toESM(require("path"), 1);
5451
- var import_types22 = require("@neat.is/types");
5777
+ var import_node_path37 = __toESM(require("path"), 1);
5778
+ var import_types23 = require("@neat.is/types");
5452
5779
  function neatHome() {
5453
5780
  const override = process.env.NEAT_HOME;
5454
- if (override && override.length > 0) return import_node_path35.default.resolve(override);
5455
- return import_node_path35.default.join(import_node_os3.default.homedir(), ".neat");
5781
+ if (override && override.length > 0) return import_node_path37.default.resolve(override);
5782
+ return import_node_path37.default.join(import_node_os3.default.homedir(), ".neat");
5456
5783
  }
5457
5784
  function registryPath() {
5458
- return import_node_path35.default.join(neatHome(), "projects.json");
5785
+ return import_node_path37.default.join(neatHome(), "projects.json");
5459
5786
  }
5460
5787
  async function readRegistry() {
5461
5788
  const file = registryPath();
5462
5789
  let raw;
5463
5790
  try {
5464
- raw = await import_node_fs22.promises.readFile(file, "utf8");
5791
+ raw = await import_node_fs23.promises.readFile(file, "utf8");
5465
5792
  } catch (err) {
5466
5793
  if (err.code === "ENOENT") {
5467
5794
  return { version: 1, projects: [] };
@@ -5469,7 +5796,7 @@ async function readRegistry() {
5469
5796
  throw err;
5470
5797
  }
5471
5798
  const parsed = JSON.parse(raw);
5472
- return import_types22.RegistryFileSchema.parse(parsed);
5799
+ return import_types23.RegistryFileSchema.parse(parsed);
5473
5800
  }
5474
5801
  async function getProject(name) {
5475
5802
  const reg = await readRegistry();
@@ -5671,11 +5998,11 @@ function registerRoutes(scope, ctx) {
5671
5998
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
5672
5999
  const parsed = [];
5673
6000
  for (const c of candidates) {
5674
- const r = import_types23.DivergenceTypeSchema.safeParse(c);
6001
+ const r = import_types24.DivergenceTypeSchema.safeParse(c);
5675
6002
  if (!r.success) {
5676
6003
  return reply.code(400).send({
5677
6004
  error: `unknown divergence type "${c}"`,
5678
- allowed: import_types23.DivergenceTypeSchema.options
6005
+ allowed: import_types24.DivergenceTypeSchema.options
5679
6006
  });
5680
6007
  }
5681
6008
  parsed.push(r.data);
@@ -5888,7 +6215,7 @@ function registerRoutes(scope, ctx) {
5888
6215
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
5889
6216
  let violations = await log.readAll();
5890
6217
  if (req.query.severity) {
5891
- const sev = import_types23.PolicySeveritySchema.safeParse(req.query.severity);
6218
+ const sev = import_types24.PolicySeveritySchema.safeParse(req.query.severity);
5892
6219
  if (!sev.success) {
5893
6220
  return reply.code(400).send({
5894
6221
  error: "invalid severity",
@@ -5905,7 +6232,7 @@ function registerRoutes(scope, ctx) {
5905
6232
  scope.post("/policies/check", async (req, reply) => {
5906
6233
  const proj = resolveProject(registry, req, reply, ctx.bootstrap);
5907
6234
  if (!proj) return;
5908
- const parsed = import_types23.PoliciesCheckBodySchema.safeParse(req.body ?? {});
6235
+ const parsed = import_types24.PoliciesCheckBodySchema.safeParse(req.body ?? {});
5909
6236
  if (!parsed.success) {
5910
6237
  return reply.code(400).send({
5911
6238
  error: "invalid /policies/check body",
@@ -6147,8 +6474,8 @@ init_otel_grpc();
6147
6474
 
6148
6475
  // src/search.ts
6149
6476
  init_cjs_shims();
6150
- var import_node_fs23 = require("fs");
6151
- var import_node_path38 = __toESM(require("path"), 1);
6477
+ var import_node_fs24 = require("fs");
6478
+ var import_node_path40 = __toESM(require("path"), 1);
6152
6479
  var import_node_crypto3 = require("crypto");
6153
6480
  var DEFAULT_LIMIT = 10;
6154
6481
  var NOMIC_DIM = 768;
@@ -6278,7 +6605,7 @@ async function pickEmbedder() {
6278
6605
  }
6279
6606
  async function readCache(cachePath) {
6280
6607
  try {
6281
- const raw = await import_node_fs23.promises.readFile(cachePath, "utf8");
6608
+ const raw = await import_node_fs24.promises.readFile(cachePath, "utf8");
6282
6609
  const parsed = JSON.parse(raw);
6283
6610
  if (parsed.version !== 1) return null;
6284
6611
  return parsed;
@@ -6287,8 +6614,8 @@ async function readCache(cachePath) {
6287
6614
  }
6288
6615
  }
6289
6616
  async function writeCache(cachePath, cache) {
6290
- await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(cachePath), { recursive: true });
6291
- await import_node_fs23.promises.writeFile(cachePath, JSON.stringify(cache));
6617
+ await import_node_fs24.promises.mkdir(import_node_path40.default.dirname(cachePath), { recursive: true });
6618
+ await import_node_fs24.promises.writeFile(cachePath, JSON.stringify(cache));
6292
6619
  }
6293
6620
  var VectorIndex = class {
6294
6621
  constructor(embedder, cachePath) {
@@ -6468,14 +6795,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
6468
6795
  async function main() {
6469
6796
  const baseDirEnv = process.env.NEAT_OUT_DIR;
6470
6797
  const legacyOutPath = process.env.NEAT_OUT_PATH;
6471
- const baseDir = baseDirEnv ? import_node_path39.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path39.default.resolve(import_node_path39.default.dirname(legacyOutPath)) : import_node_path39.default.resolve("./neat-out");
6472
- const defaultScanPath = import_node_path39.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
6798
+ const baseDir = baseDirEnv ? import_node_path41.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path41.default.resolve(import_node_path41.default.dirname(legacyOutPath)) : import_node_path41.default.resolve("./neat-out");
6799
+ const defaultScanPath = import_node_path41.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
6473
6800
  const registry = new Projects();
6474
6801
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
6475
6802
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
6476
6803
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
6477
6804
  const projectScan = process.env[envKey];
6478
- await bootProject(registry, name, projectScan ? import_node_path39.default.resolve(projectScan) : void 0, baseDir);
6805
+ await bootProject(registry, name, projectScan ? import_node_path41.default.resolve(projectScan) : void 0, baseDir);
6479
6806
  }
6480
6807
  const host = process.env.HOST ?? "0.0.0.0";
6481
6808
  const port = Number(process.env.PORT ?? 8080);