@neat.is/core 0.4.12 → 0.4.13

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/cli.cjs CHANGED
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
58
58
  const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
59
59
  const publicRead = opts.publicRead === true;
60
60
  app.addHook("preHandler", (req, reply, done) => {
61
- const path47 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
61
+ const path49 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
62
62
  for (const suffix of suffixes) {
63
- if (path47 === suffix || path47.endsWith(suffix)) {
63
+ if (path49 === suffix || path49.endsWith(suffix)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -186,8 +186,8 @@ function reshapeGrpcRequest(req) {
186
186
  };
187
187
  }
188
188
  function resolveProtoRoot() {
189
- const here = import_node_path37.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
190
- return import_node_path37.default.resolve(here, "..", "proto");
189
+ const here = import_node_path39.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
190
+ return import_node_path39.default.resolve(here, "..", "proto");
191
191
  }
192
192
  function loadTraceService() {
193
193
  const protoRoot = resolveProtoRoot();
@@ -255,13 +255,13 @@ async function startOtelGrpcReceiver(opts) {
255
255
  })
256
256
  };
257
257
  }
258
- var import_node_url, import_node_path37, import_node_crypto2, grpc, protoLoader;
258
+ var import_node_url, import_node_path39, import_node_crypto2, grpc, protoLoader;
259
259
  var init_otel_grpc = __esm({
260
260
  "src/otel-grpc.ts"() {
261
261
  "use strict";
262
262
  init_cjs_shims();
263
263
  import_node_url = require("url");
264
- import_node_path37 = __toESM(require("path"), 1);
264
+ import_node_path39 = __toESM(require("path"), 1);
265
265
  import_node_crypto2 = require("crypto");
266
266
  grpc = __toESM(require("@grpc/grpc-js"), 1);
267
267
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -371,10 +371,10 @@ function parseOtlpRequest(body) {
371
371
  return out;
372
372
  }
373
373
  function loadProtoRoot() {
374
- const here = import_node_path38.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
375
- const protoRoot = import_node_path38.default.resolve(here, "..", "proto");
374
+ const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
375
+ const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
376
376
  const root = new import_protobufjs.default.Root();
377
- root.resolvePath = (_origin, target) => import_node_path38.default.resolve(protoRoot, target);
377
+ root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
378
378
  root.loadSync(
379
379
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
380
380
  { keepCase: true }
@@ -567,12 +567,12 @@ async function buildOtelReceiver(opts) {
567
567
  };
568
568
  return decorated;
569
569
  }
570
- var import_node_path38, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
570
+ var import_node_path40, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
571
571
  var init_otel = __esm({
572
572
  "src/otel.ts"() {
573
573
  "use strict";
574
574
  init_cjs_shims();
575
- import_node_path38 = __toESM(require("path"), 1);
575
+ import_node_path40 = __toESM(require("path"), 1);
576
576
  import_node_url2 = require("url");
577
577
  import_fastify2 = __toESM(require("fastify"), 1);
578
578
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -600,8 +600,8 @@ __export(cli_exports, {
600
600
  });
601
601
  module.exports = __toCommonJS(cli_exports);
602
602
  init_cjs_shims();
603
- var import_node_path46 = __toESM(require("path"), 1);
604
- var import_node_fs30 = require("fs");
603
+ var import_node_path48 = __toESM(require("path"), 1);
604
+ var import_node_fs31 = require("fs");
605
605
  var import_node_url3 = require("url");
606
606
 
607
607
  // src/graph.ts
@@ -1131,19 +1131,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1131
1131
  function longestIncomingWalk(graph, start, maxDepth) {
1132
1132
  let best = { path: [start], edges: [] };
1133
1133
  const visited = /* @__PURE__ */ new Set([start]);
1134
- function step(node, path47, edges) {
1135
- if (path47.length > best.path.length) {
1136
- best = { path: [...path47], edges: [...edges] };
1134
+ function step(node, path49, edges) {
1135
+ if (path49.length > best.path.length) {
1136
+ best = { path: [...path49], edges: [...edges] };
1137
1137
  }
1138
- if (path47.length - 1 >= maxDepth) return;
1138
+ if (path49.length - 1 >= maxDepth) return;
1139
1139
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1140
1140
  for (const [srcId, edge] of incoming) {
1141
1141
  if (visited.has(srcId)) continue;
1142
1142
  visited.add(srcId);
1143
- path47.push(srcId);
1143
+ path49.push(srcId);
1144
1144
  edges.push(edge);
1145
- step(srcId, path47, edges);
1146
- path47.pop();
1145
+ step(srcId, path49, edges);
1146
+ path49.pop();
1147
1147
  edges.pop();
1148
1148
  visited.delete(srcId);
1149
1149
  }
@@ -3115,10 +3115,8 @@ async function addServiceAliases(graph, scanPath, services) {
3115
3115
  await collectK8sAliases(graph, scanPath, byName);
3116
3116
  }
3117
3117
 
3118
- // src/extract/databases/index.ts
3118
+ // src/extract/files.ts
3119
3119
  init_cjs_shims();
3120
- var import_node_path18 = __toESM(require("path"), 1);
3121
- var import_types8 = require("@neat.is/types");
3122
3120
 
3123
3121
  // src/extract/calls/shared.ts
3124
3122
  init_cjs_shims();
@@ -3217,11 +3215,333 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
3217
3215
  return { fileNodeId, nodesAdded, edgesAdded };
3218
3216
  }
3219
3217
 
3218
+ // src/extract/files.ts
3219
+ var import_node_path11 = __toESM(require("path"), 1);
3220
+ async function addFiles(graph, services) {
3221
+ let nodesAdded = 0;
3222
+ let edgesAdded = 0;
3223
+ for (const service of services) {
3224
+ const filePaths = await walkSourceFiles(service.dir);
3225
+ for (const filePath of filePaths) {
3226
+ const relPath = toPosix2(import_node_path11.default.relative(service.dir, filePath));
3227
+ const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
3228
+ graph,
3229
+ service.pkg.name,
3230
+ service.node.id,
3231
+ relPath
3232
+ );
3233
+ nodesAdded += n;
3234
+ edgesAdded += e;
3235
+ }
3236
+ }
3237
+ return { nodesAdded, edgesAdded };
3238
+ }
3239
+
3240
+ // src/extract/imports.ts
3241
+ init_cjs_shims();
3242
+ var import_node_path12 = __toESM(require("path"), 1);
3243
+ var import_node_fs11 = require("fs");
3244
+ var import_tree_sitter = __toESM(require("tree-sitter"), 1);
3245
+ var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
3246
+ var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
3247
+ var import_types8 = require("@neat.is/types");
3248
+ var PARSE_CHUNK = 16384;
3249
+ function parseSource(parser, source) {
3250
+ return parser.parse(
3251
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK)
3252
+ );
3253
+ }
3254
+ function makeJsParser() {
3255
+ const p = new import_tree_sitter.default();
3256
+ p.setLanguage(import_tree_sitter_javascript.default);
3257
+ return p;
3258
+ }
3259
+ function makePyParser() {
3260
+ const p = new import_tree_sitter.default();
3261
+ p.setLanguage(import_tree_sitter_python.default);
3262
+ return p;
3263
+ }
3264
+ function stringLiteralText(node) {
3265
+ for (let i = 0; i < node.childCount; i++) {
3266
+ const child = node.child(i);
3267
+ if (child?.type === "string_fragment") return child.text;
3268
+ }
3269
+ const raw = node.text;
3270
+ if (raw.length >= 2) return raw.slice(1, -1);
3271
+ return raw.length === 0 ? null : "";
3272
+ }
3273
+ function clipSnippet(text) {
3274
+ const oneLine = text.split("\n")[0] ?? text;
3275
+ return oneLine.length > 120 ? oneLine.slice(0, 120) : oneLine;
3276
+ }
3277
+ function collectJsImports(node, out) {
3278
+ if (node.type === "import_statement") {
3279
+ const source = node.childForFieldName("source");
3280
+ if (source) {
3281
+ const specifier = stringLiteralText(source);
3282
+ if (specifier) {
3283
+ out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
3284
+ }
3285
+ }
3286
+ return;
3287
+ }
3288
+ if (node.type === "call_expression") {
3289
+ const fn = node.childForFieldName("function");
3290
+ if (fn?.type === "identifier" && fn.text === "require") {
3291
+ const args = node.childForFieldName("arguments");
3292
+ const firstArg = args?.namedChild(0);
3293
+ if (firstArg?.type === "string") {
3294
+ const specifier = stringLiteralText(firstArg);
3295
+ if (specifier) {
3296
+ out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
3297
+ }
3298
+ }
3299
+ }
3300
+ }
3301
+ for (let i = 0; i < node.namedChildCount; i++) {
3302
+ const child = node.namedChild(i);
3303
+ if (child) collectJsImports(child, out);
3304
+ }
3305
+ }
3306
+ function collectPyImports(node, out) {
3307
+ if (node.type === "import_from_statement") {
3308
+ let level = 0;
3309
+ let modulePath = "";
3310
+ let pastFrom = false;
3311
+ for (let i = 0; i < node.childCount; i++) {
3312
+ const child = node.child(i);
3313
+ if (!child) continue;
3314
+ if (!pastFrom) {
3315
+ if (child.type === "from") pastFrom = true;
3316
+ continue;
3317
+ }
3318
+ if (child.type === "import") break;
3319
+ if (child.type === "relative_import") {
3320
+ for (let j = 0; j < child.childCount; j++) {
3321
+ const rc = child.child(j);
3322
+ if (!rc) continue;
3323
+ if (rc.type === "import_prefix") {
3324
+ for (let k = 0; k < rc.childCount; k++) {
3325
+ if (rc.child(k)?.type === ".") level++;
3326
+ }
3327
+ } else if (rc.type === "dotted_name") modulePath = rc.text;
3328
+ }
3329
+ break;
3330
+ }
3331
+ if (child.type === "dotted_name") {
3332
+ modulePath = child.text;
3333
+ break;
3334
+ }
3335
+ }
3336
+ if (level > 0 || modulePath) {
3337
+ out.push({ modulePath, level, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
3338
+ }
3339
+ }
3340
+ for (let i = 0; i < node.namedChildCount; i++) {
3341
+ const child = node.namedChild(i);
3342
+ if (child) collectPyImports(child, out);
3343
+ }
3344
+ }
3345
+ async function fileExists(p) {
3346
+ try {
3347
+ await import_node_fs11.promises.access(p);
3348
+ return true;
3349
+ } catch {
3350
+ return false;
3351
+ }
3352
+ }
3353
+ function isWithinServiceDir(candidate, serviceDir) {
3354
+ const rel = import_node_path12.default.relative(serviceDir, candidate);
3355
+ return rel !== "" && !rel.startsWith("..") && !import_node_path12.default.isAbsolute(rel);
3356
+ }
3357
+ var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
3358
+ var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
3359
+ async function firstExistingCandidate(base, serviceDir) {
3360
+ for (const ext of JS_EXTENSIONS) {
3361
+ const candidate = base + ext;
3362
+ if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3363
+ return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
3364
+ }
3365
+ }
3366
+ for (const indexFile of JS_INDEX_FILES) {
3367
+ const candidate = import_node_path12.default.join(base, indexFile);
3368
+ if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3369
+ return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
3370
+ }
3371
+ }
3372
+ return null;
3373
+ }
3374
+ async function loadTsPathConfig(serviceDir) {
3375
+ const tsconfigPath = import_node_path12.default.join(serviceDir, "tsconfig.json");
3376
+ let raw;
3377
+ try {
3378
+ raw = await import_node_fs11.promises.readFile(tsconfigPath, "utf8");
3379
+ } catch {
3380
+ return null;
3381
+ }
3382
+ try {
3383
+ const parsed = JSON.parse(raw);
3384
+ const paths = parsed.compilerOptions?.paths;
3385
+ if (!paths || Object.keys(paths).length === 0) return null;
3386
+ const baseUrl = parsed.compilerOptions?.baseUrl;
3387
+ return { paths, baseDir: baseUrl ? import_node_path12.default.resolve(serviceDir, baseUrl) : serviceDir };
3388
+ } catch (err) {
3389
+ recordExtractionError("import alias resolution", tsconfigPath, err);
3390
+ return null;
3391
+ }
3392
+ }
3393
+ async function resolveTsAlias(specifier, config, serviceDir) {
3394
+ for (const [pattern, targets] of Object.entries(config.paths)) {
3395
+ let suffix = null;
3396
+ if (pattern === specifier) {
3397
+ suffix = "";
3398
+ } else if (pattern.endsWith("/*")) {
3399
+ const prefix = pattern.slice(0, -1);
3400
+ if (specifier.startsWith(prefix)) suffix = specifier.slice(prefix.length);
3401
+ }
3402
+ if (suffix === null) continue;
3403
+ for (const target of targets) {
3404
+ const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
3405
+ const resolvedBase = import_node_path12.default.resolve(config.baseDir, targetBase, suffix);
3406
+ const hit = await firstExistingCandidate(resolvedBase, serviceDir);
3407
+ if (hit) return hit;
3408
+ if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists(resolvedBase)) {
3409
+ return toPosix2(import_node_path12.default.relative(serviceDir, resolvedBase));
3410
+ }
3411
+ }
3412
+ }
3413
+ return null;
3414
+ }
3415
+ async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3416
+ if (!specifier) return null;
3417
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
3418
+ const base = import_node_path12.default.resolve(importerDir, specifier);
3419
+ const ext = import_node_path12.default.extname(specifier);
3420
+ if (ext) {
3421
+ if (ext === ".js" || ext === ".jsx") {
3422
+ const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
3423
+ const tsSibling = base.slice(0, -ext.length) + tsExt;
3424
+ if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists(tsSibling)) {
3425
+ return toPosix2(import_node_path12.default.relative(serviceDir, tsSibling));
3426
+ }
3427
+ }
3428
+ if (isWithinServiceDir(base, serviceDir) && await fileExists(base)) {
3429
+ return toPosix2(import_node_path12.default.relative(serviceDir, base));
3430
+ }
3431
+ return null;
3432
+ }
3433
+ return firstExistingCandidate(base, serviceDir);
3434
+ }
3435
+ if (tsPaths) return resolveTsAlias(specifier, tsPaths, serviceDir);
3436
+ return null;
3437
+ }
3438
+ async function resolvePyImport(imp, importerPath, serviceDir) {
3439
+ if (!imp.modulePath) return null;
3440
+ const relPath = imp.modulePath.split(".").join("/");
3441
+ let baseDir;
3442
+ if (imp.level > 0) {
3443
+ baseDir = import_node_path12.default.dirname(importerPath);
3444
+ for (let i = 1; i < imp.level; i++) baseDir = import_node_path12.default.dirname(baseDir);
3445
+ } else {
3446
+ baseDir = serviceDir;
3447
+ }
3448
+ const candidates = [import_node_path12.default.join(baseDir, `${relPath}.py`), import_node_path12.default.join(baseDir, relPath, "__init__.py")];
3449
+ for (const candidate of candidates) {
3450
+ if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3451
+ return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
3452
+ }
3453
+ }
3454
+ return null;
3455
+ }
3456
+ function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
3457
+ const importeeFileId = (0, import_types8.fileId)(serviceName, importeeRelPath);
3458
+ if (!graph.hasNode(importeeFileId)) return 0;
3459
+ const edgeId = (0, import_types8.extractedEdgeId)(importerFileId, importeeFileId, import_types8.EdgeType.IMPORTS);
3460
+ if (graph.hasEdge(edgeId)) return 0;
3461
+ const edge = {
3462
+ id: edgeId,
3463
+ source: importerFileId,
3464
+ target: importeeFileId,
3465
+ type: import_types8.EdgeType.IMPORTS,
3466
+ provenance: import_types8.Provenance.EXTRACTED,
3467
+ confidence: (0, import_types8.confidenceForExtracted)("structural"),
3468
+ evidence: { file: importerRelPath, line, snippet: snippet2 }
3469
+ };
3470
+ graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
3471
+ return 1;
3472
+ }
3473
+ async function addImports(graph, services) {
3474
+ const jsParser = makeJsParser();
3475
+ const pyParser = makePyParser();
3476
+ let edgesAdded = 0;
3477
+ for (const service of services) {
3478
+ const tsPaths = await loadTsPathConfig(service.dir);
3479
+ const files = await loadSourceFiles(service.dir);
3480
+ for (const file of files) {
3481
+ if (isTestPath(file.path)) continue;
3482
+ const relFile = toPosix2(import_node_path12.default.relative(service.dir, file.path));
3483
+ const importerFileId = (0, import_types8.fileId)(service.pkg.name, relFile);
3484
+ const isPython = import_node_path12.default.extname(file.path) === ".py";
3485
+ if (isPython) {
3486
+ let pyImports = [];
3487
+ try {
3488
+ const tree = parseSource(pyParser, file.content);
3489
+ collectPyImports(tree.rootNode, pyImports);
3490
+ } catch (err) {
3491
+ recordExtractionError("import extraction", file.path, err);
3492
+ continue;
3493
+ }
3494
+ for (const imp of pyImports) {
3495
+ const resolved = await resolvePyImport(imp, file.path, service.dir);
3496
+ if (!resolved) continue;
3497
+ edgesAdded += emitImportEdge(
3498
+ graph,
3499
+ service.pkg.name,
3500
+ importerFileId,
3501
+ relFile,
3502
+ resolved,
3503
+ imp.line,
3504
+ imp.snippet
3505
+ );
3506
+ }
3507
+ continue;
3508
+ }
3509
+ let jsImports = [];
3510
+ try {
3511
+ const tree = parseSource(jsParser, file.content);
3512
+ collectJsImports(tree.rootNode, jsImports);
3513
+ } catch (err) {
3514
+ recordExtractionError("import extraction", file.path, err);
3515
+ continue;
3516
+ }
3517
+ for (const imp of jsImports) {
3518
+ const resolved = await resolveJsImport(imp.specifier, import_node_path12.default.dirname(file.path), service.dir, tsPaths);
3519
+ if (!resolved) continue;
3520
+ edgesAdded += emitImportEdge(
3521
+ graph,
3522
+ service.pkg.name,
3523
+ importerFileId,
3524
+ relFile,
3525
+ resolved,
3526
+ imp.line,
3527
+ imp.snippet
3528
+ );
3529
+ }
3530
+ }
3531
+ }
3532
+ return { nodesAdded: 0, edgesAdded };
3533
+ }
3534
+
3535
+ // src/extract/databases/index.ts
3536
+ init_cjs_shims();
3537
+ var import_node_path20 = __toESM(require("path"), 1);
3538
+ var import_types9 = require("@neat.is/types");
3539
+
3220
3540
  // src/extract/databases/db-config-yaml.ts
3221
3541
  init_cjs_shims();
3222
- var import_node_path11 = __toESM(require("path"), 1);
3542
+ var import_node_path13 = __toESM(require("path"), 1);
3223
3543
  async function parse(serviceDir) {
3224
- const yamlPath = import_node_path11.default.join(serviceDir, "db-config.yaml");
3544
+ const yamlPath = import_node_path13.default.join(serviceDir, "db-config.yaml");
3225
3545
  if (!await exists(yamlPath)) return [];
3226
3546
  const raw = await readYaml(yamlPath);
3227
3547
  return [
@@ -3239,13 +3559,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
3239
3559
 
3240
3560
  // src/extract/databases/dotenv.ts
3241
3561
  init_cjs_shims();
3242
- var import_node_fs12 = require("fs");
3243
- var import_node_path13 = __toESM(require("path"), 1);
3562
+ var import_node_fs13 = require("fs");
3563
+ var import_node_path15 = __toESM(require("path"), 1);
3244
3564
 
3245
3565
  // src/extract/databases/shared.ts
3246
3566
  init_cjs_shims();
3247
- var import_node_fs11 = require("fs");
3248
- var import_node_path12 = __toESM(require("path"), 1);
3567
+ var import_node_fs12 = require("fs");
3568
+ var import_node_path14 = __toESM(require("path"), 1);
3249
3569
  function schemeToEngine(scheme) {
3250
3570
  const s = scheme.toLowerCase().split("+")[0];
3251
3571
  switch (s) {
@@ -3284,14 +3604,14 @@ function parseConnectionString(url) {
3284
3604
  }
3285
3605
  async function readIfExists(filePath) {
3286
3606
  try {
3287
- return await import_node_fs11.promises.readFile(filePath, "utf8");
3607
+ return await import_node_fs12.promises.readFile(filePath, "utf8");
3288
3608
  } catch {
3289
3609
  return null;
3290
3610
  }
3291
3611
  }
3292
3612
  async function findFirst(serviceDir, candidates) {
3293
3613
  for (const rel of candidates) {
3294
- const abs = import_node_path12.default.join(serviceDir, rel);
3614
+ const abs = import_node_path14.default.join(serviceDir, rel);
3295
3615
  const content = await readIfExists(abs);
3296
3616
  if (content !== null) return abs;
3297
3617
  }
@@ -3342,15 +3662,15 @@ function parseDotenvLine(line) {
3342
3662
  return { key, value };
3343
3663
  }
3344
3664
  async function parse2(serviceDir) {
3345
- const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
3665
+ const entries = await import_node_fs13.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
3346
3666
  const configs = [];
3347
3667
  const seen = /* @__PURE__ */ new Set();
3348
3668
  for (const entry2 of entries) {
3349
3669
  if (!entry2.isFile()) continue;
3350
3670
  const match = isConfigFile(entry2.name);
3351
3671
  if (!match.match || match.fileType !== "env") continue;
3352
- const filePath = import_node_path13.default.join(serviceDir, entry2.name);
3353
- const content = await import_node_fs12.promises.readFile(filePath, "utf8");
3672
+ const filePath = import_node_path15.default.join(serviceDir, entry2.name);
3673
+ const content = await import_node_fs13.promises.readFile(filePath, "utf8");
3354
3674
  for (const line of content.split("\n")) {
3355
3675
  const parsed = parseDotenvLine(line);
3356
3676
  if (!parsed) continue;
@@ -3369,9 +3689,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
3369
3689
 
3370
3690
  // src/extract/databases/prisma.ts
3371
3691
  init_cjs_shims();
3372
- var import_node_path14 = __toESM(require("path"), 1);
3692
+ var import_node_path16 = __toESM(require("path"), 1);
3373
3693
  async function parse3(serviceDir) {
3374
- const schemaPath = import_node_path14.default.join(serviceDir, "prisma", "schema.prisma");
3694
+ const schemaPath = import_node_path16.default.join(serviceDir, "prisma", "schema.prisma");
3375
3695
  const content = await readIfExists(schemaPath);
3376
3696
  if (!content) return [];
3377
3697
  const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
@@ -3503,10 +3823,10 @@ var knexParser = { name: "knex", parse: parse5 };
3503
3823
 
3504
3824
  // src/extract/databases/ormconfig.ts
3505
3825
  init_cjs_shims();
3506
- var import_node_path15 = __toESM(require("path"), 1);
3826
+ var import_node_path17 = __toESM(require("path"), 1);
3507
3827
  async function parse6(serviceDir) {
3508
3828
  for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
3509
- const abs = import_node_path15.default.join(serviceDir, candidate);
3829
+ const abs = import_node_path17.default.join(serviceDir, candidate);
3510
3830
  if (!await exists(abs)) continue;
3511
3831
  const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
3512
3832
  const entries = Array.isArray(raw) ? raw : [raw];
@@ -3566,9 +3886,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
3566
3886
 
3567
3887
  // src/extract/databases/sequelize.ts
3568
3888
  init_cjs_shims();
3569
- var import_node_path16 = __toESM(require("path"), 1);
3889
+ var import_node_path18 = __toESM(require("path"), 1);
3570
3890
  async function parse8(serviceDir) {
3571
- const configPath = import_node_path16.default.join(serviceDir, "config", "config.json");
3891
+ const configPath = import_node_path18.default.join(serviceDir, "config", "config.json");
3572
3892
  if (!await exists(configPath)) return [];
3573
3893
  const raw = await readJson(configPath);
3574
3894
  const out = [];
@@ -3595,7 +3915,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
3595
3915
 
3596
3916
  // src/extract/databases/docker-compose.ts
3597
3917
  init_cjs_shims();
3598
- var import_node_path17 = __toESM(require("path"), 1);
3918
+ var import_node_path19 = __toESM(require("path"), 1);
3599
3919
  function portFromService(svc) {
3600
3920
  for (const raw of svc.ports ?? []) {
3601
3921
  const str = String(raw);
@@ -3622,7 +3942,7 @@ function databaseFromEnv(svc) {
3622
3942
  }
3623
3943
  async function parse9(serviceDir) {
3624
3944
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
3625
- const abs = import_node_path17.default.join(serviceDir, name);
3945
+ const abs = import_node_path19.default.join(serviceDir, name);
3626
3946
  if (!await exists(abs)) continue;
3627
3947
  const raw = await readYaml(abs);
3628
3948
  if (!raw?.services) return [];
@@ -3663,8 +3983,8 @@ function compatibleDriversFor(engine) {
3663
3983
  }
3664
3984
  function toDatabaseNode(config) {
3665
3985
  return {
3666
- id: (0, import_types8.databaseId)(config.host),
3667
- type: import_types8.NodeType.DatabaseNode,
3986
+ id: (0, import_types9.databaseId)(config.host),
3987
+ type: import_types9.NodeType.DatabaseNode,
3668
3988
  name: config.database || config.host,
3669
3989
  engine: config.engine,
3670
3990
  engineVersion: config.engineVersion,
@@ -3794,7 +4114,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
3794
4114
  discoveredVia: mergedDiscoveredVia
3795
4115
  });
3796
4116
  }
3797
- const relConfigFile = toPosix2(import_node_path18.default.relative(service.dir, config.sourceFile));
4117
+ const relConfigFile = toPosix2(import_node_path20.default.relative(service.dir, config.sourceFile));
3798
4118
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
3799
4119
  graph,
3800
4120
  service.pkg.name,
@@ -3803,14 +4123,14 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
3803
4123
  );
3804
4124
  nodesAdded += fn;
3805
4125
  edgesAdded += fe;
3806
- const evidenceFile = toPosix2(import_node_path18.default.relative(scanPath, config.sourceFile));
4126
+ const evidenceFile = toPosix2(import_node_path20.default.relative(scanPath, config.sourceFile));
3807
4127
  const edge = {
3808
- id: (0, import_types4.extractedEdgeId)(fileNodeId, dbNode.id, import_types8.EdgeType.CONNECTS_TO),
4128
+ id: (0, import_types4.extractedEdgeId)(fileNodeId, dbNode.id, import_types9.EdgeType.CONNECTS_TO),
3809
4129
  source: fileNodeId,
3810
4130
  target: dbNode.id,
3811
- type: import_types8.EdgeType.CONNECTS_TO,
3812
- provenance: import_types8.Provenance.EXTRACTED,
3813
- confidence: (0, import_types8.confidenceForExtracted)("structural"),
4131
+ type: import_types9.EdgeType.CONNECTS_TO,
4132
+ provenance: import_types9.Provenance.EXTRACTED,
4133
+ confidence: (0, import_types9.confidenceForExtracted)("structural"),
3814
4134
  evidence: { file: evidenceFile }
3815
4135
  };
3816
4136
  if (!graph.hasEdge(edge.id)) {
@@ -3837,15 +4157,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
3837
4157
 
3838
4158
  // src/extract/configs.ts
3839
4159
  init_cjs_shims();
3840
- var import_node_fs13 = require("fs");
3841
- var import_node_path19 = __toESM(require("path"), 1);
3842
- var import_types9 = require("@neat.is/types");
4160
+ var import_node_fs14 = require("fs");
4161
+ var import_node_path21 = __toESM(require("path"), 1);
4162
+ var import_types10 = require("@neat.is/types");
3843
4163
  async function walkConfigFiles(dir) {
3844
4164
  const out = [];
3845
4165
  async function walk(current) {
3846
- const entries = await import_node_fs13.promises.readdir(current, { withFileTypes: true });
4166
+ const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
3847
4167
  for (const entry2 of entries) {
3848
- const full = import_node_path19.default.join(current, entry2.name);
4168
+ const full = import_node_path21.default.join(current, entry2.name);
3849
4169
  if (entry2.isDirectory()) {
3850
4170
  if (IGNORED_DIRS.has(entry2.name)) continue;
3851
4171
  if (await isPythonVenvDir(full)) continue;
@@ -3864,19 +4184,19 @@ async function addConfigNodes(graph, services, scanPath) {
3864
4184
  for (const service of services) {
3865
4185
  const configFiles = await walkConfigFiles(service.dir);
3866
4186
  for (const file of configFiles) {
3867
- const relPath = import_node_path19.default.relative(scanPath, file);
4187
+ const relPath = import_node_path21.default.relative(scanPath, file);
3868
4188
  const node = {
3869
- id: (0, import_types9.configId)(relPath),
3870
- type: import_types9.NodeType.ConfigNode,
3871
- name: import_node_path19.default.basename(file),
4189
+ id: (0, import_types10.configId)(relPath),
4190
+ type: import_types10.NodeType.ConfigNode,
4191
+ name: import_node_path21.default.basename(file),
3872
4192
  path: relPath,
3873
- fileType: isConfigFile(import_node_path19.default.basename(file)).fileType
4193
+ fileType: isConfigFile(import_node_path21.default.basename(file)).fileType
3874
4194
  };
3875
4195
  if (!graph.hasNode(node.id)) {
3876
4196
  graph.addNode(node.id, node);
3877
4197
  nodesAdded++;
3878
4198
  }
3879
- const relToService = toPosix2(import_node_path19.default.relative(service.dir, file));
4199
+ const relToService = toPosix2(import_node_path21.default.relative(service.dir, file));
3880
4200
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
3881
4201
  graph,
3882
4202
  service.pkg.name,
@@ -3886,13 +4206,13 @@ async function addConfigNodes(graph, services, scanPath) {
3886
4206
  nodesAdded += fn;
3887
4207
  edgesAdded += fe;
3888
4208
  const edge = {
3889
- id: (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types9.EdgeType.CONFIGURED_BY),
4209
+ id: (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types10.EdgeType.CONFIGURED_BY),
3890
4210
  source: fileNodeId,
3891
4211
  target: node.id,
3892
- type: import_types9.EdgeType.CONFIGURED_BY,
3893
- provenance: import_types9.Provenance.EXTRACTED,
3894
- confidence: (0, import_types9.confidenceForExtracted)("structural"),
3895
- evidence: { file: relPath.split(import_node_path19.default.sep).join("/") }
4212
+ type: import_types10.EdgeType.CONFIGURED_BY,
4213
+ provenance: import_types10.Provenance.EXTRACTED,
4214
+ confidence: (0, import_types10.confidenceForExtracted)("structural"),
4215
+ evidence: { file: relPath.split(import_node_path21.default.sep).join("/") }
3896
4216
  };
3897
4217
  if (!graph.hasEdge(edge.id)) {
3898
4218
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -3905,15 +4225,15 @@ async function addConfigNodes(graph, services, scanPath) {
3905
4225
 
3906
4226
  // src/extract/calls/index.ts
3907
4227
  init_cjs_shims();
3908
- var import_types15 = require("@neat.is/types");
4228
+ var import_types16 = require("@neat.is/types");
3909
4229
 
3910
4230
  // src/extract/calls/http.ts
3911
4231
  init_cjs_shims();
3912
- var import_node_path20 = __toESM(require("path"), 1);
3913
- var import_tree_sitter = __toESM(require("tree-sitter"), 1);
3914
- var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
3915
- var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
3916
- var import_types10 = require("@neat.is/types");
4232
+ var import_node_path22 = __toESM(require("path"), 1);
4233
+ var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
4234
+ var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
4235
+ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
4236
+ var import_types11 = require("@neat.is/types");
3917
4237
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
3918
4238
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
3919
4239
  function isInsideJsxExternalLink(node) {
@@ -3941,14 +4261,14 @@ function collectStringLiterals(node, out) {
3941
4261
  if (child) collectStringLiterals(child, out);
3942
4262
  }
3943
4263
  }
3944
- var PARSE_CHUNK = 16384;
3945
- function parseSource(parser, source) {
4264
+ var PARSE_CHUNK2 = 16384;
4265
+ function parseSource2(parser, source) {
3946
4266
  return parser.parse(
3947
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK)
4267
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK2)
3948
4268
  );
3949
4269
  }
3950
4270
  function callsFromSource(source, parser, knownHosts) {
3951
- const tree = parseSource(parser, source);
4271
+ const tree = parseSource2(parser, source);
3952
4272
  const literals = [];
3953
4273
  collectStringLiterals(tree.rootNode, literals);
3954
4274
  const out = [];
@@ -3962,25 +4282,25 @@ function callsFromSource(source, parser, knownHosts) {
3962
4282
  }
3963
4283
  return out;
3964
4284
  }
3965
- function makeJsParser() {
3966
- const p = new import_tree_sitter.default();
3967
- p.setLanguage(import_tree_sitter_javascript.default);
4285
+ function makeJsParser2() {
4286
+ const p = new import_tree_sitter2.default();
4287
+ p.setLanguage(import_tree_sitter_javascript2.default);
3968
4288
  return p;
3969
4289
  }
3970
- function makePyParser() {
3971
- const p = new import_tree_sitter.default();
3972
- p.setLanguage(import_tree_sitter_python.default);
4290
+ function makePyParser2() {
4291
+ const p = new import_tree_sitter2.default();
4292
+ p.setLanguage(import_tree_sitter_python2.default);
3973
4293
  return p;
3974
4294
  }
3975
4295
  async function addHttpCallEdges(graph, services) {
3976
- const jsParser = makeJsParser();
3977
- const pyParser = makePyParser();
4296
+ const jsParser = makeJsParser2();
4297
+ const pyParser = makePyParser2();
3978
4298
  const knownHosts = /* @__PURE__ */ new Set();
3979
4299
  const hostToNodeId = /* @__PURE__ */ new Map();
3980
4300
  for (const service of services) {
3981
- knownHosts.add(import_node_path20.default.basename(service.dir));
4301
+ knownHosts.add(import_node_path22.default.basename(service.dir));
3982
4302
  knownHosts.add(service.pkg.name);
3983
- hostToNodeId.set(import_node_path20.default.basename(service.dir), service.node.id);
4303
+ hostToNodeId.set(import_node_path22.default.basename(service.dir), service.node.id);
3984
4304
  hostToNodeId.set(service.pkg.name, service.node.id);
3985
4305
  }
3986
4306
  let nodesAdded = 0;
@@ -3990,7 +4310,7 @@ async function addHttpCallEdges(graph, services) {
3990
4310
  const seen = /* @__PURE__ */ new Set();
3991
4311
  for (const file of files) {
3992
4312
  if (isTestPath(file.path)) continue;
3993
- const parser = import_node_path20.default.extname(file.path) === ".py" ? pyParser : jsParser;
4313
+ const parser = import_node_path22.default.extname(file.path) === ".py" ? pyParser : jsParser;
3994
4314
  let sites;
3995
4315
  try {
3996
4316
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -3999,14 +4319,14 @@ async function addHttpCallEdges(graph, services) {
3999
4319
  continue;
4000
4320
  }
4001
4321
  if (sites.length === 0) continue;
4002
- const relFile = toPosix2(import_node_path20.default.relative(service.dir, file.path));
4322
+ const relFile = toPosix2(import_node_path22.default.relative(service.dir, file.path));
4003
4323
  for (const site of sites) {
4004
4324
  const targetId = hostToNodeId.get(site.host);
4005
4325
  if (!targetId || targetId === service.node.id) continue;
4006
4326
  const dedupKey = `${relFile}|${targetId}`;
4007
4327
  if (seen.has(dedupKey)) continue;
4008
4328
  seen.add(dedupKey);
4009
- const confidence = (0, import_types10.confidenceForExtracted)("hostname-shape-match");
4329
+ const confidence = (0, import_types11.confidenceForExtracted)("hostname-shape-match");
4010
4330
  const ev = {
4011
4331
  file: relFile,
4012
4332
  line: site.line,
@@ -4020,25 +4340,25 @@ async function addHttpCallEdges(graph, services) {
4020
4340
  );
4021
4341
  nodesAdded += n;
4022
4342
  edgesAdded += e;
4023
- if (!(0, import_types10.passesExtractedFloor)(confidence)) {
4343
+ if (!(0, import_types11.passesExtractedFloor)(confidence)) {
4024
4344
  noteExtractedDropped({
4025
4345
  source: fileNodeId,
4026
4346
  target: targetId,
4027
- type: import_types10.EdgeType.CALLS,
4347
+ type: import_types11.EdgeType.CALLS,
4028
4348
  confidence,
4029
4349
  confidenceKind: "hostname-shape-match",
4030
4350
  evidence: ev
4031
4351
  });
4032
4352
  continue;
4033
4353
  }
4034
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types10.EdgeType.CALLS);
4354
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types11.EdgeType.CALLS);
4035
4355
  if (!graph.hasEdge(edgeId)) {
4036
4356
  const edge = {
4037
4357
  id: edgeId,
4038
4358
  source: fileNodeId,
4039
4359
  target: targetId,
4040
- type: import_types10.EdgeType.CALLS,
4041
- provenance: import_types10.Provenance.EXTRACTED,
4360
+ type: import_types11.EdgeType.CALLS,
4361
+ provenance: import_types11.Provenance.EXTRACTED,
4042
4362
  confidence,
4043
4363
  evidence: ev
4044
4364
  };
@@ -4053,8 +4373,8 @@ async function addHttpCallEdges(graph, services) {
4053
4373
 
4054
4374
  // src/extract/calls/kafka.ts
4055
4375
  init_cjs_shims();
4056
- var import_node_path21 = __toESM(require("path"), 1);
4057
- var import_types11 = require("@neat.is/types");
4376
+ var import_node_path23 = __toESM(require("path"), 1);
4377
+ var import_types12 = require("@neat.is/types");
4058
4378
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
4059
4379
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
4060
4380
  function findAll(re, text) {
@@ -4075,7 +4395,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4075
4395
  seen.add(key);
4076
4396
  const line = lineOf(file.content, topic);
4077
4397
  out.push({
4078
- infraId: (0, import_types11.infraId)("kafka-topic", topic),
4398
+ infraId: (0, import_types12.infraId)("kafka-topic", topic),
4079
4399
  name: topic,
4080
4400
  kind: "kafka-topic",
4081
4401
  edgeType,
@@ -4084,7 +4404,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4084
4404
  // tier (ADR-066).
4085
4405
  confidenceKind: "verified-call-site",
4086
4406
  evidence: {
4087
- file: import_node_path21.default.relative(serviceDir, file.path),
4407
+ file: import_node_path23.default.relative(serviceDir, file.path),
4088
4408
  line,
4089
4409
  snippet: snippet(file.content, line)
4090
4410
  }
@@ -4097,8 +4417,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4097
4417
 
4098
4418
  // src/extract/calls/redis.ts
4099
4419
  init_cjs_shims();
4100
- var import_node_path22 = __toESM(require("path"), 1);
4101
- var import_types12 = require("@neat.is/types");
4420
+ var import_node_path24 = __toESM(require("path"), 1);
4421
+ var import_types13 = require("@neat.is/types");
4102
4422
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
4103
4423
  function redisEndpointsFromFile(file, serviceDir) {
4104
4424
  const out = [];
@@ -4111,7 +4431,7 @@ function redisEndpointsFromFile(file, serviceDir) {
4111
4431
  seen.add(host);
4112
4432
  const line = lineOf(file.content, host);
4113
4433
  out.push({
4114
- infraId: (0, import_types12.infraId)("redis", host),
4434
+ infraId: (0, import_types13.infraId)("redis", host),
4115
4435
  name: host,
4116
4436
  kind: "redis",
4117
4437
  edgeType: "CALLS",
@@ -4120,7 +4440,7 @@ function redisEndpointsFromFile(file, serviceDir) {
4120
4440
  // support tier (ADR-066).
4121
4441
  confidenceKind: "url-with-structural-support",
4122
4442
  evidence: {
4123
- file: import_node_path22.default.relative(serviceDir, file.path),
4443
+ file: import_node_path24.default.relative(serviceDir, file.path),
4124
4444
  line,
4125
4445
  snippet: snippet(file.content, line)
4126
4446
  }
@@ -4131,8 +4451,8 @@ function redisEndpointsFromFile(file, serviceDir) {
4131
4451
 
4132
4452
  // src/extract/calls/aws.ts
4133
4453
  init_cjs_shims();
4134
- var import_node_path23 = __toESM(require("path"), 1);
4135
- var import_types13 = require("@neat.is/types");
4454
+ var import_node_path25 = __toESM(require("path"), 1);
4455
+ var import_types14 = require("@neat.is/types");
4136
4456
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
4137
4457
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
4138
4458
  function hasMarker(text, markers) {
@@ -4156,7 +4476,7 @@ function awsEndpointsFromFile(file, serviceDir) {
4156
4476
  seen.add(key);
4157
4477
  const line = lineOf(file.content, name);
4158
4478
  out.push({
4159
- infraId: (0, import_types13.infraId)(kind, name),
4479
+ infraId: (0, import_types14.infraId)(kind, name),
4160
4480
  name,
4161
4481
  kind,
4162
4482
  edgeType: "CALLS",
@@ -4165,7 +4485,7 @@ function awsEndpointsFromFile(file, serviceDir) {
4165
4485
  // (ADR-066).
4166
4486
  confidenceKind: "verified-call-site",
4167
4487
  evidence: {
4168
- file: import_node_path23.default.relative(serviceDir, file.path),
4488
+ file: import_node_path25.default.relative(serviceDir, file.path),
4169
4489
  line,
4170
4490
  snippet: snippet(file.content, line)
4171
4491
  }
@@ -4190,8 +4510,8 @@ function awsEndpointsFromFile(file, serviceDir) {
4190
4510
 
4191
4511
  // src/extract/calls/grpc.ts
4192
4512
  init_cjs_shims();
4193
- var import_node_path24 = __toESM(require("path"), 1);
4194
- var import_types14 = require("@neat.is/types");
4513
+ var import_node_path26 = __toESM(require("path"), 1);
4514
+ var import_types15 = require("@neat.is/types");
4195
4515
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
4196
4516
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
4197
4517
  var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
@@ -4240,7 +4560,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
4240
4560
  const { kind } = classified;
4241
4561
  const line = lineOf(file.content, m[0]);
4242
4562
  out.push({
4243
- infraId: (0, import_types14.infraId)(kind, name),
4563
+ infraId: (0, import_types15.infraId)(kind, name),
4244
4564
  name,
4245
4565
  kind,
4246
4566
  edgeType: "CALLS",
@@ -4249,7 +4569,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
4249
4569
  // tier (ADR-066).
4250
4570
  confidenceKind: "verified-call-site",
4251
4571
  evidence: {
4252
- file: import_node_path24.default.relative(serviceDir, file.path),
4572
+ file: import_node_path26.default.relative(serviceDir, file.path),
4253
4573
  line,
4254
4574
  snippet: snippet(file.content, line)
4255
4575
  }
@@ -4262,11 +4582,11 @@ function grpcEndpointsFromFile(file, serviceDir) {
4262
4582
  function edgeTypeFromEndpoint(ep) {
4263
4583
  switch (ep.edgeType) {
4264
4584
  case "PUBLISHES_TO":
4265
- return import_types15.EdgeType.PUBLISHES_TO;
4585
+ return import_types16.EdgeType.PUBLISHES_TO;
4266
4586
  case "CONSUMES_FROM":
4267
- return import_types15.EdgeType.CONSUMES_FROM;
4587
+ return import_types16.EdgeType.CONSUMES_FROM;
4268
4588
  default:
4269
- return import_types15.EdgeType.CALLS;
4589
+ return import_types16.EdgeType.CALLS;
4270
4590
  }
4271
4591
  }
4272
4592
  function isAwsKind(kind) {
@@ -4293,7 +4613,7 @@ async function addExternalEndpointEdges(graph, services) {
4293
4613
  if (!graph.hasNode(ep.infraId)) {
4294
4614
  const node = {
4295
4615
  id: ep.infraId,
4296
- type: import_types15.NodeType.InfraNode,
4616
+ type: import_types16.NodeType.InfraNode,
4297
4617
  name: ep.name,
4298
4618
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
4299
4619
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -4305,7 +4625,7 @@ async function addExternalEndpointEdges(graph, services) {
4305
4625
  nodesAdded++;
4306
4626
  }
4307
4627
  const edgeType = edgeTypeFromEndpoint(ep);
4308
- const confidence = (0, import_types15.confidenceForExtracted)(ep.confidenceKind);
4628
+ const confidence = (0, import_types16.confidenceForExtracted)(ep.confidenceKind);
4309
4629
  const relFile = toPosix2(ep.evidence.file);
4310
4630
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
4311
4631
  graph,
@@ -4315,7 +4635,7 @@ async function addExternalEndpointEdges(graph, services) {
4315
4635
  );
4316
4636
  nodesAdded += n;
4317
4637
  edgesAdded += e;
4318
- if (!(0, import_types15.passesExtractedFloor)(confidence)) {
4638
+ if (!(0, import_types16.passesExtractedFloor)(confidence)) {
4319
4639
  noteExtractedDropped({
4320
4640
  source: fileNodeId,
4321
4641
  target: ep.infraId,
@@ -4335,7 +4655,7 @@ async function addExternalEndpointEdges(graph, services) {
4335
4655
  source: fileNodeId,
4336
4656
  target: ep.infraId,
4337
4657
  type: edgeType,
4338
- provenance: import_types15.Provenance.EXTRACTED,
4658
+ provenance: import_types16.Provenance.EXTRACTED,
4339
4659
  confidence,
4340
4660
  evidence: ep.evidence
4341
4661
  };
@@ -4360,16 +4680,16 @@ init_cjs_shims();
4360
4680
 
4361
4681
  // src/extract/infra/docker-compose.ts
4362
4682
  init_cjs_shims();
4363
- var import_node_path25 = __toESM(require("path"), 1);
4364
- var import_types17 = require("@neat.is/types");
4683
+ var import_node_path27 = __toESM(require("path"), 1);
4684
+ var import_types18 = require("@neat.is/types");
4365
4685
 
4366
4686
  // src/extract/infra/shared.ts
4367
4687
  init_cjs_shims();
4368
- var import_types16 = require("@neat.is/types");
4688
+ var import_types17 = require("@neat.is/types");
4369
4689
  function makeInfraNode(kind, name, provider = "self", extras) {
4370
4690
  return {
4371
- id: (0, import_types16.infraId)(kind, name),
4372
- type: import_types16.NodeType.InfraNode,
4691
+ id: (0, import_types17.infraId)(kind, name),
4692
+ type: import_types17.NodeType.InfraNode,
4373
4693
  name,
4374
4694
  provider,
4375
4695
  kind,
@@ -4398,7 +4718,7 @@ function dependsOnList(value) {
4398
4718
  }
4399
4719
  function serviceNameToServiceNode(name, services) {
4400
4720
  for (const s of services) {
4401
- if (s.node.name === name || import_node_path25.default.basename(s.dir) === name) return s.node.id;
4721
+ if (s.node.name === name || import_node_path27.default.basename(s.dir) === name) return s.node.id;
4402
4722
  }
4403
4723
  return null;
4404
4724
  }
@@ -4407,7 +4727,7 @@ async function addComposeInfra(graph, scanPath, services) {
4407
4727
  let edgesAdded = 0;
4408
4728
  let composePath = null;
4409
4729
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
4410
- const abs = import_node_path25.default.join(scanPath, name);
4730
+ const abs = import_node_path27.default.join(scanPath, name);
4411
4731
  if (await exists(abs)) {
4412
4732
  composePath = abs;
4413
4733
  break;
@@ -4420,13 +4740,13 @@ async function addComposeInfra(graph, scanPath, services) {
4420
4740
  } catch (err) {
4421
4741
  recordExtractionError(
4422
4742
  "infra docker-compose",
4423
- import_node_path25.default.relative(scanPath, composePath),
4743
+ import_node_path27.default.relative(scanPath, composePath),
4424
4744
  err
4425
4745
  );
4426
4746
  return { nodesAdded, edgesAdded };
4427
4747
  }
4428
4748
  if (!compose?.services) return { nodesAdded, edgesAdded };
4429
- const evidenceFile = import_node_path25.default.relative(scanPath, composePath).split(import_node_path25.default.sep).join("/");
4749
+ const evidenceFile = import_node_path27.default.relative(scanPath, composePath).split(import_node_path27.default.sep).join("/");
4430
4750
  const composeNameToNodeId = /* @__PURE__ */ new Map();
4431
4751
  for (const [composeName, svc] of Object.entries(compose.services)) {
4432
4752
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -4448,15 +4768,15 @@ async function addComposeInfra(graph, scanPath, services) {
4448
4768
  for (const dep of dependsOnList(svc.depends_on)) {
4449
4769
  const targetId = composeNameToNodeId.get(dep);
4450
4770
  if (!targetId) continue;
4451
- const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types17.EdgeType.DEPENDS_ON);
4771
+ const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types18.EdgeType.DEPENDS_ON);
4452
4772
  if (graph.hasEdge(edgeId)) continue;
4453
4773
  const edge = {
4454
4774
  id: edgeId,
4455
4775
  source: sourceId,
4456
4776
  target: targetId,
4457
- type: import_types17.EdgeType.DEPENDS_ON,
4458
- provenance: import_types17.Provenance.EXTRACTED,
4459
- confidence: (0, import_types17.confidenceForExtracted)("structural"),
4777
+ type: import_types18.EdgeType.DEPENDS_ON,
4778
+ provenance: import_types18.Provenance.EXTRACTED,
4779
+ confidence: (0, import_types18.confidenceForExtracted)("structural"),
4460
4780
  evidence: { file: evidenceFile }
4461
4781
  };
4462
4782
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -4468,9 +4788,9 @@ async function addComposeInfra(graph, scanPath, services) {
4468
4788
 
4469
4789
  // src/extract/infra/dockerfile.ts
4470
4790
  init_cjs_shims();
4471
- var import_node_path26 = __toESM(require("path"), 1);
4472
- var import_node_fs14 = require("fs");
4473
- var import_types18 = require("@neat.is/types");
4791
+ var import_node_path28 = __toESM(require("path"), 1);
4792
+ var import_node_fs15 = require("fs");
4793
+ var import_types19 = require("@neat.is/types");
4474
4794
  function runtimeImage(content) {
4475
4795
  const lines = content.split("\n");
4476
4796
  let last = null;
@@ -4489,15 +4809,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4489
4809
  let nodesAdded = 0;
4490
4810
  let edgesAdded = 0;
4491
4811
  for (const service of services) {
4492
- const dockerfilePath = import_node_path26.default.join(service.dir, "Dockerfile");
4812
+ const dockerfilePath = import_node_path28.default.join(service.dir, "Dockerfile");
4493
4813
  if (!await exists(dockerfilePath)) continue;
4494
4814
  let content;
4495
4815
  try {
4496
- content = await import_node_fs14.promises.readFile(dockerfilePath, "utf8");
4816
+ content = await import_node_fs15.promises.readFile(dockerfilePath, "utf8");
4497
4817
  } catch (err) {
4498
4818
  recordExtractionError(
4499
4819
  "infra dockerfile",
4500
- import_node_path26.default.relative(scanPath, dockerfilePath),
4820
+ import_node_path28.default.relative(scanPath, dockerfilePath),
4501
4821
  err
4502
4822
  );
4503
4823
  continue;
@@ -4509,7 +4829,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4509
4829
  graph.addNode(node.id, node);
4510
4830
  nodesAdded++;
4511
4831
  }
4512
- const relDockerfile = toPosix2(import_node_path26.default.relative(service.dir, dockerfilePath));
4832
+ const relDockerfile = toPosix2(import_node_path28.default.relative(service.dir, dockerfilePath));
4513
4833
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4514
4834
  graph,
4515
4835
  service.pkg.name,
@@ -4518,17 +4838,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4518
4838
  );
4519
4839
  nodesAdded += fn;
4520
4840
  edgesAdded += fe;
4521
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types18.EdgeType.RUNS_ON);
4841
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types19.EdgeType.RUNS_ON);
4522
4842
  if (!graph.hasEdge(edgeId)) {
4523
4843
  const edge = {
4524
4844
  id: edgeId,
4525
4845
  source: fileNodeId,
4526
4846
  target: node.id,
4527
- type: import_types18.EdgeType.RUNS_ON,
4528
- provenance: import_types18.Provenance.EXTRACTED,
4529
- confidence: (0, import_types18.confidenceForExtracted)("structural"),
4847
+ type: import_types19.EdgeType.RUNS_ON,
4848
+ provenance: import_types19.Provenance.EXTRACTED,
4849
+ confidence: (0, import_types19.confidenceForExtracted)("structural"),
4530
4850
  evidence: {
4531
- file: toPosix2(import_node_path26.default.relative(scanPath, dockerfilePath))
4851
+ file: toPosix2(import_node_path28.default.relative(scanPath, dockerfilePath))
4532
4852
  }
4533
4853
  };
4534
4854
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -4540,21 +4860,21 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4540
4860
 
4541
4861
  // src/extract/infra/terraform.ts
4542
4862
  init_cjs_shims();
4543
- var import_node_fs15 = require("fs");
4544
- var import_node_path27 = __toESM(require("path"), 1);
4863
+ var import_node_fs16 = require("fs");
4864
+ var import_node_path29 = __toESM(require("path"), 1);
4545
4865
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
4546
4866
  async function walkTfFiles(start, depth = 0, max = 5) {
4547
4867
  if (depth > max) return [];
4548
4868
  const out = [];
4549
- const entries = await import_node_fs15.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4869
+ const entries = await import_node_fs16.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4550
4870
  for (const entry2 of entries) {
4551
4871
  if (entry2.isDirectory()) {
4552
4872
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
4553
- const child = import_node_path27.default.join(start, entry2.name);
4873
+ const child = import_node_path29.default.join(start, entry2.name);
4554
4874
  if (await isPythonVenvDir(child)) continue;
4555
4875
  out.push(...await walkTfFiles(child, depth + 1, max));
4556
4876
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
4557
- out.push(import_node_path27.default.join(start, entry2.name));
4877
+ out.push(import_node_path29.default.join(start, entry2.name));
4558
4878
  }
4559
4879
  }
4560
4880
  return out;
@@ -4563,7 +4883,7 @@ async function addTerraformResources(graph, scanPath) {
4563
4883
  let nodesAdded = 0;
4564
4884
  const files = await walkTfFiles(scanPath);
4565
4885
  for (const file of files) {
4566
- const content = await import_node_fs15.promises.readFile(file, "utf8");
4886
+ const content = await import_node_fs16.promises.readFile(file, "utf8");
4567
4887
  RESOURCE_RE.lastIndex = 0;
4568
4888
  let m;
4569
4889
  while ((m = RESOURCE_RE.exec(content)) !== null) {
@@ -4581,8 +4901,8 @@ async function addTerraformResources(graph, scanPath) {
4581
4901
 
4582
4902
  // src/extract/infra/k8s.ts
4583
4903
  init_cjs_shims();
4584
- var import_node_fs16 = require("fs");
4585
- var import_node_path28 = __toESM(require("path"), 1);
4904
+ var import_node_fs17 = require("fs");
4905
+ var import_node_path30 = __toESM(require("path"), 1);
4586
4906
  var import_yaml3 = require("yaml");
4587
4907
  var K8S_KIND_TO_INFRA_KIND = {
4588
4908
  Service: "k8s-service",
@@ -4596,15 +4916,15 @@ var K8S_KIND_TO_INFRA_KIND = {
4596
4916
  async function walkYamlFiles2(start, depth = 0, max = 5) {
4597
4917
  if (depth > max) return [];
4598
4918
  const out = [];
4599
- const entries = await import_node_fs16.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4919
+ const entries = await import_node_fs17.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4600
4920
  for (const entry2 of entries) {
4601
4921
  if (entry2.isDirectory()) {
4602
4922
  if (IGNORED_DIRS.has(entry2.name)) continue;
4603
- const child = import_node_path28.default.join(start, entry2.name);
4923
+ const child = import_node_path30.default.join(start, entry2.name);
4604
4924
  if (await isPythonVenvDir(child)) continue;
4605
4925
  out.push(...await walkYamlFiles2(child, depth + 1, max));
4606
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path28.default.extname(entry2.name))) {
4607
- out.push(import_node_path28.default.join(start, entry2.name));
4926
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path30.default.extname(entry2.name))) {
4927
+ out.push(import_node_path30.default.join(start, entry2.name));
4608
4928
  }
4609
4929
  }
4610
4930
  return out;
@@ -4613,7 +4933,7 @@ async function addK8sResources(graph, scanPath) {
4613
4933
  let nodesAdded = 0;
4614
4934
  const files = await walkYamlFiles2(scanPath);
4615
4935
  for (const file of files) {
4616
- const content = await import_node_fs16.promises.readFile(file, "utf8");
4936
+ const content = await import_node_fs17.promises.readFile(file, "utf8");
4617
4937
  let docs;
4618
4938
  try {
4619
4939
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -4648,17 +4968,17 @@ async function addInfra(graph, scanPath, services) {
4648
4968
  }
4649
4969
 
4650
4970
  // src/extract/index.ts
4651
- var import_node_path30 = __toESM(require("path"), 1);
4971
+ var import_node_path32 = __toESM(require("path"), 1);
4652
4972
 
4653
4973
  // src/extract/retire.ts
4654
4974
  init_cjs_shims();
4655
- var import_node_fs17 = require("fs");
4656
- var import_node_path29 = __toESM(require("path"), 1);
4657
- var import_types19 = require("@neat.is/types");
4975
+ var import_node_fs18 = require("fs");
4976
+ var import_node_path31 = __toESM(require("path"), 1);
4977
+ var import_types20 = require("@neat.is/types");
4658
4978
  function dropOrphanedFileNodes(graph) {
4659
4979
  const orphans = [];
4660
4980
  graph.forEachNode((id, attrs) => {
4661
- if (attrs.type !== import_types19.NodeType.FileNode) return;
4981
+ if (attrs.type !== import_types20.NodeType.FileNode) return;
4662
4982
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
4663
4983
  orphans.push(id);
4664
4984
  }
@@ -4671,7 +4991,7 @@ function retireEdgesByFile(graph, file) {
4671
4991
  const toDrop = [];
4672
4992
  graph.forEachEdge((id, attrs) => {
4673
4993
  const edge = attrs;
4674
- if (edge.provenance !== import_types19.Provenance.EXTRACTED) return;
4994
+ if (edge.provenance !== import_types20.Provenance.EXTRACTED) return;
4675
4995
  if (!edge.evidence?.file) return;
4676
4996
  if (edge.evidence.file === normalized) toDrop.push(id);
4677
4997
  });
@@ -4684,14 +5004,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
4684
5004
  const bases = [scanPath, ...serviceDirs];
4685
5005
  graph.forEachEdge((id, attrs) => {
4686
5006
  const edge = attrs;
4687
- if (edge.provenance !== import_types19.Provenance.EXTRACTED) return;
5007
+ if (edge.provenance !== import_types20.Provenance.EXTRACTED) return;
4688
5008
  const evidenceFile = edge.evidence?.file;
4689
5009
  if (!evidenceFile) return;
4690
- if (import_node_path29.default.isAbsolute(evidenceFile)) {
4691
- if (!(0, import_node_fs17.existsSync)(evidenceFile)) toDrop.push(id);
5010
+ if (import_node_path31.default.isAbsolute(evidenceFile)) {
5011
+ if (!(0, import_node_fs18.existsSync)(evidenceFile)) toDrop.push(id);
4692
5012
  return;
4693
5013
  }
4694
- const found = bases.some((base) => (0, import_node_fs17.existsSync)(import_node_path29.default.join(base, evidenceFile)));
5014
+ const found = bases.some((base) => (0, import_node_fs18.existsSync)(import_node_path31.default.join(base, evidenceFile)));
4695
5015
  if (!found) toDrop.push(id);
4696
5016
  });
4697
5017
  for (const id of toDrop) graph.dropEdge(id);
@@ -4706,6 +5026,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4706
5026
  const services = await discoverServices(scanPath);
4707
5027
  const phase1Nodes = addServiceNodes(graph, services);
4708
5028
  await addServiceAliases(graph, scanPath, services);
5029
+ const fileEnum = await addFiles(graph, services);
5030
+ const importGraph = await addImports(graph, services);
4709
5031
  const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
4710
5032
  const phase3 = await addConfigNodes(graph, services, scanPath);
4711
5033
  const phase4 = await addCallEdges(graph, services);
@@ -4729,7 +5051,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4729
5051
  }
4730
5052
  const droppedEntries = drainDroppedExtracted();
4731
5053
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
4732
- const rejectedPath = import_node_path30.default.join(import_node_path30.default.dirname(opts.errorsPath), "rejected.ndjson");
5054
+ const rejectedPath = import_node_path32.default.join(import_node_path32.default.dirname(opts.errorsPath), "rejected.ndjson");
4733
5055
  try {
4734
5056
  await writeRejectedExtracted(droppedEntries, rejectedPath);
4735
5057
  } catch (err) {
@@ -4739,8 +5061,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4739
5061
  }
4740
5062
  }
4741
5063
  const result = {
4742
- nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
4743
- edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
5064
+ nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
5065
+ edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
4744
5066
  frontiersPromoted,
4745
5067
  extractionErrors: errorEntries.length,
4746
5068
  errorEntries,
@@ -4763,7 +5085,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4763
5085
 
4764
5086
  // src/divergences.ts
4765
5087
  init_cjs_shims();
4766
- var import_types20 = require("@neat.is/types");
5088
+ var import_types21 = require("@neat.is/types");
4767
5089
  function bucketKey(source, target, type) {
4768
5090
  return `${type}|${source}|${target}`;
4769
5091
  }
@@ -4771,22 +5093,22 @@ function bucketEdges(graph) {
4771
5093
  const buckets = /* @__PURE__ */ new Map();
4772
5094
  graph.forEachEdge((id, attrs) => {
4773
5095
  const e = attrs;
4774
- const parsed = (0, import_types20.parseEdgeId)(id);
5096
+ const parsed = (0, import_types21.parseEdgeId)(id);
4775
5097
  const provenance = parsed?.provenance ?? e.provenance;
4776
5098
  const key = bucketKey(e.source, e.target, e.type);
4777
5099
  const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
4778
5100
  switch (provenance) {
4779
- case import_types20.Provenance.EXTRACTED:
5101
+ case import_types21.Provenance.EXTRACTED:
4780
5102
  cur.extracted = e;
4781
5103
  break;
4782
- case import_types20.Provenance.OBSERVED:
5104
+ case import_types21.Provenance.OBSERVED:
4783
5105
  cur.observed = e;
4784
5106
  break;
4785
- case import_types20.Provenance.INFERRED:
5107
+ case import_types21.Provenance.INFERRED:
4786
5108
  cur.inferred = e;
4787
5109
  break;
4788
5110
  default:
4789
- if (e.provenance === import_types20.Provenance.STALE) cur.stale = e;
5111
+ if (e.provenance === import_types21.Provenance.STALE) cur.stale = e;
4790
5112
  }
4791
5113
  buckets.set(key, cur);
4792
5114
  });
@@ -4795,7 +5117,7 @@ function bucketEdges(graph) {
4795
5117
  function nodeIsFrontier(graph, nodeId) {
4796
5118
  if (!graph.hasNode(nodeId)) return false;
4797
5119
  const attrs = graph.getNodeAttributes(nodeId);
4798
- return attrs.type === import_types20.NodeType.FrontierNode;
5120
+ return attrs.type === import_types21.NodeType.FrontierNode;
4799
5121
  }
4800
5122
  function clampConfidence(n) {
4801
5123
  if (!Number.isFinite(n)) return 0;
@@ -4816,7 +5138,7 @@ function gradedConfidence(edge) {
4816
5138
  }
4817
5139
  function detectMissingDivergences(graph, bucket) {
4818
5140
  const out = [];
4819
- if (bucket.type === import_types20.EdgeType.CONTAINS) return out;
5141
+ if (bucket.type === import_types21.EdgeType.CONTAINS) return out;
4820
5142
  if (bucket.extracted && !bucket.observed) {
4821
5143
  if (!nodeIsFrontier(graph, bucket.target)) {
4822
5144
  out.push({
@@ -4857,7 +5179,7 @@ function declaredHostFor(svc) {
4857
5179
  function hasExtractedConfiguredBy(graph, svcId) {
4858
5180
  for (const edgeId of graph.outboundEdges(svcId)) {
4859
5181
  const e = graph.getEdgeAttributes(edgeId);
4860
- if (e.type === import_types20.EdgeType.CONFIGURED_BY && e.provenance === import_types20.Provenance.EXTRACTED) {
5182
+ if (e.type === import_types21.EdgeType.CONFIGURED_BY && e.provenance === import_types21.Provenance.EXTRACTED) {
4861
5183
  return true;
4862
5184
  }
4863
5185
  }
@@ -4870,10 +5192,10 @@ function detectHostMismatch(graph, svcId, svc) {
4870
5192
  const out = [];
4871
5193
  for (const edgeId of graph.outboundEdges(svcId)) {
4872
5194
  const edge = graph.getEdgeAttributes(edgeId);
4873
- if (edge.type !== import_types20.EdgeType.CONNECTS_TO) continue;
4874
- if (edge.provenance !== import_types20.Provenance.OBSERVED) continue;
5195
+ if (edge.type !== import_types21.EdgeType.CONNECTS_TO) continue;
5196
+ if (edge.provenance !== import_types21.Provenance.OBSERVED) continue;
4875
5197
  const target = graph.getNodeAttributes(edge.target);
4876
- if (target.type !== import_types20.NodeType.DatabaseNode) continue;
5198
+ if (target.type !== import_types21.NodeType.DatabaseNode) continue;
4877
5199
  const observedHost = target.host?.trim();
4878
5200
  if (!observedHost) continue;
4879
5201
  if (observedHost === declaredHost) continue;
@@ -4895,10 +5217,10 @@ function detectCompatDivergences(graph, svcId, svc) {
4895
5217
  const deps = svc.dependencies ?? {};
4896
5218
  for (const edgeId of graph.outboundEdges(svcId)) {
4897
5219
  const edge = graph.getEdgeAttributes(edgeId);
4898
- if (edge.type !== import_types20.EdgeType.CONNECTS_TO) continue;
4899
- if (edge.provenance !== import_types20.Provenance.OBSERVED) continue;
5220
+ if (edge.type !== import_types21.EdgeType.CONNECTS_TO) continue;
5221
+ if (edge.provenance !== import_types21.Provenance.OBSERVED) continue;
4900
5222
  const target = graph.getNodeAttributes(edge.target);
4901
- if (target.type !== import_types20.NodeType.DatabaseNode) continue;
5223
+ if (target.type !== import_types21.NodeType.DatabaseNode) continue;
4902
5224
  for (const pair of compatPairs()) {
4903
5225
  if (pair.engine !== target.engine) continue;
4904
5226
  const declared = deps[pair.driver];
@@ -4959,7 +5281,7 @@ function computeDivergences(graph, opts = {}) {
4959
5281
  }
4960
5282
  graph.forEachNode((nodeId, attrs) => {
4961
5283
  const n = attrs;
4962
- if (n.type !== import_types20.NodeType.ServiceNode) return;
5284
+ if (n.type !== import_types21.NodeType.ServiceNode) return;
4963
5285
  const svc = n;
4964
5286
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
4965
5287
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -4992,7 +5314,7 @@ function computeDivergences(graph, opts = {}) {
4992
5314
  if (a.source !== b.source) return a.source.localeCompare(b.source);
4993
5315
  return a.target.localeCompare(b.target);
4994
5316
  });
4995
- return import_types20.DivergenceResultSchema.parse({
5317
+ return import_types21.DivergenceResultSchema.parse({
4996
5318
  divergences: filtered,
4997
5319
  totalAffected: filtered.length,
4998
5320
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -5001,9 +5323,9 @@ function computeDivergences(graph, opts = {}) {
5001
5323
 
5002
5324
  // src/persist.ts
5003
5325
  init_cjs_shims();
5004
- var import_node_fs18 = require("fs");
5005
- var import_node_path31 = __toESM(require("path"), 1);
5006
- var import_types21 = require("@neat.is/types");
5326
+ var import_node_fs19 = require("fs");
5327
+ var import_node_path33 = __toESM(require("path"), 1);
5328
+ var import_types22 = require("@neat.is/types");
5007
5329
  var SCHEMA_VERSION = 4;
5008
5330
  function migrateV1ToV2(payload) {
5009
5331
  const nodes = payload.graph.nodes;
@@ -5025,12 +5347,12 @@ function migrateV2ToV3(payload) {
5025
5347
  for (const edge of edges) {
5026
5348
  const attrs = edge.attributes;
5027
5349
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
5028
- attrs.provenance = import_types21.Provenance.OBSERVED;
5350
+ attrs.provenance = import_types22.Provenance.OBSERVED;
5029
5351
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
5030
5352
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
5031
5353
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
5032
5354
  if (type && source && target) {
5033
- const newId = (0, import_types21.observedEdgeId)(source, target, type);
5355
+ const newId = (0, import_types22.observedEdgeId)(source, target, type);
5034
5356
  attrs.id = newId;
5035
5357
  if (edge.key) edge.key = newId;
5036
5358
  }
@@ -5039,7 +5361,7 @@ function migrateV2ToV3(payload) {
5039
5361
  return { ...payload, schemaVersion: 3 };
5040
5362
  }
5041
5363
  async function ensureDir(filePath) {
5042
- await import_node_fs18.promises.mkdir(import_node_path31.default.dirname(filePath), { recursive: true });
5364
+ await import_node_fs19.promises.mkdir(import_node_path33.default.dirname(filePath), { recursive: true });
5043
5365
  }
5044
5366
  async function saveGraphToDisk(graph, outPath) {
5045
5367
  await ensureDir(outPath);
@@ -5049,13 +5371,13 @@ async function saveGraphToDisk(graph, outPath) {
5049
5371
  graph: graph.export()
5050
5372
  };
5051
5373
  const tmp = `${outPath}.tmp`;
5052
- await import_node_fs18.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
5053
- await import_node_fs18.promises.rename(tmp, outPath);
5374
+ await import_node_fs19.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
5375
+ await import_node_fs19.promises.rename(tmp, outPath);
5054
5376
  }
5055
5377
  async function loadGraphFromDisk(graph, outPath) {
5056
5378
  let raw;
5057
5379
  try {
5058
- raw = await import_node_fs18.promises.readFile(outPath, "utf8");
5380
+ raw = await import_node_fs19.promises.readFile(outPath, "utf8");
5059
5381
  } catch (err) {
5060
5382
  if (err.code === "ENOENT") return;
5061
5383
  throw err;
@@ -5114,8 +5436,8 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
5114
5436
 
5115
5437
  // src/gitignore.ts
5116
5438
  init_cjs_shims();
5117
- var import_node_fs19 = require("fs");
5118
- var import_node_path32 = __toESM(require("path"), 1);
5439
+ var import_node_fs20 = require("fs");
5440
+ var import_node_path34 = __toESM(require("path"), 1);
5119
5441
  var NEAT_OUT_LINE = "neat-out/";
5120
5442
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
5121
5443
  function isNeatOutLine(line) {
@@ -5123,15 +5445,15 @@ function isNeatOutLine(line) {
5123
5445
  return trimmed === "neat-out/" || trimmed === "neat-out";
5124
5446
  }
5125
5447
  async function ensureNeatOutIgnored(projectDir) {
5126
- const file = import_node_path32.default.join(projectDir, ".gitignore");
5448
+ const file = import_node_path34.default.join(projectDir, ".gitignore");
5127
5449
  let existing = null;
5128
5450
  try {
5129
- existing = await import_node_fs19.promises.readFile(file, "utf8");
5451
+ existing = await import_node_fs20.promises.readFile(file, "utf8");
5130
5452
  } catch (err) {
5131
5453
  if (err.code !== "ENOENT") throw err;
5132
5454
  }
5133
5455
  if (existing === null) {
5134
- await import_node_fs19.promises.writeFile(file, `${NEAT_HEADER}
5456
+ await import_node_fs20.promises.writeFile(file, `${NEAT_HEADER}
5135
5457
  ${NEAT_OUT_LINE}
5136
5458
  `, "utf8");
5137
5459
  return { action: "created", file };
@@ -5144,13 +5466,13 @@ ${NEAT_OUT_LINE}
5144
5466
  ${NEAT_HEADER}
5145
5467
  ${NEAT_OUT_LINE}
5146
5468
  `;
5147
- await import_node_fs19.promises.writeFile(file, existing + appended, "utf8");
5469
+ await import_node_fs20.promises.writeFile(file, existing + appended, "utf8");
5148
5470
  return { action: "added", file };
5149
5471
  }
5150
5472
 
5151
5473
  // src/summary.ts
5152
5474
  init_cjs_shims();
5153
- var import_types22 = require("@neat.is/types");
5475
+ var import_types23 = require("@neat.is/types");
5154
5476
  function renderOtelEnvBlock() {
5155
5477
  return [
5156
5478
  "for prod OTel routing, set these in your deploy platform's env:",
@@ -5160,19 +5482,19 @@ function renderOtelEnvBlock() {
5160
5482
  }
5161
5483
  function findIncompatServices(nodes) {
5162
5484
  return nodes.filter(
5163
- (n) => n.type === import_types22.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
5485
+ (n) => n.type === import_types23.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
5164
5486
  );
5165
5487
  }
5166
5488
  function servicesWithoutObserved(nodes, edges) {
5167
5489
  const seen = /* @__PURE__ */ new Set();
5168
5490
  for (const e of edges) {
5169
- if (e.provenance === import_types22.Provenance.OBSERVED) {
5491
+ if (e.provenance === import_types23.Provenance.OBSERVED) {
5170
5492
  seen.add(e.source);
5171
5493
  seen.add(e.target);
5172
5494
  }
5173
5495
  }
5174
5496
  return nodes.filter(
5175
- (n) => n.type === import_types22.NodeType.ServiceNode && !seen.has(n.id)
5497
+ (n) => n.type === import_types23.NodeType.ServiceNode && !seen.has(n.id)
5176
5498
  );
5177
5499
  }
5178
5500
  function formatDivergence(d) {
@@ -5246,27 +5568,27 @@ function formatIncompat(inc) {
5246
5568
 
5247
5569
  // src/watch.ts
5248
5570
  init_cjs_shims();
5249
- var import_node_fs25 = __toESM(require("fs"), 1);
5250
- var import_node_path40 = __toESM(require("path"), 1);
5571
+ var import_node_fs26 = __toESM(require("fs"), 1);
5572
+ var import_node_path42 = __toESM(require("path"), 1);
5251
5573
  var import_chokidar = __toESM(require("chokidar"), 1);
5252
5574
 
5253
5575
  // src/api.ts
5254
5576
  init_cjs_shims();
5255
5577
  var import_fastify = __toESM(require("fastify"), 1);
5256
5578
  var import_cors = __toESM(require("@fastify/cors"), 1);
5257
- var import_types24 = require("@neat.is/types");
5579
+ var import_types25 = require("@neat.is/types");
5258
5580
 
5259
5581
  // src/extend/index.ts
5260
5582
  init_cjs_shims();
5261
- var import_node_fs21 = require("fs");
5262
- var import_node_path34 = __toESM(require("path"), 1);
5583
+ var import_node_fs22 = require("fs");
5584
+ var import_node_path36 = __toESM(require("path"), 1);
5263
5585
  var import_node_os2 = __toESM(require("os"), 1);
5264
5586
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
5265
5587
 
5266
5588
  // src/installers/package-manager.ts
5267
5589
  init_cjs_shims();
5268
- var import_node_fs20 = require("fs");
5269
- var import_node_path33 = __toESM(require("path"), 1);
5590
+ var import_node_fs21 = require("fs");
5591
+ var import_node_path35 = __toESM(require("path"), 1);
5270
5592
  var import_node_child_process = require("child_process");
5271
5593
  var LOCKFILE_PRIORITY = [
5272
5594
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -5281,29 +5603,29 @@ var LOCKFILE_PRIORITY = [
5281
5603
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
5282
5604
  async function exists2(p) {
5283
5605
  try {
5284
- await import_node_fs20.promises.access(p);
5606
+ await import_node_fs21.promises.access(p);
5285
5607
  return true;
5286
5608
  } catch {
5287
5609
  return false;
5288
5610
  }
5289
5611
  }
5290
5612
  async function detectPackageManager(serviceDir) {
5291
- let dir = import_node_path33.default.resolve(serviceDir);
5613
+ let dir = import_node_path35.default.resolve(serviceDir);
5292
5614
  const stops = /* @__PURE__ */ new Set();
5293
5615
  for (let i = 0; i < 64; i++) {
5294
5616
  if (stops.has(dir)) break;
5295
5617
  stops.add(dir);
5296
5618
  for (const candidate of LOCKFILE_PRIORITY) {
5297
- const lockPath = import_node_path33.default.join(dir, candidate.lockfile);
5619
+ const lockPath = import_node_path35.default.join(dir, candidate.lockfile);
5298
5620
  if (await exists2(lockPath)) {
5299
5621
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
5300
5622
  }
5301
5623
  }
5302
- const parent = import_node_path33.default.dirname(dir);
5624
+ const parent = import_node_path35.default.dirname(dir);
5303
5625
  if (parent === dir) break;
5304
5626
  dir = parent;
5305
5627
  }
5306
- return { pm: "npm", cwd: import_node_path33.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
5628
+ return { pm: "npm", cwd: import_node_path35.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
5307
5629
  }
5308
5630
  async function runPackageManagerInstall(cmd) {
5309
5631
  return new Promise((resolve) => {
@@ -5343,32 +5665,32 @@ ${err.message}`
5343
5665
  }
5344
5666
 
5345
5667
  // src/extend/index.ts
5346
- async function fileExists(p) {
5668
+ async function fileExists2(p) {
5347
5669
  try {
5348
- await import_node_fs21.promises.access(p);
5670
+ await import_node_fs22.promises.access(p);
5349
5671
  return true;
5350
5672
  } catch {
5351
5673
  return false;
5352
5674
  }
5353
5675
  }
5354
5676
  async function readPackageJson(scanPath) {
5355
- const pkgPath = import_node_path34.default.join(scanPath, "package.json");
5356
- const raw = await import_node_fs21.promises.readFile(pkgPath, "utf8");
5677
+ const pkgPath = import_node_path36.default.join(scanPath, "package.json");
5678
+ const raw = await import_node_fs22.promises.readFile(pkgPath, "utf8");
5357
5679
  return JSON.parse(raw);
5358
5680
  }
5359
5681
  async function findHookFiles(scanPath) {
5360
- const entries = await import_node_fs21.promises.readdir(scanPath);
5682
+ const entries = await import_node_fs22.promises.readdir(scanPath);
5361
5683
  return entries.filter(
5362
5684
  (e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
5363
5685
  ).sort();
5364
5686
  }
5365
5687
  function extendLogPath() {
5366
- return process.env.NEAT_EXTEND_LOG ?? import_node_path34.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
5688
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path36.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
5367
5689
  }
5368
5690
  async function appendExtendLog(entry2) {
5369
5691
  const logPath = extendLogPath();
5370
- await import_node_fs21.promises.mkdir(import_node_path34.default.dirname(logPath), { recursive: true });
5371
- await import_node_fs21.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
5692
+ await import_node_fs22.promises.mkdir(import_node_path36.default.dirname(logPath), { recursive: true });
5693
+ await import_node_fs22.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
5372
5694
  }
5373
5695
  function splicedContent(fileContent, snippet2) {
5374
5696
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -5426,7 +5748,7 @@ function lookupInstrumentation(library, installedVersion) {
5426
5748
  }
5427
5749
  async function describeProjectInstrumentation(ctx) {
5428
5750
  const hookFiles = await findHookFiles(ctx.scanPath);
5429
- const envNeat = await fileExists(import_node_path34.default.join(ctx.scanPath, ".env.neat"));
5751
+ const envNeat = await fileExists2(import_node_path36.default.join(ctx.scanPath, ".env.neat"));
5430
5752
  const registryInstrPackages = new Set(
5431
5753
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
5432
5754
  );
@@ -5448,31 +5770,31 @@ async function applyExtension(ctx, args, options) {
5448
5770
  );
5449
5771
  }
5450
5772
  for (const file of hookFiles) {
5451
- const content = await import_node_fs21.promises.readFile(import_node_path34.default.join(ctx.scanPath, file), "utf8");
5773
+ const content = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
5452
5774
  if (content.includes(args.registration_snippet)) {
5453
5775
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
5454
5776
  }
5455
5777
  }
5456
5778
  const primaryFile = hookFiles[0];
5457
- const primaryPath = import_node_path34.default.join(ctx.scanPath, primaryFile);
5779
+ const primaryPath = import_node_path36.default.join(ctx.scanPath, primaryFile);
5458
5780
  const filesTouched = [];
5459
5781
  const depsAdded = [];
5460
- const pkgPath = import_node_path34.default.join(ctx.scanPath, "package.json");
5782
+ const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
5461
5783
  const pkg = await readPackageJson(ctx.scanPath);
5462
5784
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
5463
5785
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
5464
- await import_node_fs21.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5786
+ await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5465
5787
  filesTouched.push("package.json");
5466
5788
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
5467
5789
  }
5468
- const hookContent = await import_node_fs21.promises.readFile(primaryPath, "utf8");
5790
+ const hookContent = await import_node_fs22.promises.readFile(primaryPath, "utf8");
5469
5791
  const patched = splicedContent(hookContent, args.registration_snippet);
5470
5792
  if (!patched) {
5471
5793
  throw new Error(
5472
5794
  `Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
5473
5795
  );
5474
5796
  }
5475
- await import_node_fs21.promises.writeFile(primaryPath, patched, "utf8");
5797
+ await import_node_fs22.promises.writeFile(primaryPath, patched, "utf8");
5476
5798
  filesTouched.push(primaryFile);
5477
5799
  const cmd = await detectPackageManager(ctx.scanPath);
5478
5800
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -5503,7 +5825,7 @@ async function dryRunExtension(ctx, args) {
5503
5825
  };
5504
5826
  }
5505
5827
  for (const file of hookFiles) {
5506
- const content = await import_node_fs21.promises.readFile(import_node_path34.default.join(ctx.scanPath, file), "utf8");
5828
+ const content = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
5507
5829
  if (content.includes(args.registration_snippet)) {
5508
5830
  return {
5509
5831
  library: args.library,
@@ -5525,7 +5847,7 @@ async function dryRunExtension(ctx, args) {
5525
5847
  depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
5526
5848
  filesTouched.push("package.json");
5527
5849
  }
5528
- const hookContent = await import_node_fs21.promises.readFile(import_node_path34.default.join(ctx.scanPath, primaryFile), "utf8");
5850
+ const hookContent = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, primaryFile), "utf8");
5529
5851
  const patched = splicedContent(hookContent, args.registration_snippet);
5530
5852
  if (patched) {
5531
5853
  filesTouched.push(primaryFile);
@@ -5537,31 +5859,31 @@ async function dryRunExtension(ctx, args) {
5537
5859
  }
5538
5860
  async function rollbackExtension(ctx, args) {
5539
5861
  const logPath = extendLogPath();
5540
- if (!await fileExists(logPath)) {
5862
+ if (!await fileExists2(logPath)) {
5541
5863
  return { undone: false, message: "no apply found for library" };
5542
5864
  }
5543
- const raw = await import_node_fs21.promises.readFile(logPath, "utf8");
5865
+ const raw = await import_node_fs22.promises.readFile(logPath, "utf8");
5544
5866
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
5545
5867
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
5546
5868
  if (!match) {
5547
5869
  return { undone: false, message: "no apply found for library" };
5548
5870
  }
5549
- const pkgPath = import_node_path34.default.join(ctx.scanPath, "package.json");
5550
- if (await fileExists(pkgPath)) {
5871
+ const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
5872
+ if (await fileExists2(pkgPath)) {
5551
5873
  const pkg = await readPackageJson(ctx.scanPath);
5552
5874
  if (pkg.dependencies?.[match.instrumentation_package]) {
5553
5875
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
5554
5876
  pkg.dependencies = rest;
5555
- await import_node_fs21.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5877
+ await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5556
5878
  }
5557
5879
  }
5558
5880
  const hookFiles = await findHookFiles(ctx.scanPath);
5559
5881
  for (const file of hookFiles) {
5560
- const filePath = import_node_path34.default.join(ctx.scanPath, file);
5561
- const content = await import_node_fs21.promises.readFile(filePath, "utf8");
5882
+ const filePath = import_node_path36.default.join(ctx.scanPath, file);
5883
+ const content = await import_node_fs22.promises.readFile(filePath, "utf8");
5562
5884
  if (content.includes(match.registration_snippet)) {
5563
5885
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
5564
- await import_node_fs21.promises.writeFile(filePath, filtered, "utf8");
5886
+ await import_node_fs22.promises.writeFile(filePath, filtered, "utf8");
5565
5887
  break;
5566
5888
  }
5567
5889
  }
@@ -5573,7 +5895,7 @@ async function rollbackExtension(ctx, args) {
5573
5895
 
5574
5896
  // src/diff.ts
5575
5897
  init_cjs_shims();
5576
- var import_node_fs22 = require("fs");
5898
+ var import_node_fs23 = require("fs");
5577
5899
  async function loadSnapshotForDiff(target) {
5578
5900
  if (/^https?:\/\//i.test(target)) {
5579
5901
  const res = await fetch(target);
@@ -5582,7 +5904,7 @@ async function loadSnapshotForDiff(target) {
5582
5904
  }
5583
5905
  return await res.json();
5584
5906
  }
5585
- const raw = await import_node_fs22.promises.readFile(target, "utf8");
5907
+ const raw = await import_node_fs23.promises.readFile(target, "utf8");
5586
5908
  return JSON.parse(raw);
5587
5909
  }
5588
5910
  function indexEntries(entries) {
@@ -5650,23 +5972,23 @@ function canonicalJson(value) {
5650
5972
 
5651
5973
  // src/projects.ts
5652
5974
  init_cjs_shims();
5653
- var import_node_path35 = __toESM(require("path"), 1);
5975
+ var import_node_path37 = __toESM(require("path"), 1);
5654
5976
  function pathsForProject(project, baseDir) {
5655
5977
  if (project === DEFAULT_PROJECT) {
5656
5978
  return {
5657
- snapshotPath: import_node_path35.default.join(baseDir, "graph.json"),
5658
- errorsPath: import_node_path35.default.join(baseDir, "errors.ndjson"),
5659
- staleEventsPath: import_node_path35.default.join(baseDir, "stale-events.ndjson"),
5660
- embeddingsCachePath: import_node_path35.default.join(baseDir, "embeddings.json"),
5661
- policyViolationsPath: import_node_path35.default.join(baseDir, "policy-violations.ndjson")
5979
+ snapshotPath: import_node_path37.default.join(baseDir, "graph.json"),
5980
+ errorsPath: import_node_path37.default.join(baseDir, "errors.ndjson"),
5981
+ staleEventsPath: import_node_path37.default.join(baseDir, "stale-events.ndjson"),
5982
+ embeddingsCachePath: import_node_path37.default.join(baseDir, "embeddings.json"),
5983
+ policyViolationsPath: import_node_path37.default.join(baseDir, "policy-violations.ndjson")
5662
5984
  };
5663
5985
  }
5664
5986
  return {
5665
- snapshotPath: import_node_path35.default.join(baseDir, `${project}.json`),
5666
- errorsPath: import_node_path35.default.join(baseDir, `errors.${project}.ndjson`),
5667
- staleEventsPath: import_node_path35.default.join(baseDir, `stale-events.${project}.ndjson`),
5668
- embeddingsCachePath: import_node_path35.default.join(baseDir, `embeddings.${project}.json`),
5669
- policyViolationsPath: import_node_path35.default.join(baseDir, `policy-violations.${project}.ndjson`)
5987
+ snapshotPath: import_node_path37.default.join(baseDir, `${project}.json`),
5988
+ errorsPath: import_node_path37.default.join(baseDir, `errors.${project}.ndjson`),
5989
+ staleEventsPath: import_node_path37.default.join(baseDir, `stale-events.${project}.ndjson`),
5990
+ embeddingsCachePath: import_node_path37.default.join(baseDir, `embeddings.${project}.json`),
5991
+ policyViolationsPath: import_node_path37.default.join(baseDir, `policy-violations.${project}.ndjson`)
5670
5992
  };
5671
5993
  }
5672
5994
  var Projects = class {
@@ -5702,25 +6024,25 @@ var Projects = class {
5702
6024
 
5703
6025
  // src/registry.ts
5704
6026
  init_cjs_shims();
5705
- var import_node_fs23 = require("fs");
6027
+ var import_node_fs24 = require("fs");
5706
6028
  var import_node_os3 = __toESM(require("os"), 1);
5707
- var import_node_path36 = __toESM(require("path"), 1);
5708
- var import_types23 = require("@neat.is/types");
6029
+ var import_node_path38 = __toESM(require("path"), 1);
6030
+ var import_types24 = require("@neat.is/types");
5709
6031
  var LOCK_TIMEOUT_MS = 5e3;
5710
6032
  var LOCK_RETRY_MS = 50;
5711
6033
  function neatHome() {
5712
6034
  const override = process.env.NEAT_HOME;
5713
- if (override && override.length > 0) return import_node_path36.default.resolve(override);
5714
- return import_node_path36.default.join(import_node_os3.default.homedir(), ".neat");
6035
+ if (override && override.length > 0) return import_node_path38.default.resolve(override);
6036
+ return import_node_path38.default.join(import_node_os3.default.homedir(), ".neat");
5715
6037
  }
5716
6038
  function registryPath() {
5717
- return import_node_path36.default.join(neatHome(), "projects.json");
6039
+ return import_node_path38.default.join(neatHome(), "projects.json");
5718
6040
  }
5719
6041
  function registryLockPath() {
5720
- return import_node_path36.default.join(neatHome(), "projects.json.lock");
6042
+ return import_node_path38.default.join(neatHome(), "projects.json.lock");
5721
6043
  }
5722
6044
  function daemonPidPath() {
5723
- return import_node_path36.default.join(neatHome(), "neatd.pid");
6045
+ return import_node_path38.default.join(neatHome(), "neatd.pid");
5724
6046
  }
5725
6047
  function isPidAliveDefault(pid) {
5726
6048
  try {
@@ -5732,7 +6054,7 @@ function isPidAliveDefault(pid) {
5732
6054
  }
5733
6055
  async function readPidFile(file) {
5734
6056
  try {
5735
- const raw = await import_node_fs23.promises.readFile(file, "utf8");
6057
+ const raw = await import_node_fs24.promises.readFile(file, "utf8");
5736
6058
  const pid = Number.parseInt(raw.trim(), 10);
5737
6059
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
5738
6060
  } catch {
@@ -5780,32 +6102,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
5780
6102
  }
5781
6103
  }
5782
6104
  async function normalizeProjectPath(input) {
5783
- const resolved = import_node_path36.default.resolve(input);
6105
+ const resolved = import_node_path38.default.resolve(input);
5784
6106
  try {
5785
- return await import_node_fs23.promises.realpath(resolved);
6107
+ return await import_node_fs24.promises.realpath(resolved);
5786
6108
  } catch {
5787
6109
  return resolved;
5788
6110
  }
5789
6111
  }
5790
6112
  async function writeAtomically(target, contents) {
5791
- await import_node_fs23.promises.mkdir(import_node_path36.default.dirname(target), { recursive: true });
6113
+ await import_node_fs24.promises.mkdir(import_node_path38.default.dirname(target), { recursive: true });
5792
6114
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
5793
- const fd = await import_node_fs23.promises.open(tmp, "w");
6115
+ const fd = await import_node_fs24.promises.open(tmp, "w");
5794
6116
  try {
5795
6117
  await fd.writeFile(contents, "utf8");
5796
6118
  await fd.sync();
5797
6119
  } finally {
5798
6120
  await fd.close();
5799
6121
  }
5800
- await import_node_fs23.promises.rename(tmp, target);
6122
+ await import_node_fs24.promises.rename(tmp, target);
5801
6123
  }
5802
6124
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
5803
6125
  const deadline = Date.now() + timeoutMs;
5804
- await import_node_fs23.promises.mkdir(import_node_path36.default.dirname(lockPath), { recursive: true });
6126
+ await import_node_fs24.promises.mkdir(import_node_path38.default.dirname(lockPath), { recursive: true });
5805
6127
  let probedHolder = false;
5806
6128
  while (true) {
5807
6129
  try {
5808
- const fd = await import_node_fs23.promises.open(lockPath, "wx");
6130
+ const fd = await import_node_fs24.promises.open(lockPath, "wx");
5809
6131
  try {
5810
6132
  await fd.writeFile(`${process.pid}
5811
6133
  `, "utf8");
@@ -5830,7 +6152,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
5830
6152
  }
5831
6153
  }
5832
6154
  async function releaseLock(lockPath) {
5833
- await import_node_fs23.promises.unlink(lockPath).catch(() => {
6155
+ await import_node_fs24.promises.unlink(lockPath).catch(() => {
5834
6156
  });
5835
6157
  }
5836
6158
  async function withLock(fn) {
@@ -5846,7 +6168,7 @@ async function readRegistry() {
5846
6168
  const file = registryPath();
5847
6169
  let raw;
5848
6170
  try {
5849
- raw = await import_node_fs23.promises.readFile(file, "utf8");
6171
+ raw = await import_node_fs24.promises.readFile(file, "utf8");
5850
6172
  } catch (err) {
5851
6173
  if (err.code === "ENOENT") {
5852
6174
  return { version: 1, projects: [] };
@@ -5854,10 +6176,10 @@ async function readRegistry() {
5854
6176
  throw err;
5855
6177
  }
5856
6178
  const parsed = JSON.parse(raw);
5857
- return import_types23.RegistryFileSchema.parse(parsed);
6179
+ return import_types24.RegistryFileSchema.parse(parsed);
5858
6180
  }
5859
6181
  async function writeRegistry(reg) {
5860
- const validated = import_types23.RegistryFileSchema.parse(reg);
6182
+ const validated = import_types24.RegistryFileSchema.parse(reg);
5861
6183
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
5862
6184
  }
5863
6185
  var ProjectNameCollisionError = class extends Error {
@@ -6120,11 +6442,11 @@ function registerRoutes(scope, ctx) {
6120
6442
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
6121
6443
  const parsed = [];
6122
6444
  for (const c of candidates) {
6123
- const r = import_types24.DivergenceTypeSchema.safeParse(c);
6445
+ const r = import_types25.DivergenceTypeSchema.safeParse(c);
6124
6446
  if (!r.success) {
6125
6447
  return reply.code(400).send({
6126
6448
  error: `unknown divergence type "${c}"`,
6127
- allowed: import_types24.DivergenceTypeSchema.options
6449
+ allowed: import_types25.DivergenceTypeSchema.options
6128
6450
  });
6129
6451
  }
6130
6452
  parsed.push(r.data);
@@ -6337,7 +6659,7 @@ function registerRoutes(scope, ctx) {
6337
6659
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
6338
6660
  let violations = await log.readAll();
6339
6661
  if (req.query.severity) {
6340
- const sev = import_types24.PolicySeveritySchema.safeParse(req.query.severity);
6662
+ const sev = import_types25.PolicySeveritySchema.safeParse(req.query.severity);
6341
6663
  if (!sev.success) {
6342
6664
  return reply.code(400).send({
6343
6665
  error: "invalid severity",
@@ -6354,7 +6676,7 @@ function registerRoutes(scope, ctx) {
6354
6676
  scope.post("/policies/check", async (req, reply) => {
6355
6677
  const proj = resolveProject(registry, req, reply, ctx.bootstrap);
6356
6678
  if (!proj) return;
6357
- const parsed = import_types24.PoliciesCheckBodySchema.safeParse(req.body ?? {});
6679
+ const parsed = import_types25.PoliciesCheckBodySchema.safeParse(req.body ?? {});
6358
6680
  if (!parsed.success) {
6359
6681
  return reply.code(400).send({
6360
6682
  error: "invalid /policies/check body",
@@ -6597,8 +6919,8 @@ init_otel_grpc();
6597
6919
 
6598
6920
  // src/search.ts
6599
6921
  init_cjs_shims();
6600
- var import_node_fs24 = require("fs");
6601
- var import_node_path39 = __toESM(require("path"), 1);
6922
+ var import_node_fs25 = require("fs");
6923
+ var import_node_path41 = __toESM(require("path"), 1);
6602
6924
  var import_node_crypto3 = require("crypto");
6603
6925
  var DEFAULT_LIMIT = 10;
6604
6926
  var NOMIC_DIM = 768;
@@ -6728,7 +7050,7 @@ async function pickEmbedder() {
6728
7050
  }
6729
7051
  async function readCache(cachePath) {
6730
7052
  try {
6731
- const raw = await import_node_fs24.promises.readFile(cachePath, "utf8");
7053
+ const raw = await import_node_fs25.promises.readFile(cachePath, "utf8");
6732
7054
  const parsed = JSON.parse(raw);
6733
7055
  if (parsed.version !== 1) return null;
6734
7056
  return parsed;
@@ -6737,8 +7059,8 @@ async function readCache(cachePath) {
6737
7059
  }
6738
7060
  }
6739
7061
  async function writeCache(cachePath, cache) {
6740
- await import_node_fs24.promises.mkdir(import_node_path39.default.dirname(cachePath), { recursive: true });
6741
- await import_node_fs24.promises.writeFile(cachePath, JSON.stringify(cache));
7062
+ await import_node_fs25.promises.mkdir(import_node_path41.default.dirname(cachePath), { recursive: true });
7063
+ await import_node_fs25.promises.writeFile(cachePath, JSON.stringify(cache));
6742
7064
  }
6743
7065
  var VectorIndex = class {
6744
7066
  constructor(embedder, cachePath) {
@@ -6886,6 +7208,7 @@ async function buildSearchIndex(graph, options = {}) {
6886
7208
  var ALL_PHASES = [
6887
7209
  "services",
6888
7210
  "aliases",
7211
+ "imports",
6889
7212
  "databases",
6890
7213
  "configs",
6891
7214
  "calls",
@@ -6893,8 +7216,8 @@ var ALL_PHASES = [
6893
7216
  ];
6894
7217
  function classifyChange(relPath) {
6895
7218
  const phases = /* @__PURE__ */ new Set();
6896
- const base = import_node_path40.default.basename(relPath).toLowerCase();
6897
- const segments = relPath.split(import_node_path40.default.sep).map((s) => s.toLowerCase());
7219
+ const base = import_node_path42.default.basename(relPath).toLowerCase();
7220
+ const segments = relPath.split(import_node_path42.default.sep).map((s) => s.toLowerCase());
6898
7221
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
6899
7222
  phases.add("services");
6900
7223
  phases.add("aliases");
@@ -6909,6 +7232,7 @@ function classifyChange(relPath) {
6909
7232
  phases.add("aliases");
6910
7233
  }
6911
7234
  if (/\.(?:js|jsx|mjs|cjs|ts|tsx|py)$/.test(base)) {
7235
+ phases.add("imports");
6912
7236
  phases.add("calls");
6913
7237
  }
6914
7238
  if (/\.ya?ml$/.test(base) && !/^docker-compose.*\.ya?ml$/.test(base)) {
@@ -6930,6 +7254,11 @@ async function runExtractPhases(graph, scanPath, phases, project = DEFAULT_PROJE
6930
7254
  if (phases.has("aliases")) {
6931
7255
  await addServiceAliases(graph, scanPath, services);
6932
7256
  }
7257
+ if (phases.has("imports")) {
7258
+ const r = await addImports(graph, services);
7259
+ nodesAdded += r.nodesAdded;
7260
+ edgesAdded += r.edgesAdded;
7261
+ }
6933
7262
  if (phases.has("databases")) {
6934
7263
  const r = await addDatabasesAndCompat(graph, services, scanPath);
6935
7264
  nodesAdded += r.nodesAdded;
@@ -7002,16 +7331,16 @@ function countWatchableDirs(scanPath, limit) {
7002
7331
  if (count >= limit) return;
7003
7332
  let entries;
7004
7333
  try {
7005
- entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
7334
+ entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
7006
7335
  } catch {
7007
7336
  return;
7008
7337
  }
7009
7338
  for (const e of entries) {
7010
7339
  if (count >= limit) return;
7011
7340
  if (!e.isDirectory()) continue;
7012
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path40.default.join(dir, e.name) + import_node_path40.default.sep))) continue;
7341
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path42.default.join(dir, e.name) + import_node_path42.default.sep))) continue;
7013
7342
  count++;
7014
- if (depth < 2) visit(import_node_path40.default.join(dir, e.name), depth + 1);
7343
+ if (depth < 2) visit(import_node_path42.default.join(dir, e.name), depth + 1);
7015
7344
  }
7016
7345
  };
7017
7346
  visit(scanPath, 0);
@@ -7029,8 +7358,8 @@ async function startWatch(graph, opts) {
7029
7358
  const projectName = opts.project ?? DEFAULT_PROJECT;
7030
7359
  await loadGraphFromDisk(graph, opts.outPath);
7031
7360
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
7032
- const policyFilePath = import_node_path40.default.join(opts.scanPath, "policy.json");
7033
- const policyViolationsPath = import_node_path40.default.join(import_node_path40.default.dirname(opts.outPath), "policy-violations.ndjson");
7361
+ const policyFilePath = import_node_path42.default.join(opts.scanPath, "policy.json");
7362
+ const policyViolationsPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.outPath), "policy-violations.ndjson");
7034
7363
  let policies = [];
7035
7364
  try {
7036
7365
  policies = await loadPolicyFile(policyFilePath);
@@ -7081,7 +7410,7 @@ async function startWatch(graph, opts) {
7081
7410
  assertBindAuthority(host, auth.authToken);
7082
7411
  const port = opts.port ?? 8080;
7083
7412
  const otelPort = opts.otelPort ?? 4318;
7084
- const cachePath = opts.embeddingsCachePath ?? import_node_path40.default.join(import_node_path40.default.dirname(opts.outPath), "embeddings.json");
7413
+ const cachePath = opts.embeddingsCachePath ?? import_node_path42.default.join(import_node_path42.default.dirname(opts.outPath), "embeddings.json");
7085
7414
  let searchIndex;
7086
7415
  try {
7087
7416
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -7099,7 +7428,7 @@ async function startWatch(graph, opts) {
7099
7428
  // Paths are derived from the explicit options the watch caller passes
7100
7429
  // — pathsForProject is only used to fill in the embeddings/snapshot
7101
7430
  // fields so the registry shape is complete.
7102
- ...pathsForProject(projectName, import_node_path40.default.dirname(opts.outPath)),
7431
+ ...pathsForProject(projectName, import_node_path42.default.dirname(opts.outPath)),
7103
7432
  snapshotPath: opts.outPath,
7104
7433
  errorsPath: opts.errorsPath,
7105
7434
  staleEventsPath: opts.staleEventsPath
@@ -7186,9 +7515,9 @@ async function startWatch(graph, opts) {
7186
7515
  };
7187
7516
  const onPath = (absPath) => {
7188
7517
  if (shouldIgnore(absPath)) return;
7189
- const rel = import_node_path40.default.relative(opts.scanPath, absPath);
7518
+ const rel = import_node_path42.default.relative(opts.scanPath, absPath);
7190
7519
  if (!rel || rel.startsWith("..")) return;
7191
- pendingPaths.add(rel.split(import_node_path40.default.sep).join("/"));
7520
+ pendingPaths.add(rel.split(import_node_path42.default.sep).join("/"));
7192
7521
  const phases = classifyChange(rel);
7193
7522
  if (phases.size === 0) {
7194
7523
  for (const p of ALL_PHASES) pending.add(p);
@@ -7242,8 +7571,8 @@ async function startWatch(graph, opts) {
7242
7571
 
7243
7572
  // src/deploy/detect.ts
7244
7573
  init_cjs_shims();
7245
- var import_node_fs26 = require("fs");
7246
- var import_node_path41 = __toESM(require("path"), 1);
7574
+ var import_node_fs27 = require("fs");
7575
+ var import_node_path43 = __toESM(require("path"), 1);
7247
7576
  var import_node_child_process2 = require("child_process");
7248
7577
  var import_node_crypto4 = require("crypto");
7249
7578
  function generateToken() {
@@ -7343,21 +7672,21 @@ async function runDeploy(opts = {}) {
7343
7672
  const token = generateToken();
7344
7673
  switch (substrate) {
7345
7674
  case "docker-compose": {
7346
- const artifactPath = import_node_path41.default.join(cwd, "docker-compose.neat.yml");
7675
+ const artifactPath = import_node_path43.default.join(cwd, "docker-compose.neat.yml");
7347
7676
  const contents = emitDockerCompose(cwd);
7348
- await import_node_fs26.promises.writeFile(artifactPath, contents, "utf8");
7677
+ await import_node_fs27.promises.writeFile(artifactPath, contents, "utf8");
7349
7678
  return {
7350
7679
  substrate,
7351
7680
  artifactPath,
7352
7681
  token,
7353
7682
  contents,
7354
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path41.default.basename(artifactPath)} up -d`
7683
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path43.default.basename(artifactPath)} up -d`
7355
7684
  };
7356
7685
  }
7357
7686
  case "systemd": {
7358
- const artifactPath = import_node_path41.default.join(cwd, "neat.service");
7687
+ const artifactPath = import_node_path43.default.join(cwd, "neat.service");
7359
7688
  const contents = emitSystemdUnit(cwd);
7360
- await import_node_fs26.promises.writeFile(artifactPath, contents, "utf8");
7689
+ await import_node_fs27.promises.writeFile(artifactPath, contents, "utf8");
7361
7690
  return {
7362
7691
  substrate,
7363
7692
  artifactPath,
@@ -7390,8 +7719,8 @@ init_cjs_shims();
7390
7719
 
7391
7720
  // src/installers/javascript.ts
7392
7721
  init_cjs_shims();
7393
- var import_node_fs27 = require("fs");
7394
- var import_node_path42 = __toESM(require("path"), 1);
7722
+ var import_node_fs28 = require("fs");
7723
+ var import_node_path44 = __toESM(require("path"), 1);
7395
7724
  var import_semver2 = __toESM(require("semver"), 1);
7396
7725
 
7397
7726
  // src/installers/templates.ts
@@ -7929,15 +8258,15 @@ var OTEL_ENV = {
7929
8258
  value: "http://localhost:4318/projects/<project>/v1/traces"
7930
8259
  };
7931
8260
  function serviceNodeName(pkg, serviceDir) {
7932
- return pkg.name ?? import_node_path42.default.basename(serviceDir);
8261
+ return pkg.name ?? import_node_path44.default.basename(serviceDir);
7933
8262
  }
7934
8263
  function projectToken(pkg, serviceDir, project) {
7935
8264
  if (project && project.length > 0) return project;
7936
- return pkg.name ?? import_node_path42.default.basename(serviceDir);
8265
+ return pkg.name ?? import_node_path44.default.basename(serviceDir);
7937
8266
  }
7938
8267
  async function readJsonFile(p) {
7939
8268
  try {
7940
- const raw = await import_node_fs27.promises.readFile(p, "utf8");
8269
+ const raw = await import_node_fs28.promises.readFile(p, "utf8");
7941
8270
  return JSON.parse(raw);
7942
8271
  } catch {
7943
8272
  return null;
@@ -7946,16 +8275,16 @@ async function readJsonFile(p) {
7946
8275
  async function detectRuntimeKind(pkgRoot, pkg) {
7947
8276
  const deps = allDeps(pkg);
7948
8277
  if ("react-native" in deps || "expo" in deps) return "react-native";
7949
- const appJson = await readJsonFile(import_node_path42.default.join(pkgRoot, "app.json"));
8278
+ const appJson = await readJsonFile(import_node_path44.default.join(pkgRoot, "app.json"));
7950
8279
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
7951
8280
  return "react-native";
7952
8281
  }
7953
- if (await exists3(import_node_path42.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path42.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path42.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
8282
+ if (await exists3(import_node_path44.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path44.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path44.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
7954
8283
  return "browser-bundle";
7955
8284
  }
7956
- if (await exists3(import_node_path42.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
7957
- if (await exists3(import_node_path42.default.join(pkgRoot, "bun.lockb"))) return "bun";
7958
- if (await exists3(import_node_path42.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path42.default.join(pkgRoot, "deno.lock"))) {
8285
+ if (await exists3(import_node_path44.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
8286
+ if (await exists3(import_node_path44.default.join(pkgRoot, "bun.lockb"))) return "bun";
8287
+ if (await exists3(import_node_path44.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path44.default.join(pkgRoot, "deno.lock"))) {
7959
8288
  return "deno";
7960
8289
  }
7961
8290
  const engines = pkg.engines ?? {};
@@ -7964,7 +8293,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
7964
8293
  }
7965
8294
  async function readPackageJson2(serviceDir) {
7966
8295
  try {
7967
- const raw = await import_node_fs27.promises.readFile(import_node_path42.default.join(serviceDir, "package.json"), "utf8");
8296
+ const raw = await import_node_fs28.promises.readFile(import_node_path44.default.join(serviceDir, "package.json"), "utf8");
7968
8297
  return JSON.parse(raw);
7969
8298
  } catch {
7970
8299
  return null;
@@ -7972,7 +8301,7 @@ async function readPackageJson2(serviceDir) {
7972
8301
  }
7973
8302
  async function exists3(p) {
7974
8303
  try {
7975
- await import_node_fs27.promises.stat(p);
8304
+ await import_node_fs28.promises.stat(p);
7976
8305
  return true;
7977
8306
  } catch {
7978
8307
  return false;
@@ -7980,7 +8309,7 @@ async function exists3(p) {
7980
8309
  }
7981
8310
  async function readFileMaybe(p) {
7982
8311
  try {
7983
- return await import_node_fs27.promises.readFile(p, "utf8");
8312
+ return await import_node_fs28.promises.readFile(p, "utf8");
7984
8313
  } catch {
7985
8314
  return null;
7986
8315
  }
@@ -8008,7 +8337,7 @@ function needsVersionUpgrade(installed, expected) {
8008
8337
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
8009
8338
  async function findNextConfig(serviceDir) {
8010
8339
  for (const name of NEXT_CONFIG_CANDIDATES) {
8011
- const candidate = import_node_path42.default.join(serviceDir, name);
8340
+ const candidate = import_node_path44.default.join(serviceDir, name);
8012
8341
  if (await exists3(candidate)) return candidate;
8013
8342
  }
8014
8343
  return null;
@@ -8035,7 +8364,7 @@ function hasRemixDependency(pkg) {
8035
8364
  }
8036
8365
  async function findRemixEntry(serviceDir) {
8037
8366
  for (const rel of REMIX_ENTRY_CANDIDATES) {
8038
- const candidate = import_node_path42.default.join(serviceDir, rel);
8367
+ const candidate = import_node_path44.default.join(serviceDir, rel);
8039
8368
  if (await exists3(candidate)) return candidate;
8040
8369
  }
8041
8370
  return null;
@@ -8047,14 +8376,14 @@ function hasSvelteKitDependency(pkg) {
8047
8376
  }
8048
8377
  async function findSvelteKitHooks(serviceDir) {
8049
8378
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
8050
- const candidate = import_node_path42.default.join(serviceDir, rel);
8379
+ const candidate = import_node_path44.default.join(serviceDir, rel);
8051
8380
  if (await exists3(candidate)) return candidate;
8052
8381
  }
8053
8382
  return null;
8054
8383
  }
8055
8384
  async function findSvelteKitConfig(serviceDir) {
8056
8385
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
8057
- const candidate = import_node_path42.default.join(serviceDir, rel);
8386
+ const candidate = import_node_path44.default.join(serviceDir, rel);
8058
8387
  if (await exists3(candidate)) return candidate;
8059
8388
  }
8060
8389
  return null;
@@ -8065,7 +8394,7 @@ function hasNuxtDependency(pkg) {
8065
8394
  }
8066
8395
  async function findNuxtConfig(serviceDir) {
8067
8396
  for (const name of NUXT_CONFIG_CANDIDATES) {
8068
- const candidate = import_node_path42.default.join(serviceDir, name);
8397
+ const candidate = import_node_path44.default.join(serviceDir, name);
8069
8398
  if (await exists3(candidate)) return candidate;
8070
8399
  }
8071
8400
  return null;
@@ -8076,7 +8405,7 @@ function hasAstroDependency(pkg) {
8076
8405
  }
8077
8406
  async function findAstroConfig(serviceDir) {
8078
8407
  for (const name of ASTRO_CONFIG_CANDIDATES) {
8079
- const candidate = import_node_path42.default.join(serviceDir, name);
8408
+ const candidate = import_node_path44.default.join(serviceDir, name);
8080
8409
  if (await exists3(candidate)) return candidate;
8081
8410
  }
8082
8411
  return null;
@@ -8090,7 +8419,7 @@ function parseNextMajor(range) {
8090
8419
  return Number.isFinite(n) ? n : null;
8091
8420
  }
8092
8421
  async function isTypeScriptProject(serviceDir) {
8093
- return exists3(import_node_path42.default.join(serviceDir, "tsconfig.json"));
8422
+ return exists3(import_node_path44.default.join(serviceDir, "tsconfig.json"));
8094
8423
  }
8095
8424
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
8096
8425
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -8136,7 +8465,7 @@ function entryFromScript(script) {
8136
8465
  }
8137
8466
  async function resolveEntry(serviceDir, pkg) {
8138
8467
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
8139
- const candidate = import_node_path42.default.resolve(serviceDir, pkg.main);
8468
+ const candidate = import_node_path44.default.resolve(serviceDir, pkg.main);
8140
8469
  if (await exists3(candidate)) return candidate;
8141
8470
  }
8142
8471
  if (pkg.bin) {
@@ -8150,36 +8479,36 @@ async function resolveEntry(serviceDir, pkg) {
8150
8479
  if (typeof first === "string") binEntry = first;
8151
8480
  }
8152
8481
  if (binEntry) {
8153
- const candidate = import_node_path42.default.resolve(serviceDir, binEntry);
8482
+ const candidate = import_node_path44.default.resolve(serviceDir, binEntry);
8154
8483
  if (await exists3(candidate)) return candidate;
8155
8484
  }
8156
8485
  }
8157
8486
  const startEntry = entryFromScript(pkg.scripts?.start);
8158
8487
  if (startEntry) {
8159
- const candidate = import_node_path42.default.resolve(serviceDir, startEntry);
8488
+ const candidate = import_node_path44.default.resolve(serviceDir, startEntry);
8160
8489
  if (await exists3(candidate)) return candidate;
8161
8490
  }
8162
8491
  const devEntry = entryFromScript(pkg.scripts?.dev);
8163
8492
  if (devEntry) {
8164
- const candidate = import_node_path42.default.resolve(serviceDir, devEntry);
8493
+ const candidate = import_node_path44.default.resolve(serviceDir, devEntry);
8165
8494
  if (await exists3(candidate)) return candidate;
8166
8495
  }
8167
8496
  for (const rel of SRC_INDEX_CANDIDATES) {
8168
- const candidate = import_node_path42.default.join(serviceDir, rel);
8497
+ const candidate = import_node_path44.default.join(serviceDir, rel);
8169
8498
  if (await exists3(candidate)) return candidate;
8170
8499
  }
8171
8500
  for (const rel of SRC_NAMED_CANDIDATES) {
8172
- const candidate = import_node_path42.default.join(serviceDir, rel);
8501
+ const candidate = import_node_path44.default.join(serviceDir, rel);
8173
8502
  if (await exists3(candidate)) return candidate;
8174
8503
  }
8175
8504
  for (const name of INDEX_CANDIDATES) {
8176
- const candidate = import_node_path42.default.join(serviceDir, name);
8505
+ const candidate = import_node_path44.default.join(serviceDir, name);
8177
8506
  if (await exists3(candidate)) return candidate;
8178
8507
  }
8179
8508
  return null;
8180
8509
  }
8181
8510
  function dispatchEntry(entryFile, pkg) {
8182
- const ext = import_node_path42.default.extname(entryFile).toLowerCase();
8511
+ const ext = import_node_path44.default.extname(entryFile).toLowerCase();
8183
8512
  if (ext === ".ts" || ext === ".tsx") return "ts";
8184
8513
  if (ext === ".mjs") return "esm";
8185
8514
  if (ext === ".cjs") return "cjs";
@@ -8196,9 +8525,9 @@ function otelInitContents(flavor) {
8196
8525
  return OTEL_INIT_CJS;
8197
8526
  }
8198
8527
  function injectionLine(flavor, entryFile, otelInitFile) {
8199
- let rel = import_node_path42.default.relative(import_node_path42.default.dirname(entryFile), otelInitFile);
8528
+ let rel = import_node_path44.default.relative(import_node_path44.default.dirname(entryFile), otelInitFile);
8200
8529
  if (!rel.startsWith(".")) rel = `./${rel}`;
8201
- rel = rel.split(import_node_path42.default.sep).join("/");
8530
+ rel = rel.split(import_node_path44.default.sep).join("/");
8202
8531
  if (flavor === "cjs") return `require('${rel}')`;
8203
8532
  if (flavor === "esm") return `import '${rel}'`;
8204
8533
  const tsRel = rel.replace(/\.ts$/, "");
@@ -8211,23 +8540,23 @@ function lineIsOtelInjection(line) {
8211
8540
  }
8212
8541
  async function detectsSrcLayout(serviceDir) {
8213
8542
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
8214
- exists3(import_node_path42.default.join(serviceDir, "src", "app")),
8215
- exists3(import_node_path42.default.join(serviceDir, "src", "pages")),
8216
- exists3(import_node_path42.default.join(serviceDir, "app")),
8217
- exists3(import_node_path42.default.join(serviceDir, "pages"))
8543
+ exists3(import_node_path44.default.join(serviceDir, "src", "app")),
8544
+ exists3(import_node_path44.default.join(serviceDir, "src", "pages")),
8545
+ exists3(import_node_path44.default.join(serviceDir, "app")),
8546
+ exists3(import_node_path44.default.join(serviceDir, "pages"))
8218
8547
  ]);
8219
8548
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
8220
8549
  }
8221
8550
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
8222
8551
  const useTs = await isTypeScriptProject(serviceDir);
8223
8552
  const srcLayout = await detectsSrcLayout(serviceDir);
8224
- const baseDir = srcLayout ? import_node_path42.default.join(serviceDir, "src") : serviceDir;
8225
- const instrumentationFile = import_node_path42.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
8226
- const instrumentationNodeFile = import_node_path42.default.join(
8553
+ const baseDir = srcLayout ? import_node_path44.default.join(serviceDir, "src") : serviceDir;
8554
+ const instrumentationFile = import_node_path44.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
8555
+ const instrumentationNodeFile = import_node_path44.default.join(
8227
8556
  baseDir,
8228
8557
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
8229
8558
  );
8230
- const envNeatFile = import_node_path42.default.join(baseDir, ".env.neat");
8559
+ const envNeatFile = import_node_path44.default.join(baseDir, ".env.neat");
8231
8560
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
8232
8561
  const dependencyEdits = [];
8233
8562
  for (const sdk of SDK_PACKAGES) {
@@ -8284,7 +8613,7 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
8284
8613
  const nextMajor = parseNextMajor(nextRange);
8285
8614
  if (nextMajor !== null && nextMajor < 15) {
8286
8615
  try {
8287
- const raw = await import_node_fs27.promises.readFile(nextConfigPath, "utf8");
8616
+ const raw = await import_node_fs28.promises.readFile(nextConfigPath, "utf8");
8288
8617
  if (!raw.includes("instrumentationHook")) {
8289
8618
  nextConfigEdit = {
8290
8619
  file: nextConfigPath,
@@ -8332,7 +8661,7 @@ function buildDependencyEdits(pkg, manifestPath) {
8332
8661
  return edits;
8333
8662
  }
8334
8663
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
8335
- const envNeatFile = import_node_path42.default.join(serviceDir, ".env.neat");
8664
+ const envNeatFile = import_node_path44.default.join(serviceDir, ".env.neat");
8336
8665
  if (!await exists3(envNeatFile)) {
8337
8666
  generatedFiles.push({
8338
8667
  file: envNeatFile,
@@ -8367,7 +8696,7 @@ function fileImportsOtelHook(raw, specifiers) {
8367
8696
  }
8368
8697
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
8369
8698
  const useTs = await isTypeScriptProject(serviceDir);
8370
- const otelServerFile = import_node_path42.default.join(
8699
+ const otelServerFile = import_node_path44.default.join(
8371
8700
  serviceDir,
8372
8701
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
8373
8702
  );
@@ -8388,7 +8717,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
8388
8717
  await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
8389
8718
  const entrypointEdits = [];
8390
8719
  try {
8391
- const raw = await import_node_fs27.promises.readFile(entryFile, "utf8");
8720
+ const raw = await import_node_fs28.promises.readFile(entryFile, "utf8");
8392
8721
  if (!fileImportsOtelHook(raw, ["./otel.server"])) {
8393
8722
  const lines = raw.split(/\r?\n/);
8394
8723
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
@@ -8424,11 +8753,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
8424
8753
  }
8425
8754
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
8426
8755
  const useTs = await isTypeScriptProject(serviceDir);
8427
- const otelInitFile = import_node_path42.default.join(
8756
+ const otelInitFile = import_node_path44.default.join(
8428
8757
  serviceDir,
8429
8758
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
8430
8759
  );
8431
- const resolvedHooksFile = hooksFile ?? import_node_path42.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
8760
+ const resolvedHooksFile = hooksFile ?? import_node_path44.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
8432
8761
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
8433
8762
  const generatedFiles = [];
8434
8763
  const entrypointEdits = [];
@@ -8453,7 +8782,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
8453
8782
  });
8454
8783
  } else {
8455
8784
  try {
8456
- const raw = await import_node_fs27.promises.readFile(hooksFile, "utf8");
8785
+ const raw = await import_node_fs28.promises.readFile(hooksFile, "utf8");
8457
8786
  if (!fileImportsOtelHook(raw, ["./otel-init"])) {
8458
8787
  const lines = raw.split(/\r?\n/);
8459
8788
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
@@ -8490,11 +8819,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
8490
8819
  }
8491
8820
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
8492
8821
  const useTs = await isTypeScriptProject(serviceDir);
8493
- const otelPluginFile = import_node_path42.default.join(
8822
+ const otelPluginFile = import_node_path44.default.join(
8494
8823
  serviceDir,
8495
8824
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
8496
8825
  );
8497
- const otelInitFile = import_node_path42.default.join(
8826
+ const otelInitFile = import_node_path44.default.join(
8498
8827
  serviceDir,
8499
8828
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
8500
8829
  );
@@ -8545,19 +8874,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
8545
8874
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
8546
8875
  async function findAstroMiddleware(serviceDir) {
8547
8876
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
8548
- const candidate = import_node_path42.default.join(serviceDir, rel);
8877
+ const candidate = import_node_path44.default.join(serviceDir, rel);
8549
8878
  if (await exists3(candidate)) return candidate;
8550
8879
  }
8551
8880
  return null;
8552
8881
  }
8553
8882
  async function planAstro(serviceDir, pkg, manifestPath, project) {
8554
8883
  const useTs = await isTypeScriptProject(serviceDir);
8555
- const otelInitFile = import_node_path42.default.join(
8884
+ const otelInitFile = import_node_path44.default.join(
8556
8885
  serviceDir,
8557
8886
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
8558
8887
  );
8559
8888
  const existingMiddleware = await findAstroMiddleware(serviceDir);
8560
- const middlewareFile = existingMiddleware ?? import_node_path42.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
8889
+ const middlewareFile = existingMiddleware ?? import_node_path44.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
8561
8890
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
8562
8891
  const generatedFiles = [];
8563
8892
  const entrypointEdits = [];
@@ -8582,7 +8911,7 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
8582
8911
  });
8583
8912
  } else {
8584
8913
  try {
8585
- const raw = await import_node_fs27.promises.readFile(existingMiddleware, "utf8");
8914
+ const raw = await import_node_fs28.promises.readFile(existingMiddleware, "utf8");
8586
8915
  if (!fileImportsOtelHook(raw, ["./otel-init"])) {
8587
8916
  const lines = raw.split(/\r?\n/);
8588
8917
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
@@ -8653,7 +8982,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
8653
8982
  }
8654
8983
  async function plan(serviceDir, opts) {
8655
8984
  const pkg = await readPackageJson2(serviceDir);
8656
- const manifestPath = import_node_path42.default.join(serviceDir, "package.json");
8985
+ const manifestPath = import_node_path44.default.join(serviceDir, "package.json");
8657
8986
  const project = opts?.project;
8658
8987
  const empty = {
8659
8988
  language: "javascript",
@@ -8688,8 +9017,8 @@ async function plan(serviceDir, opts) {
8688
9017
  return { ...empty, libOnly: true };
8689
9018
  }
8690
9019
  const flavor = dispatchEntry(entryFile, pkg);
8691
- const otelInitFile = import_node_path42.default.join(import_node_path42.default.dirname(entryFile), otelInitFilename(flavor));
8692
- const envNeatFile = import_node_path42.default.join(serviceDir, ".env.neat");
9020
+ const otelInitFile = import_node_path44.default.join(import_node_path44.default.dirname(entryFile), otelInitFilename(flavor));
9021
+ const envNeatFile = import_node_path44.default.join(serviceDir, ".env.neat");
8693
9022
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
8694
9023
  const dependencyEdits = [];
8695
9024
  for (const sdk of SDK_PACKAGES) {
@@ -8713,7 +9042,7 @@ async function plan(serviceDir, opts) {
8713
9042
  }
8714
9043
  const entrypointEdits = [];
8715
9044
  try {
8716
- const raw = await import_node_fs27.promises.readFile(entryFile, "utf8");
9045
+ const raw = await import_node_fs28.promises.readFile(entryFile, "utf8");
8717
9046
  const lines = raw.split(/\r?\n/);
8718
9047
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
8719
9048
  if (!lineIsOtelInjection(firstReal)) {
@@ -8758,13 +9087,13 @@ async function plan(serviceDir, opts) {
8758
9087
  };
8759
9088
  }
8760
9089
  function isAllowedWritePath(serviceDir, target) {
8761
- const rel = import_node_path42.default.relative(serviceDir, target);
9090
+ const rel = import_node_path44.default.relative(serviceDir, target);
8762
9091
  if (rel.startsWith("..")) return false;
8763
- const base = import_node_path42.default.basename(target);
9092
+ const base = import_node_path44.default.basename(target);
8764
9093
  if (base === "package.json") return true;
8765
9094
  if (base === ".env.neat") return true;
8766
9095
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
8767
- const relPosix = rel.split(import_node_path42.default.sep).join("/");
9096
+ const relPosix = rel.split(import_node_path44.default.sep).join("/");
8768
9097
  if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
8769
9098
  if (relPosix === base) return true;
8770
9099
  if (relPosix === `src/${base}`) return true;
@@ -8781,10 +9110,10 @@ function isAllowedWritePath(serviceDir, target) {
8781
9110
  return false;
8782
9111
  }
8783
9112
  async function writeAtomic(file, contents) {
8784
- await import_node_fs27.promises.mkdir(import_node_path42.default.dirname(file), { recursive: true });
9113
+ await import_node_fs28.promises.mkdir(import_node_path44.default.dirname(file), { recursive: true });
8785
9114
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
8786
- await import_node_fs27.promises.writeFile(tmp, contents, "utf8");
8787
- await import_node_fs27.promises.rename(tmp, file);
9115
+ await import_node_fs28.promises.writeFile(tmp, contents, "utf8");
9116
+ await import_node_fs28.promises.rename(tmp, file);
8788
9117
  }
8789
9118
  async function apply(installPlan) {
8790
9119
  const { serviceDir } = installPlan;
@@ -8860,7 +9189,7 @@ async function apply(installPlan) {
8860
9189
  for (const target of allTargets) {
8861
9190
  if (await exists3(target)) {
8862
9191
  try {
8863
- originals.set(target, await import_node_fs27.promises.readFile(target, "utf8"));
9192
+ originals.set(target, await import_node_fs28.promises.readFile(target, "utf8"));
8864
9193
  } catch {
8865
9194
  }
8866
9195
  }
@@ -8943,14 +9272,14 @@ async function rollback(installPlan, originals, createdFiles) {
8943
9272
  const removed = [];
8944
9273
  for (const [file, raw] of originals.entries()) {
8945
9274
  try {
8946
- await import_node_fs27.promises.writeFile(file, raw, "utf8");
9275
+ await import_node_fs28.promises.writeFile(file, raw, "utf8");
8947
9276
  restored.push(file);
8948
9277
  } catch {
8949
9278
  }
8950
9279
  }
8951
9280
  for (const file of createdFiles) {
8952
9281
  try {
8953
- await import_node_fs27.promises.unlink(file);
9282
+ await import_node_fs28.promises.unlink(file);
8954
9283
  removed.push(file);
8955
9284
  } catch {
8956
9285
  }
@@ -8965,8 +9294,8 @@ async function rollback(installPlan, originals, createdFiles) {
8965
9294
  ...removed.map((f) => `removed: ${f}`),
8966
9295
  ""
8967
9296
  ];
8968
- const rollbackPath = import_node_path42.default.join(installPlan.serviceDir, "neat-rollback.patch");
8969
- await import_node_fs27.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9297
+ const rollbackPath = import_node_path44.default.join(installPlan.serviceDir, "neat-rollback.patch");
9298
+ await import_node_fs28.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
8970
9299
  }
8971
9300
  function injectInstrumentationHook(raw) {
8972
9301
  if (raw.includes("instrumentationHook")) return raw;
@@ -8995,8 +9324,8 @@ var javascriptInstaller = {
8995
9324
 
8996
9325
  // src/installers/python.ts
8997
9326
  init_cjs_shims();
8998
- var import_node_fs28 = require("fs");
8999
- var import_node_path43 = __toESM(require("path"), 1);
9327
+ var import_node_fs29 = require("fs");
9328
+ var import_node_path45 = __toESM(require("path"), 1);
9000
9329
  var SDK_PACKAGES2 = [
9001
9330
  { name: "opentelemetry-distro", version: ">=0.49b0" },
9002
9331
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -9008,7 +9337,7 @@ var OTEL_ENV2 = {
9008
9337
  };
9009
9338
  async function exists4(p) {
9010
9339
  try {
9011
- await import_node_fs28.promises.stat(p);
9340
+ await import_node_fs29.promises.stat(p);
9012
9341
  return true;
9013
9342
  } catch {
9014
9343
  return false;
@@ -9017,7 +9346,7 @@ async function exists4(p) {
9017
9346
  async function detect2(serviceDir) {
9018
9347
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
9019
9348
  for (const m of markers) {
9020
- if (await exists4(import_node_path43.default.join(serviceDir, m))) return true;
9349
+ if (await exists4(import_node_path45.default.join(serviceDir, m))) return true;
9021
9350
  }
9022
9351
  return false;
9023
9352
  }
@@ -9027,9 +9356,9 @@ function reqPackageName(line) {
9027
9356
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
9028
9357
  }
9029
9358
  async function planRequirementsTxtEdits(serviceDir) {
9030
- const file = import_node_path43.default.join(serviceDir, "requirements.txt");
9359
+ const file = import_node_path45.default.join(serviceDir, "requirements.txt");
9031
9360
  if (!await exists4(file)) return null;
9032
- const raw = await import_node_fs28.promises.readFile(file, "utf8");
9361
+ const raw = await import_node_fs29.promises.readFile(file, "utf8");
9033
9362
  const presentNames = new Set(
9034
9363
  raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
9035
9364
  );
@@ -9037,9 +9366,9 @@ async function planRequirementsTxtEdits(serviceDir) {
9037
9366
  return { manifest: file, missing: [...missing] };
9038
9367
  }
9039
9368
  async function planProcfileEdits(serviceDir) {
9040
- const procfile = import_node_path43.default.join(serviceDir, "Procfile");
9369
+ const procfile = import_node_path45.default.join(serviceDir, "Procfile");
9041
9370
  if (!await exists4(procfile)) return [];
9042
- const raw = await import_node_fs28.promises.readFile(procfile, "utf8");
9371
+ const raw = await import_node_fs29.promises.readFile(procfile, "utf8");
9043
9372
  const edits = [];
9044
9373
  for (const line of raw.split(/\r?\n/)) {
9045
9374
  if (line.length === 0) continue;
@@ -9091,8 +9420,8 @@ async function applyRequirementsTxt(manifest, edits, original) {
9091
9420
  const next = `${original}${trailing}${newlines.join("\n")}
9092
9421
  `;
9093
9422
  const tmp = `${manifest}.${process.pid}.${Date.now()}.tmp`;
9094
- await import_node_fs28.promises.writeFile(tmp, next, "utf8");
9095
- await import_node_fs28.promises.rename(tmp, manifest);
9423
+ await import_node_fs29.promises.writeFile(tmp, next, "utf8");
9424
+ await import_node_fs29.promises.rename(tmp, manifest);
9096
9425
  }
9097
9426
  async function applyProcfile(procfile, edits, original) {
9098
9427
  let next = original;
@@ -9101,8 +9430,8 @@ async function applyProcfile(procfile, edits, original) {
9101
9430
  next = next.replace(e.before, e.after);
9102
9431
  }
9103
9432
  const tmp = `${procfile}.${process.pid}.${Date.now()}.tmp`;
9104
- await import_node_fs28.promises.writeFile(tmp, next, "utf8");
9105
- await import_node_fs28.promises.rename(tmp, procfile);
9433
+ await import_node_fs29.promises.writeFile(tmp, next, "utf8");
9434
+ await import_node_fs29.promises.rename(tmp, procfile);
9106
9435
  }
9107
9436
  async function apply2(installPlan) {
9108
9437
  const { serviceDir } = installPlan;
@@ -9115,7 +9444,7 @@ async function apply2(installPlan) {
9115
9444
  const originals = /* @__PURE__ */ new Map();
9116
9445
  for (const file of touched) {
9117
9446
  try {
9118
- originals.set(file, await import_node_fs28.promises.readFile(file, "utf8"));
9447
+ originals.set(file, await import_node_fs29.promises.readFile(file, "utf8"));
9119
9448
  } catch {
9120
9449
  }
9121
9450
  }
@@ -9126,7 +9455,7 @@ async function apply2(installPlan) {
9126
9455
  if (raw === void 0) {
9127
9456
  throw new Error(`python installer: cannot read ${file} during apply`);
9128
9457
  }
9129
- const base = import_node_path43.default.basename(file);
9458
+ const base = import_node_path45.default.basename(file);
9130
9459
  if (base === "requirements.txt") {
9131
9460
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
9132
9461
  if (edits.length > 0) {
@@ -9151,7 +9480,7 @@ async function rollback2(installPlan, originals) {
9151
9480
  const restored = [];
9152
9481
  for (const [file, raw] of originals.entries()) {
9153
9482
  try {
9154
- await import_node_fs28.promises.writeFile(file, raw, "utf8");
9483
+ await import_node_fs29.promises.writeFile(file, raw, "utf8");
9155
9484
  restored.push(file);
9156
9485
  } catch {
9157
9486
  }
@@ -9165,8 +9494,8 @@ async function rollback2(installPlan, originals) {
9165
9494
  ...restored.map((f) => `restored: ${f}`),
9166
9495
  ""
9167
9496
  ];
9168
- const rollbackPath = import_node_path43.default.join(installPlan.serviceDir, "neat-rollback.patch");
9169
- await import_node_fs28.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9497
+ const rollbackPath = import_node_path45.default.join(installPlan.serviceDir, "neat-rollback.patch");
9498
+ await import_node_fs29.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9170
9499
  }
9171
9500
  var pythonInstaller = {
9172
9501
  name: "python",
@@ -9289,10 +9618,10 @@ function renderPatch(sections) {
9289
9618
 
9290
9619
  // src/orchestrator.ts
9291
9620
  init_cjs_shims();
9292
- var import_node_fs29 = require("fs");
9621
+ var import_node_fs30 = require("fs");
9293
9622
  var import_node_http = __toESM(require("http"), 1);
9294
9623
  var import_node_net = __toESM(require("net"), 1);
9295
- var import_node_path44 = __toESM(require("path"), 1);
9624
+ var import_node_path46 = __toESM(require("path"), 1);
9296
9625
  var import_node_child_process3 = require("child_process");
9297
9626
  var import_node_readline = __toESM(require("readline"), 1);
9298
9627
  async function extractAndPersist(opts) {
@@ -9301,7 +9630,7 @@ async function extractAndPersist(opts) {
9301
9630
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
9302
9631
  resetGraph(graphKey);
9303
9632
  const graph = getGraph(graphKey);
9304
- const projectPaths = pathsForProject(graphKey, import_node_path44.default.join(opts.scanPath, "neat-out"));
9633
+ const projectPaths = pathsForProject(graphKey, import_node_path46.default.join(opts.scanPath, "neat-out"));
9305
9634
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
9306
9635
  errorsPath: projectPaths.errorsPath
9307
9636
  });
@@ -9355,7 +9684,7 @@ async function applyInstallersOver(services, project, options = {}) {
9355
9684
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
9356
9685
  } else if (outcome.outcome === "react-native") {
9357
9686
  reactNative++;
9358
- const svcName = import_node_path44.default.basename(svc.dir);
9687
+ const svcName = import_node_path46.default.basename(svc.dir);
9359
9688
  console.log(
9360
9689
  `neat: ${svc.dir} detected as React Native / Expo
9361
9690
  The installer doesn't cover this runtime deterministically.
@@ -9366,7 +9695,7 @@ async function applyInstallersOver(services, project, options = {}) {
9366
9695
  );
9367
9696
  } else if (outcome.outcome === "bun") {
9368
9697
  bun++;
9369
- const svcName = import_node_path44.default.basename(svc.dir);
9698
+ const svcName = import_node_path46.default.basename(svc.dir);
9370
9699
  console.log(
9371
9700
  `neat: ${svc.dir} detected as Bun
9372
9701
  The installer doesn't cover this runtime deterministically.
@@ -9377,7 +9706,7 @@ async function applyInstallersOver(services, project, options = {}) {
9377
9706
  );
9378
9707
  } else if (outcome.outcome === "deno") {
9379
9708
  deno++;
9380
- const svcName = import_node_path44.default.basename(svc.dir);
9709
+ const svcName = import_node_path46.default.basename(svc.dir);
9381
9710
  console.log(
9382
9711
  `neat: ${svc.dir} detected as Deno
9383
9712
  The installer doesn't cover this runtime deterministically.
@@ -9388,7 +9717,7 @@ async function applyInstallersOver(services, project, options = {}) {
9388
9717
  );
9389
9718
  } else if (outcome.outcome === "cloudflare-workers") {
9390
9719
  cloudflareWorkers++;
9391
- const svcName = import_node_path44.default.basename(svc.dir);
9720
+ const svcName = import_node_path46.default.basename(svc.dir);
9392
9721
  console.log(
9393
9722
  `neat: ${svc.dir} detected as Cloudflare Workers
9394
9723
  The installer doesn't cover this runtime deterministically.
@@ -9399,7 +9728,7 @@ async function applyInstallersOver(services, project, options = {}) {
9399
9728
  );
9400
9729
  } else if (outcome.outcome === "electron") {
9401
9730
  electron++;
9402
- const svcName = import_node_path44.default.basename(svc.dir);
9731
+ const svcName = import_node_path46.default.basename(svc.dir);
9403
9732
  console.log(
9404
9733
  `neat: ${svc.dir} detected as Electron
9405
9734
  The installer doesn't cover this runtime deterministically.
@@ -9573,10 +9902,10 @@ function formatPortCollisionMessage(port) {
9573
9902
  ];
9574
9903
  }
9575
9904
  function spawnDaemonDetached() {
9576
- const here = import_node_path44.default.dirname(new URL(importMetaUrl).pathname);
9905
+ const here = import_node_path46.default.dirname(new URL(importMetaUrl).pathname);
9577
9906
  const candidates = [
9578
- import_node_path44.default.join(here, "neatd.cjs"),
9579
- import_node_path44.default.join(here, "neatd.js")
9907
+ import_node_path46.default.join(here, "neatd.cjs"),
9908
+ import_node_path46.default.join(here, "neatd.js")
9580
9909
  ];
9581
9910
  let entry2 = null;
9582
9911
  const fsSync = require("fs");
@@ -9634,7 +9963,7 @@ async function runOrchestrator(opts) {
9634
9963
  browser: "skipped"
9635
9964
  }
9636
9965
  };
9637
- const stat = await import_node_fs29.promises.stat(opts.scanPath).catch(() => null);
9966
+ const stat = await import_node_fs30.promises.stat(opts.scanPath).catch(() => null);
9638
9967
  if (!stat || !stat.isDirectory()) {
9639
9968
  console.error(`neat: ${opts.scanPath} is not a directory`);
9640
9969
  result.exitCode = 2;
@@ -9777,11 +10106,11 @@ function printSummary(result, graph, dashboardUrl) {
9777
10106
 
9778
10107
  // src/cli-verbs.ts
9779
10108
  init_cjs_shims();
9780
- var import_node_path45 = __toESM(require("path"), 1);
10109
+ var import_node_path47 = __toESM(require("path"), 1);
9781
10110
 
9782
10111
  // src/cli-client.ts
9783
10112
  init_cjs_shims();
9784
- var import_types25 = require("@neat.is/types");
10113
+ var import_types26 = require("@neat.is/types");
9785
10114
  var HttpError = class extends Error {
9786
10115
  constructor(status2, message, responseBody = "") {
9787
10116
  super(message);
@@ -9806,10 +10135,10 @@ function createHttpClient(baseUrl, bearerToken) {
9806
10135
  const root = baseUrl.replace(/\/$/, "");
9807
10136
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
9808
10137
  return {
9809
- async get(path47) {
10138
+ async get(path49) {
9810
10139
  let res;
9811
10140
  try {
9812
- res = await fetch(`${root}${path47}`, {
10141
+ res = await fetch(`${root}${path49}`, {
9813
10142
  headers: { ...authHeader }
9814
10143
  });
9815
10144
  } catch (err) {
@@ -9821,16 +10150,16 @@ function createHttpClient(baseUrl, bearerToken) {
9821
10150
  const body = await res.text().catch(() => "");
9822
10151
  throw new HttpError(
9823
10152
  res.status,
9824
- `${res.status} ${res.statusText} on GET ${path47}: ${body}`,
10153
+ `${res.status} ${res.statusText} on GET ${path49}: ${body}`,
9825
10154
  body
9826
10155
  );
9827
10156
  }
9828
10157
  return await res.json();
9829
10158
  },
9830
- async post(path47, body) {
10159
+ async post(path49, body) {
9831
10160
  let res;
9832
10161
  try {
9833
- res = await fetch(`${root}${path47}`, {
10162
+ res = await fetch(`${root}${path49}`, {
9834
10163
  method: "POST",
9835
10164
  headers: { "content-type": "application/json", ...authHeader },
9836
10165
  body: JSON.stringify(body)
@@ -9844,7 +10173,7 @@ function createHttpClient(baseUrl, bearerToken) {
9844
10173
  const text = await res.text().catch(() => "");
9845
10174
  throw new HttpError(
9846
10175
  res.status,
9847
- `${res.status} ${res.statusText} on POST ${path47}: ${text}`,
10176
+ `${res.status} ${res.statusText} on POST ${path49}: ${text}`,
9848
10177
  text
9849
10178
  );
9850
10179
  }
@@ -9858,12 +10187,12 @@ function projectPath(project, suffix) {
9858
10187
  }
9859
10188
  async function runRootCause(client, input) {
9860
10189
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
9861
- const path47 = projectPath(
10190
+ const path49 = projectPath(
9862
10191
  input.project,
9863
10192
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
9864
10193
  );
9865
10194
  try {
9866
- const result = await client.get(path47);
10195
+ const result = await client.get(path49);
9867
10196
  const arrowPath = result.traversalPath.join(" \u2190 ");
9868
10197
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
9869
10198
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -9889,12 +10218,12 @@ async function runRootCause(client, input) {
9889
10218
  }
9890
10219
  async function runBlastRadius(client, input) {
9891
10220
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
9892
- const path47 = projectPath(
10221
+ const path49 = projectPath(
9893
10222
  input.project,
9894
10223
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
9895
10224
  );
9896
10225
  try {
9897
- const result = await client.get(path47);
10226
+ const result = await client.get(path49);
9898
10227
  if (result.totalAffected === 0) {
9899
10228
  return {
9900
10229
  summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
@@ -9923,17 +10252,17 @@ async function runBlastRadius(client, input) {
9923
10252
  }
9924
10253
  }
9925
10254
  function formatBlastEntry(n) {
9926
- const tag = n.edgeProvenance === import_types25.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
10255
+ const tag = n.edgeProvenance === import_types26.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
9927
10256
  return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
9928
10257
  }
9929
10258
  async function runDependencies(client, input) {
9930
10259
  const depth = input.depth ?? 3;
9931
- const path47 = projectPath(
10260
+ const path49 = projectPath(
9932
10261
  input.project,
9933
10262
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
9934
10263
  );
9935
10264
  try {
9936
- const result = await client.get(path47);
10265
+ const result = await client.get(path49);
9937
10266
  if (result.total === 0) {
9938
10267
  return {
9939
10268
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -9969,9 +10298,9 @@ async function runObservedDependencies(client, input) {
9969
10298
  const edges = await client.get(
9970
10299
  projectPath(input.project, `/graph/edges/${encodeURIComponent(input.nodeId)}`)
9971
10300
  );
9972
- const observed = edges.outbound.filter((e) => e.provenance === import_types25.Provenance.OBSERVED);
10301
+ const observed = edges.outbound.filter((e) => e.provenance === import_types26.Provenance.OBSERVED);
9973
10302
  if (observed.length === 0) {
9974
- const hasExtracted = edges.outbound.some((e) => e.provenance === import_types25.Provenance.EXTRACTED);
10303
+ const hasExtracted = edges.outbound.some((e) => e.provenance === import_types26.Provenance.EXTRACTED);
9975
10304
  const note = hasExtracted ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
9976
10305
  return { summary: `No OBSERVED dependencies for ${input.nodeId}.${note}` };
9977
10306
  }
@@ -9979,7 +10308,7 @@ async function runObservedDependencies(client, input) {
9979
10308
  return {
9980
10309
  summary: `${input.nodeId} has ${observed.length} runtime dependenc${observed.length === 1 ? "y" : "ies"} confirmed by OTel.`,
9981
10310
  block: blockLines.join("\n"),
9982
- provenance: import_types25.Provenance.OBSERVED
10311
+ provenance: import_types26.Provenance.OBSERVED
9983
10312
  };
9984
10313
  } catch (err) {
9985
10314
  if (err instanceof HttpError && err.status === 404) {
@@ -10014,9 +10343,9 @@ function formatDuration(ms) {
10014
10343
  return `${Math.round(h / 24)}d`;
10015
10344
  }
10016
10345
  async function runIncidents(client, input) {
10017
- const path47 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
10346
+ const path49 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
10018
10347
  try {
10019
- const body = await client.get(path47);
10348
+ const body = await client.get(path49);
10020
10349
  const events = body.events;
10021
10350
  if (events.length === 0) {
10022
10351
  return {
@@ -10033,7 +10362,7 @@ async function runIncidents(client, input) {
10033
10362
  return {
10034
10363
  summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
10035
10364
  block: blockLines.join("\n"),
10036
- provenance: import_types25.Provenance.OBSERVED
10365
+ provenance: import_types26.Provenance.OBSERVED
10037
10366
  };
10038
10367
  } catch (err) {
10039
10368
  if (err instanceof HttpError && err.status === 404) {
@@ -10142,7 +10471,7 @@ async function runStaleEdges(client, input) {
10142
10471
  return {
10143
10472
  summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
10144
10473
  block: blockLines.join("\n"),
10145
- provenance: import_types25.Provenance.STALE
10474
+ provenance: import_types26.Provenance.STALE
10146
10475
  };
10147
10476
  }
10148
10477
  async function runPolicies(client, input) {
@@ -10306,7 +10635,7 @@ async function resolveProjectEntry(opts) {
10306
10635
  const cwd = opts.cwd ?? process.cwd();
10307
10636
  const resolvedCwd = await normalizeProjectPath(cwd);
10308
10637
  for (const entry2 of entries) {
10309
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path45.default.sep}`)) {
10638
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path47.default.sep}`)) {
10310
10639
  return entry2;
10311
10640
  }
10312
10641
  }
@@ -10456,7 +10785,7 @@ async function runSync(opts) {
10456
10785
  }
10457
10786
 
10458
10787
  // src/cli.ts
10459
- var import_types26 = require("@neat.is/types");
10788
+ var import_types27 = require("@neat.is/types");
10460
10789
  function usage() {
10461
10790
  console.log("usage: neat <command> [args] [--project <name>]");
10462
10791
  console.log("");
@@ -10666,14 +10995,14 @@ function assignFlag(out, field, value) {
10666
10995
  out[field] = value;
10667
10996
  }
10668
10997
  function readPackageVersion() {
10669
- const here = typeof __dirname !== "undefined" ? __dirname : import_node_path46.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
10998
+ const here = typeof __dirname !== "undefined" ? __dirname : import_node_path48.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
10670
10999
  const candidates = [
10671
- import_node_path46.default.resolve(here, "../package.json"),
10672
- import_node_path46.default.resolve(here, "../../package.json")
11000
+ import_node_path48.default.resolve(here, "../package.json"),
11001
+ import_node_path48.default.resolve(here, "../../package.json")
10673
11002
  ];
10674
11003
  for (const candidate of candidates) {
10675
11004
  try {
10676
- const raw = (0, import_node_fs30.readFileSync)(candidate, "utf8");
11005
+ const raw = (0, import_node_fs31.readFileSync)(candidate, "utf8");
10677
11006
  const parsed = JSON.parse(raw);
10678
11007
  if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
10679
11008
  return parsed.version;
@@ -10737,7 +11066,7 @@ async function buildPatchSections(services, project) {
10737
11066
  }
10738
11067
  async function runInit(opts) {
10739
11068
  const written = [];
10740
- const stat = await import_node_fs30.promises.stat(opts.scanPath).catch(() => null);
11069
+ const stat = await import_node_fs31.promises.stat(opts.scanPath).catch(() => null);
10741
11070
  if (!stat || !stat.isDirectory()) {
10742
11071
  console.error(`neat init: ${opts.scanPath} is not a directory`);
10743
11072
  return { exitCode: 2, writtenFiles: written };
@@ -10746,13 +11075,13 @@ async function runInit(opts) {
10746
11075
  printDiscoveryReport(opts, services);
10747
11076
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
10748
11077
  const patch = renderPatch(sections);
10749
- const patchPath = import_node_path46.default.join(opts.scanPath, "neat.patch");
11078
+ const patchPath = import_node_path48.default.join(opts.scanPath, "neat.patch");
10750
11079
  if (opts.dryRun) {
10751
- await import_node_fs30.promises.writeFile(patchPath, patch, "utf8");
11080
+ await import_node_fs31.promises.writeFile(patchPath, patch, "utf8");
10752
11081
  written.push(patchPath);
10753
11082
  console.log(`dry-run: patch written to ${patchPath}`);
10754
- const gitignorePath = import_node_path46.default.join(opts.scanPath, ".gitignore");
10755
- const gitignoreExists = await import_node_fs30.promises.stat(gitignorePath).then(() => true).catch(() => false);
11083
+ const gitignorePath = import_node_path48.default.join(opts.scanPath, ".gitignore");
11084
+ const gitignoreExists = await import_node_fs31.promises.stat(gitignorePath).then(() => true).catch(() => false);
10756
11085
  const verb = gitignoreExists ? "append" : "create";
10757
11086
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
10758
11087
  console.log("rerun without --dry-run to register and snapshot.");
@@ -10763,9 +11092,9 @@ async function runInit(opts) {
10763
11092
  const graph = getGraph(graphKey);
10764
11093
  const projectPaths = pathsForProject(
10765
11094
  graphKey,
10766
- import_node_path46.default.join(opts.scanPath, "neat-out")
11095
+ import_node_path48.default.join(opts.scanPath, "neat-out")
10767
11096
  );
10768
- const errorsPath = import_node_path46.default.join(import_node_path46.default.dirname(opts.outPath), import_node_path46.default.basename(projectPaths.errorsPath));
11097
+ const errorsPath = import_node_path48.default.join(import_node_path48.default.dirname(opts.outPath), import_node_path48.default.basename(projectPaths.errorsPath));
10769
11098
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
10770
11099
  await saveGraphToDisk(graph, opts.outPath);
10771
11100
  written.push(opts.outPath);
@@ -10844,7 +11173,7 @@ async function runInit(opts) {
10844
11173
  console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
10845
11174
  }
10846
11175
  } else {
10847
- await import_node_fs30.promises.writeFile(patchPath, patch, "utf8");
11176
+ await import_node_fs31.promises.writeFile(patchPath, patch, "utf8");
10848
11177
  written.push(patchPath);
10849
11178
  }
10850
11179
  }
@@ -10884,9 +11213,9 @@ var CLAUDE_SKILL_CONFIG = {
10884
11213
  };
10885
11214
  function claudeConfigPath() {
10886
11215
  const override = process.env.NEAT_CLAUDE_CONFIG;
10887
- if (override && override.length > 0) return import_node_path46.default.resolve(override);
11216
+ if (override && override.length > 0) return import_node_path48.default.resolve(override);
10888
11217
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
10889
- return import_node_path46.default.join(home, ".claude.json");
11218
+ return import_node_path48.default.join(home, ".claude.json");
10890
11219
  }
10891
11220
  async function runSkill(opts) {
10892
11221
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -10898,7 +11227,7 @@ async function runSkill(opts) {
10898
11227
  const target = claudeConfigPath();
10899
11228
  let existing = {};
10900
11229
  try {
10901
- existing = JSON.parse(await import_node_fs30.promises.readFile(target, "utf8"));
11230
+ existing = JSON.parse(await import_node_fs31.promises.readFile(target, "utf8"));
10902
11231
  } catch (err) {
10903
11232
  if (err.code !== "ENOENT") {
10904
11233
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -10910,8 +11239,8 @@ async function runSkill(opts) {
10910
11239
  ...existing,
10911
11240
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
10912
11241
  };
10913
- await import_node_fs30.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
10914
- await import_node_fs30.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
11242
+ await import_node_fs31.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
11243
+ await import_node_fs31.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
10915
11244
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
10916
11245
  console.log("restart Claude Code to pick up the new MCP server.");
10917
11246
  return { exitCode: 0 };
@@ -10949,12 +11278,12 @@ async function main() {
10949
11278
  console.error("neat init: --apply and --dry-run are mutually exclusive");
10950
11279
  process.exit(2);
10951
11280
  }
10952
- const scanPath = import_node_path46.default.resolve(target);
11281
+ const scanPath = import_node_path48.default.resolve(target);
10953
11282
  const projectExplicit = parsed.project !== null;
10954
- const projectName = projectExplicit ? project : import_node_path46.default.basename(scanPath);
11283
+ const projectName = projectExplicit ? project : import_node_path48.default.basename(scanPath);
10955
11284
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
10956
- const fallback = pathsForProject(projectKey, import_node_path46.default.join(scanPath, "neat-out")).snapshotPath;
10957
- const outPath = import_node_path46.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
11285
+ const fallback = pathsForProject(projectKey, import_node_path48.default.join(scanPath, "neat-out")).snapshotPath;
11286
+ const outPath = import_node_path48.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
10958
11287
  const result = await runInit({
10959
11288
  scanPath,
10960
11289
  outPath,
@@ -10975,21 +11304,21 @@ async function main() {
10975
11304
  usage();
10976
11305
  process.exit(2);
10977
11306
  }
10978
- const scanPath = import_node_path46.default.resolve(target);
10979
- const stat = await import_node_fs30.promises.stat(scanPath).catch(() => null);
11307
+ const scanPath = import_node_path48.default.resolve(target);
11308
+ const stat = await import_node_fs31.promises.stat(scanPath).catch(() => null);
10980
11309
  if (!stat || !stat.isDirectory()) {
10981
11310
  console.error(`neat watch: ${scanPath} is not a directory`);
10982
11311
  process.exit(2);
10983
11312
  }
10984
- const projectPaths = pathsForProject(project, import_node_path46.default.join(scanPath, "neat-out"));
10985
- const outPath = import_node_path46.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
10986
- const errorsPath = import_node_path46.default.resolve(
10987
- process.env.NEAT_ERRORS_PATH ?? import_node_path46.default.join(import_node_path46.default.dirname(outPath), import_node_path46.default.basename(projectPaths.errorsPath))
11313
+ const projectPaths = pathsForProject(project, import_node_path48.default.join(scanPath, "neat-out"));
11314
+ const outPath = import_node_path48.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
11315
+ const errorsPath = import_node_path48.default.resolve(
11316
+ process.env.NEAT_ERRORS_PATH ?? import_node_path48.default.join(import_node_path48.default.dirname(outPath), import_node_path48.default.basename(projectPaths.errorsPath))
10988
11317
  );
10989
- const staleEventsPath = import_node_path46.default.resolve(
10990
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path46.default.join(import_node_path46.default.dirname(outPath), import_node_path46.default.basename(projectPaths.staleEventsPath))
11318
+ const staleEventsPath = import_node_path48.default.resolve(
11319
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path48.default.join(import_node_path48.default.dirname(outPath), import_node_path48.default.basename(projectPaths.staleEventsPath))
10991
11320
  );
10992
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path46.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
11321
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path48.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
10993
11322
  const handle = await startWatch(getGraph(project), {
10994
11323
  scanPath,
10995
11324
  outPath,
@@ -11135,11 +11464,11 @@ async function main() {
11135
11464
  process.exit(1);
11136
11465
  }
11137
11466
  async function tryOrchestrator(cmd, parsed) {
11138
- const scanPath = import_node_path46.default.resolve(cmd);
11139
- const stat = await import_node_fs30.promises.stat(scanPath).catch(() => null);
11467
+ const scanPath = import_node_path48.default.resolve(cmd);
11468
+ const stat = await import_node_fs31.promises.stat(scanPath).catch(() => null);
11140
11469
  if (!stat || !stat.isDirectory()) return null;
11141
11470
  const projectExplicit = parsed.project !== null;
11142
- const projectName = projectExplicit ? parsed.project : import_node_path46.default.basename(scanPath);
11471
+ const projectName = projectExplicit ? parsed.project : import_node_path48.default.basename(scanPath);
11143
11472
  const result = await runOrchestrator({
11144
11473
  scanPath,
11145
11474
  project: projectName,
@@ -11289,10 +11618,10 @@ async function runQueryVerb(cmd, parsed) {
11289
11618
  const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
11290
11619
  const out = [];
11291
11620
  for (const p of parts) {
11292
- const r = import_types26.DivergenceTypeSchema.safeParse(p);
11621
+ const r = import_types27.DivergenceTypeSchema.safeParse(p);
11293
11622
  if (!r.success) {
11294
11623
  console.error(
11295
- `neat divergences: unknown --type "${p}". allowed: ${import_types26.DivergenceTypeSchema.options.join(", ")}`
11624
+ `neat divergences: unknown --type "${p}". allowed: ${import_types27.DivergenceTypeSchema.options.join(", ")}`
11296
11625
  );
11297
11626
  return 2;
11298
11627
  }