@hublo/sentinel 1.2.0-alpha.8 → 1.2.0-alpha.9

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.
@@ -8,6 +8,7 @@ import {
8
8
  VERBS,
9
9
  WORKSPACE_ROOT_MARKER,
10
10
  availableTargets,
11
+ buildConfigFile,
11
12
  declaredPresetFor,
12
13
  describeFramework,
13
14
  dispatch,
@@ -21,7 +22,7 @@ import {
21
22
  registerAdapters,
22
23
  resolve,
23
24
  resolveBin
24
- } from "../chunk-A25QVPXF.js";
25
+ } from "../chunk-6RH3LZQZ.js";
25
26
 
26
27
  // bin/sentinel.ts
27
28
  import { program } from "commander";
@@ -113,6 +114,30 @@ function resolveContext(cwd2, opts2) {
113
114
  );
114
115
  }
115
116
 
117
+ // src/roles/build/scope.ts
118
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
119
+ import { join as join3 } from "path";
120
+ var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
121
+ function buildsWithDeprecatedExecutor(cwd2) {
122
+ const projectJson = join3(cwd2, "project.json");
123
+ if (!existsSync2(projectJson)) return false;
124
+ try {
125
+ const project = JSON.parse(readFileSync2(projectJson, "utf8"));
126
+ return project.targets?.build?.executor === DEPRECATED_NX_BUILDER;
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+ function explainBuildScope(cwd2) {
132
+ if (buildConfigFile(cwd2) !== void 0) {
133
+ return ` This module HAS a Vite config, so detection is the likely problem: the build toolchain is declared at the workspace root, which makes a front app detect as "node". Pass --preset to say what it is.`;
134
+ }
135
+ if (buildsWithDeprecatedExecutor(cwd2)) {
136
+ return ` This module still builds through ${DEPRECATED_NX_BUILDER}, which Nx v24 removes. It has no Vite config yet, so there is nothing for --build to adopt: add one that imports @hublo/sentinel/build/nest, then run this again. See docs/build-adoption.md.`;
137
+ }
138
+ return ` This module has neither a Vite config nor an Nx build target, so it builds nothing and --build is out of scope for it. Nothing was written, and nothing is wrong.`;
139
+ }
140
+
116
141
  // src/cli/run-init.ts
117
142
  function asMessage(error) {
118
143
  return error instanceof Error ? error.message : String(error);
@@ -151,9 +176,10 @@ async function runInit(ctx) {
151
176
  } catch (error) {
152
177
  if (error instanceof PresetUnsupportedError) {
153
178
  if (ctx.targetsExplicit) {
179
+ const hint = type === "build" ? explainBuildScope(module.root) : ` Pass --preset if it was detected wrongly (hoisted dependencies make detection fall back to "node").`;
154
180
  process.stderr.write(
155
181
  `
156
- sentinel (${type}): this module's preset is "${error.preset}", and --${type} has no adapter for it. Pass --preset if it was detected wrongly (hoisted dependencies make detection fall back to "node").
182
+ sentinel (${type}): this module's preset is "${error.preset}", and --${type} has no adapter for it.${hint}
157
183
  `
158
184
  );
159
185
  worst = Math.max(worst, 1);
@@ -205,9 +205,9 @@ function binFromOwnInstall(packageName, binName) {
205
205
  try {
206
206
  const manifest = require2.resolve(`${packageName}/package.json`);
207
207
  const bin = require2(manifest).bin;
208
- const relative4 = typeof bin === "string" ? bin : bin?.[binName];
209
- if (!relative4) return void 0;
210
- const executable = join2(dirname2(manifest), relative4);
208
+ const relative6 = typeof bin === "string" ? bin : bin?.[binName];
209
+ if (!relative6) return void 0;
210
+ const executable = join2(dirname2(manifest), relative6);
211
211
  return existsSync3(executable) ? executable : void 0;
212
212
  } catch {
213
213
  return void 0;
@@ -220,8 +220,8 @@ function binSearchPath(cwd) {
220
220
  }
221
221
 
222
222
  // src/roles/build/plan.ts
223
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
224
- import { join as join7 } from "path";
223
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
224
+ import { join as join8 } from "path";
225
225
 
226
226
  // src/core/config/existing-command.ts
227
227
  import { readFileSync as readFileSync3 } from "fs";
@@ -508,9 +508,116 @@ function composeDevScript(existing, command) {
508
508
  return chunks.join(CHAIN);
509
509
  }
510
510
 
511
- // src/roles/build/read-adoption.ts
511
+ // src/roles/build/nest/adopt.ts
512
+ import { relative as relative2 } from "path";
513
+
514
+ // src/roles/build/nest/nx-target.ts
512
515
  import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
513
- import { join as join6 } from "path";
516
+ import { join as join6, relative } from "path";
517
+ var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
518
+ var TRANSLATABLE_WEBPACK_CONFIG = "tools/webpack-configs/update.webpack.config.js";
519
+ var readProjectJson = (cwd) => {
520
+ const path = join6(cwd, "project.json");
521
+ if (!existsSync6(path)) return void 0;
522
+ try {
523
+ return JSON.parse(readFileSync4(path, "utf8"));
524
+ } catch {
525
+ return void 0;
526
+ }
527
+ };
528
+ function webpackBuildTarget(cwd) {
529
+ const project = readProjectJson(cwd);
530
+ const target = project?.targets?.build;
531
+ if (target?.executor !== DEPRECATED_NX_BUILDER) return void 0;
532
+ const { outputPath, main, webpackConfig } = target.options ?? {};
533
+ if (typeof project?.name !== "string" || typeof outputPath !== "string" || typeof main !== "string" || typeof webpackConfig !== "string") {
534
+ return void 0;
535
+ }
536
+ return {
537
+ project: project.name,
538
+ outputPath,
539
+ main,
540
+ webpackConfig,
541
+ configurations: target.configurations
542
+ };
543
+ }
544
+ function refusalFor(cwd, target) {
545
+ if (target.webpackConfig !== TRANSLATABLE_WEBPACK_CONFIG) {
546
+ return `this service builds with ${target.webpackConfig}, not the shared ${TRANSLATABLE_WEBPACK_CONFIG} this preset reproduces. Its own config adds loaders or rules the preset has no equivalent for, and translating it anyway would produce a build that succeeds and a service that fails later. Read that file, decide what the Vite config needs, and write it by hand. Nothing was written.`;
547
+ }
548
+ const live = liveFileReplacements(cwd, target);
549
+ if (live.length > 0) {
550
+ return `this service's production configuration replaces ${live.join(", ")}, and those files exist. The preset has no equivalent, so translating the target would drop a real substitution. Nothing was written.`;
551
+ }
552
+ return void 0;
553
+ }
554
+ function liveFileReplacements(cwd, target) {
555
+ const workspaceRoot = workspaceRootFor(cwd, target);
556
+ const configurations = target.configurations ?? {};
557
+ const live = [];
558
+ for (const configuration of Object.values(configurations)) {
559
+ for (const replacement of configuration.fileReplacements ?? []) {
560
+ const exists = existsSync6(join6(workspaceRoot, replacement.replace)) || existsSync6(join6(workspaceRoot, replacement.with));
561
+ if (exists) live.push(replacement.replace);
562
+ }
563
+ }
564
+ return live;
565
+ }
566
+ function workspaceRootFor(cwd, target) {
567
+ const moduleRoot = target.main.replace(/\/src\/.*$/, "");
568
+ const suffix = cwd.endsWith(moduleRoot) ? moduleRoot : "";
569
+ return suffix === "" ? cwd : cwd.slice(0, cwd.length - suffix.length - 1);
570
+ }
571
+ function workspaceRootHops(cwd, target) {
572
+ const root = workspaceRootFor(cwd, target);
573
+ const rel = relative(root, cwd);
574
+ return rel === "" ? 0 : rel.split(/[\\/]/).length;
575
+ }
576
+
577
+ // src/roles/build/nest/adopt.ts
578
+ var NEST_CONFIG_FILE = "vite.config.mts";
579
+ function nestConfigSource(cwd, target) {
580
+ const hops = workspaceRootHops(cwd, target);
581
+ const up = Array.from({ length: hops }, () => "..").join("/");
582
+ return `import path from 'node:path'
583
+
584
+ import { nestService } from '${BUILD_PRESET_SPECIFIERS.nest}'
585
+
586
+ export default nestService({
587
+ project: '${target.project}',
588
+ root: __dirname,
589
+ workspaceRoot: path.resolve(__dirname, '${up}'),
590
+ outDir: '${target.outputPath}',
591
+ })
592
+ `;
593
+ }
594
+ function nestBuildTarget(cwd, target) {
595
+ const moduleRoot = relative2(workspaceRootFor(cwd, target), cwd);
596
+ return {
597
+ executor: "nx:run-commands",
598
+ outputs: [`{workspaceRoot}/${target.outputPath}`],
599
+ options: {
600
+ cwd: moduleRoot,
601
+ command: "pnpm exec sentinel --run --build",
602
+ forwardAllArgs: false
603
+ }
604
+ };
605
+ }
606
+ function nestAdoptionOperations(cwd, target) {
607
+ return [
608
+ { kind: "write", path: NEST_CONFIG_FILE, contents: nestConfigSource(cwd, target) },
609
+ { kind: "remove-json-keys", path: "project.json", keys: [["targets", "build"]] },
610
+ {
611
+ kind: "merge-json",
612
+ path: "project.json",
613
+ value: { targets: { build: nestBuildTarget(cwd, target) } }
614
+ }
615
+ ];
616
+ }
617
+
618
+ // src/roles/build/read-adoption.ts
619
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
620
+ import { join as join7 } from "path";
514
621
  var NOT_ADOPTED = (configFile, unreadable = null) => ({
515
622
  configFile,
516
623
  preset: null,
@@ -521,7 +628,7 @@ var NOT_ADOPTED = (configFile, unreadable = null) => ({
521
628
  unreadable
522
629
  });
523
630
  function buildConfigFile(cwd) {
524
- return BUILD_CONFIG_FILES.find((name) => existsSync6(join6(cwd, name)));
631
+ return BUILD_CONFIG_FILES.find((name) => existsSync7(join7(cwd, name)));
525
632
  }
526
633
  function importsSpecifier(source, specifier) {
527
634
  const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -535,7 +642,9 @@ function importsPreset(source) {
535
642
  var REACT_PLUGIN_SPECIFIERS = ["@vitejs/plugin-react", "@tanstack/react-start"];
536
643
  function declaredBuildPreset(cwd) {
537
644
  const source = readBuildConfigSource(cwd);
538
- if (source === void 0) return void 0;
645
+ if (source === void 0) {
646
+ return webpackBuildTarget(cwd) === void 0 ? void 0 : "nest";
647
+ }
539
648
  if (importsSpecifier(source, BUILD_PRESET_SPECIFIERS.nest)) return "nest";
540
649
  if (importsPreset(source)) return "react";
541
650
  return REACT_PLUGIN_SPECIFIERS.some((specifier) => importsSpecifier(source, specifier)) ? "react" : void 0;
@@ -544,7 +653,7 @@ function readBuildConfigSource(cwd) {
544
653
  const configFile = buildConfigFile(cwd);
545
654
  if (!configFile) return void 0;
546
655
  try {
547
- return readFileSync4(join6(cwd, configFile), "utf8");
656
+ return readFileSync5(join7(cwd, configFile), "utf8");
548
657
  } catch {
549
658
  return void 0;
550
659
  }
@@ -554,7 +663,7 @@ function readBuildAdoption(cwd) {
554
663
  if (!configFile) return NOT_ADOPTED(null);
555
664
  let source;
556
665
  try {
557
- source = readFileSync4(join6(cwd, configFile), "utf8");
666
+ source = readFileSync5(join7(cwd, configFile), "utf8");
558
667
  } catch (error) {
559
668
  return NOT_ADOPTED(configFile, error instanceof Error ? error.message : String(error));
560
669
  }
@@ -760,6 +869,22 @@ function plan(context) {
760
869
  const notes = [];
761
870
  const operations = [];
762
871
  const configFile = buildConfigFile(context.cwd);
872
+ const webpackTarget = webpackBuildTarget(context.cwd);
873
+ if (configFile === void 0 && webpackTarget !== void 0) {
874
+ const refusal = refusalFor(context.cwd, webpackTarget);
875
+ if (refusal !== void 0) return { operations: [], blocked: refusal };
876
+ const dropped = Object.keys(webpackTarget.configurations ?? {});
877
+ return {
878
+ operations: nestAdoptionOperations(context.cwd, webpackTarget),
879
+ notes: [
880
+ `wrote ${NEST_CONFIG_FILE} and pointed the nx build target at sentinel. The target stays \`nx:run-commands\` with \`forwardAllArgs: false\`, so the image's \`--generatePackageJson\` still works and the Dockerfile needs no change.`,
881
+ ...dropped.length > 0 ? [
882
+ `dropped the \`${dropped.join("`, `")}\` configuration${dropped.length > 1 ? "s" : ""} from the build target: its fileReplacements point at files that do not exist, so it replaced nothing. A live one would have blocked this instead.`
883
+ ] : [],
884
+ `verify with \`nx run ${webpackTarget.project}:generate-swagger-file\` and \`git diff --exit-code\`: it runs the built bundle, so it proves the service boots AND that its published contract did not move.`
885
+ ]
886
+ };
887
+ }
763
888
  if (configFile === void 0) {
764
889
  return {
765
890
  operations: [],
@@ -809,9 +934,9 @@ function plan(context) {
809
934
  return { operations, notes };
810
935
  }
811
936
  function readConfigSource(cwd, configFile) {
812
- if (!existsSync7(join7(cwd, configFile))) return "";
937
+ if (!existsSync8(join8(cwd, configFile))) return "";
813
938
  try {
814
- return readFileSync5(join7(cwd, configFile), "utf8");
939
+ return readFileSync6(join8(cwd, configFile), "utf8");
815
940
  } catch {
816
941
  return "";
817
942
  }
@@ -1109,12 +1234,12 @@ function registerBuild() {
1109
1234
 
1110
1235
  // src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
1111
1236
  import { spawnSync as spawnSync3 } from "child_process";
1112
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
1113
- import { join as join14 } from "path";
1237
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
1238
+ import { join as join15 } from "path";
1114
1239
 
1115
1240
  // src/core/config/has-source.ts
1116
1241
  import { readdirSync } from "fs";
1117
- import { extname, join as join8, relative } from "path";
1242
+ import { extname, join as join9, relative as relative3 } from "path";
1118
1243
  var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
1119
1244
  "node_modules",
1120
1245
  "dist",
@@ -1165,7 +1290,7 @@ function hasSourceFiles(cwd, extensions) {
1165
1290
  }
1166
1291
  for (const entry of entries) {
1167
1292
  if (entry.isDirectory()) {
1168
- if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join8(dir, entry.name));
1293
+ if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join9(dir, entry.name));
1169
1294
  continue;
1170
1295
  }
1171
1296
  if (wanted.has(extname(entry.name))) return true;
@@ -1186,12 +1311,12 @@ function listSourceFiles(cwd, extensions) {
1186
1311
  continue;
1187
1312
  }
1188
1313
  for (const entry of entries) {
1189
- const full = join8(dir, entry.name);
1314
+ const full = join9(dir, entry.name);
1190
1315
  if (entry.isDirectory()) {
1191
1316
  if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(full);
1192
1317
  continue;
1193
1318
  }
1194
- if (wanted.has(extname(entry.name))) found.push(relative(cwd, full));
1319
+ if (wanted.has(extname(entry.name))) found.push(relative3(cwd, full));
1195
1320
  }
1196
1321
  }
1197
1322
  return found;
@@ -1209,8 +1334,8 @@ var WORKSPACE_ROOT_MARKER = "nx.json";
1209
1334
  var DEFAULT_MAX_DIAGNOSTICS = 100;
1210
1335
 
1211
1336
  // src/core/workspace-prep.ts
1212
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync } from "fs";
1213
- import { dirname as dirname3, join as join9, relative as relative2 } from "path";
1337
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync } from "fs";
1338
+ import { dirname as dirname3, join as join10, relative as relative4 } from "path";
1214
1339
  var OVERRIDE_KEY = "i18next>typescript";
1215
1340
  var NATIVE_TS_ALIAS = "@typescript/native";
1216
1341
  var WORKSPACE_YAML = "pnpm-workspace.yaml";
@@ -1219,7 +1344,7 @@ var LIST_ITEM = /^(\s*)-\s+(.*?)\s*$/;
1219
1344
  function findWorkspaceRoot(startDir) {
1220
1345
  let dir = startDir;
1221
1346
  for (; ; ) {
1222
- if (existsSync8(join9(dir, WORKSPACE_ROOT_MARKER))) return dir;
1347
+ if (existsSync9(join10(dir, WORKSPACE_ROOT_MARKER))) return dir;
1223
1348
  const parent = dirname3(dir);
1224
1349
  if (parent === dir) return void 0;
1225
1350
  dir = parent;
@@ -1232,9 +1357,9 @@ function declaredNativeTs(pkg) {
1232
1357
  return version || void 0;
1233
1358
  }
1234
1359
  function ensureI18nextSingleton(root, dryRun) {
1235
- const pkgPath = join9(root, "package.json");
1236
- if (!existsSync8(pkgPath)) return void 0;
1237
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
1360
+ const pkgPath = join10(root, "package.json");
1361
+ if (!existsSync9(pkgPath)) return void 0;
1362
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
1238
1363
  const want = declaredNativeTs(pkg);
1239
1364
  if (!want) return void 0;
1240
1365
  const have = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
@@ -1247,10 +1372,10 @@ function ensureI18nextSingleton(root, dryRun) {
1247
1372
  return `pinned ${OVERRIDE_KEY} to ${want} in package.json (i18next singleton)`;
1248
1373
  }
1249
1374
  function ensureReleaseAgeAllowList(root, dryRun) {
1250
- const yamlPath = join9(root, WORKSPACE_YAML);
1251
- if (!existsSync8(yamlPath)) return void 0;
1375
+ const yamlPath = join10(root, WORKSPACE_YAML);
1376
+ if (!existsSync9(yamlPath)) return void 0;
1252
1377
  const own = readOwnPackage().name;
1253
- const lines = readFileSync6(yamlPath, "utf8").split("\n");
1378
+ const lines = readFileSync7(yamlPath, "utf8").split("\n");
1254
1379
  const keyIdx = lines.findIndex((line) => line.replace(/\s+$/, "") === `${RELEASE_AGE_KEY}:`);
1255
1380
  if (keyIdx === -1) return void 0;
1256
1381
  let lastItemIdx = keyIdx;
@@ -1283,13 +1408,13 @@ var ROOT_PRETTIER_CONFIGS = [
1283
1408
  "prettier.config.mjs"
1284
1409
  ];
1285
1410
  function ensureFormatterExclusion(root, moduleDir, dryRun) {
1286
- const rel = relative2(root, moduleDir).replaceAll("\\", "/");
1411
+ const rel = relative4(root, moduleDir).replaceAll("\\", "/");
1287
1412
  if (rel === "" || rel.startsWith("..")) return void 0;
1288
- const ignorePath = join9(root, PRETTIER_IGNORE);
1289
- const hasPrettier = existsSync8(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync8(join9(root, name)));
1413
+ const ignorePath = join10(root, PRETTIER_IGNORE);
1414
+ const hasPrettier = existsSync9(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync9(join10(root, name)));
1290
1415
  if (!hasPrettier) return void 0;
1291
1416
  const pattern = `/${rel}/`;
1292
- const existing = existsSync8(ignorePath) ? readFileSync6(ignorePath, "utf8") : "";
1417
+ const existing = existsSync9(ignorePath) ? readFileSync7(ignorePath, "utf8") : "";
1293
1418
  const lines = existing.split("\n");
1294
1419
  if (lines.some((line) => line.trim().replace(/\/$/, "") === pattern.replace(/\/$/, ""))) {
1295
1420
  return void 0;
@@ -1325,10 +1450,10 @@ function ensureWorkspacePrep(opts) {
1325
1450
  function inspectWorkspacePrep(root) {
1326
1451
  const entries = [];
1327
1452
  let pkg = {};
1328
- const pkgPath = join9(root, "package.json");
1329
- if (existsSync8(pkgPath)) {
1453
+ const pkgPath = join10(root, "package.json");
1454
+ if (existsSync9(pkgPath)) {
1330
1455
  try {
1331
- pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
1456
+ pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
1332
1457
  } catch {
1333
1458
  pkg = {};
1334
1459
  }
@@ -1342,9 +1467,9 @@ function inspectWorkspacePrep(root) {
1342
1467
  });
1343
1468
  }
1344
1469
  const own = readOwnPackage().name;
1345
- const yamlPath = join9(root, WORKSPACE_YAML);
1346
- if (existsSync8(yamlPath)) {
1347
- const yaml = readFileSync6(yamlPath, "utf8");
1470
+ const yamlPath = join10(root, WORKSPACE_YAML);
1471
+ if (existsSync9(yamlPath)) {
1472
+ const yaml = readFileSync7(yamlPath, "utf8");
1348
1473
  const listed = new RegExp(`^\\s*-\\s*['"]?${own.replace("/", "\\/")}['"]?\\s*$`, "m").test(yaml);
1349
1474
  if (listed) {
1350
1475
  const gated = new RegExp(`^\\s*minimumReleaseAge\\s*:`, "m").test(yaml);
@@ -1431,8 +1556,8 @@ function writesWhenRewritten(name, command) {
1431
1556
  var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
1432
1557
 
1433
1558
  // src/roles/format/inherited-ignores.ts
1434
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
1435
- import { join as join10 } from "path";
1559
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
1560
+ import { join as join11 } from "path";
1436
1561
  var ROOT_IGNORE_FILE = ".prettierignore";
1437
1562
  function isPattern(line) {
1438
1563
  const trimmed = line.trim();
@@ -1447,11 +1572,11 @@ function toModulePattern(pattern) {
1447
1572
  }
1448
1573
  function inheritedIgnorePatterns(workspaceRoot) {
1449
1574
  if (!workspaceRoot) return [];
1450
- const path = join10(workspaceRoot, ROOT_IGNORE_FILE);
1451
- if (!existsSync9(path)) return [];
1575
+ const path = join11(workspaceRoot, ROOT_IGNORE_FILE);
1576
+ if (!existsSync10(path)) return [];
1452
1577
  let contents;
1453
1578
  try {
1454
- contents = readFileSync7(path, "utf8");
1579
+ contents = readFileSync8(path, "utf8");
1455
1580
  } catch {
1456
1581
  return [];
1457
1582
  }
@@ -1538,13 +1663,13 @@ function formatPresetFor(preset) {
1538
1663
  }
1539
1664
 
1540
1665
  // src/roles/format/prettier-config.ts
1541
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
1542
- import { join as join12 } from "path";
1666
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
1667
+ import { join as join13 } from "path";
1543
1668
 
1544
1669
  // src/roles/format/resolve-oxfmt.ts
1545
- import { readFileSync as readFileSync8 } from "fs";
1670
+ import { readFileSync as readFileSync9 } from "fs";
1546
1671
  import { createRequire as createRequire2 } from "module";
1547
- import { dirname as dirname4, join as join11 } from "path";
1672
+ import { dirname as dirname4, join as join12 } from "path";
1548
1673
  function resolveOxfmt(cwd) {
1549
1674
  return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
1550
1675
  }
@@ -1573,8 +1698,8 @@ function configSchema() {
1573
1698
  function readConfigSchema() {
1574
1699
  try {
1575
1700
  const manifest = createRequire2(import.meta.url).resolve("oxfmt/package.json");
1576
- const schemaPath = join11(dirname4(manifest), "configuration_schema.json");
1577
- return JSON.parse(readFileSync8(schemaPath, "utf8"));
1701
+ const schemaPath = join12(dirname4(manifest), "configuration_schema.json");
1702
+ return JSON.parse(readFileSync9(schemaPath, "utf8"));
1578
1703
  } catch {
1579
1704
  return void 0;
1580
1705
  }
@@ -1605,14 +1730,14 @@ function toOxfmtOverrides(value) {
1605
1730
  return { overrides, unresolved };
1606
1731
  }
1607
1732
  function readPrettierSettings(cwd) {
1608
- const file = PRETTIER_CONFIG_FILES.find((name) => existsSync10(join12(cwd, name)));
1733
+ const file = PRETTIER_CONFIG_FILES.find((name) => existsSync11(join13(cwd, name)));
1609
1734
  if (!file) return { options: {}, unresolved: [] };
1610
1735
  if (/\.(js|cjs|mjs)$/.test(file)) {
1611
1736
  return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
1612
1737
  }
1613
1738
  let parsed;
1614
1739
  try {
1615
- parsed = parseJsonc(readFileSync9(join12(cwd, file), "utf8"), file);
1740
+ parsed = parseJsonc(readFileSync10(join13(cwd, file), "utf8"), file);
1616
1741
  } catch {
1617
1742
  return { options: {}, file, unresolved: [`${file} could not be parsed`] };
1618
1743
  }
@@ -1642,8 +1767,8 @@ function readPrettierSettings(cwd) {
1642
1767
  }
1643
1768
 
1644
1769
  // src/roles/format/read-adoption.ts
1645
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
1646
- import { join as join13 } from "path";
1770
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
1771
+ import { join as join14 } from "path";
1647
1772
  var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
1648
1773
  configFile,
1649
1774
  preset: null,
@@ -1659,11 +1784,11 @@ function sameValue(a, b) {
1659
1784
  return JSON.stringify(a) === JSON.stringify(b);
1660
1785
  }
1661
1786
  function readFormatAdoption(cwd) {
1662
- const path = join13(cwd, FORMAT_CONFIG_FILE);
1663
- if (!existsSync11(path)) return NOT_ADOPTED2(null);
1787
+ const path = join14(cwd, FORMAT_CONFIG_FILE);
1788
+ if (!existsSync12(path)) return NOT_ADOPTED2(null);
1664
1789
  let parsed;
1665
1790
  try {
1666
- parsed = parseJsonc(readFileSync10(path, "utf8"), FORMAT_CONFIG_FILE);
1791
+ parsed = parseJsonc(readFileSync11(path, "utf8"), FORMAT_CONFIG_FILE);
1667
1792
  } catch (error) {
1668
1793
  const reason = error instanceof Error ? error.message : String(error);
1669
1794
  return NOT_ADOPTED2(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
@@ -1788,7 +1913,7 @@ var OxfmtAdapter = class extends BaseAdapter {
1788
1913
  manifestOperation(context.cwd, this.formatScripts(context.cwd))
1789
1914
  ];
1790
1915
  const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
1791
- (name) => existsSync12(join14(context.cwd, name))
1916
+ (name) => existsSync13(join15(context.cwd, name))
1792
1917
  );
1793
1918
  for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
1794
1919
  const removableDeps = this.modulePrettierDependencies(context.cwd);
@@ -2028,7 +2153,7 @@ var OxfmtAdapter = class extends BaseAdapter {
2028
2153
  modulePrettierDependencies(cwd) {
2029
2154
  const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
2030
2155
  try {
2031
- const manifest = JSON.parse(readFileSync11(join14(cwd, "package.json"), "utf8"));
2156
+ const manifest = JSON.parse(readFileSync12(join15(cwd, "package.json"), "utf8"));
2032
2157
  return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
2033
2158
  } catch {
2034
2159
  return [];
@@ -2110,8 +2235,8 @@ function registerFormat() {
2110
2235
 
2111
2236
  // src/roles/lint/adapters/oxlint/oxlint.adapter.ts
2112
2237
  import { spawnSync as spawnSync4 } from "child_process";
2113
- import { existsSync as existsSync20, readFileSync as readFileSync19, rmSync, writeFileSync as writeFileSync2 } from "fs";
2114
- import { join as join24 } from "path";
2238
+ import { existsSync as existsSync21, readFileSync as readFileSync20, rmSync, writeFileSync as writeFileSync2 } from "fs";
2239
+ import { join as join25 } from "path";
2115
2240
 
2116
2241
  // src/core/config/deferred-rules.ts
2117
2242
  function deferredRuleNames(rules) {
@@ -2120,7 +2245,7 @@ function deferredRuleNames(rules) {
2120
2245
 
2121
2246
  // src/core/fix-report.ts
2122
2247
  import { statSync } from "fs";
2123
- import { join as join15 } from "path";
2248
+ import { join as join16 } from "path";
2124
2249
  var CONFIG_REFUSED = /failed to parse .*config|invalid config file/i;
2125
2250
  function readFailure(output) {
2126
2251
  const lines = output.split("\n").map((line2) => line2.trim());
@@ -2140,7 +2265,7 @@ function fingerprint(path) {
2140
2265
  function snapshotFiles(cwd, extensions) {
2141
2266
  const snapshot = /* @__PURE__ */ new Map();
2142
2267
  for (const file of listSourceFiles(cwd, extensions)) {
2143
- const mark = fingerprint(join15(cwd, file));
2268
+ const mark = fingerprint(join16(cwd, file));
2144
2269
  if (mark !== void 0) snapshot.set(file, mark);
2145
2270
  }
2146
2271
  return snapshot;
@@ -2148,7 +2273,7 @@ function snapshotFiles(cwd, extensions) {
2148
2273
  function changedSince(cwd, before, extensions) {
2149
2274
  const changed = [];
2150
2275
  for (const file of listSourceFiles(cwd, extensions)) {
2151
- const now = fingerprint(join15(cwd, file));
2276
+ const now = fingerprint(join16(cwd, file));
2152
2277
  if (now === void 0) continue;
2153
2278
  if (before.get(file) !== now) changed.push(file);
2154
2279
  }
@@ -2167,8 +2292,8 @@ function describeFixOutcome(outcome, toolLabel) {
2167
2292
  }
2168
2293
 
2169
2294
  // src/roles/lint/module-baseline.ts
2170
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
2171
- import { join as join16 } from "path";
2295
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
2296
+ import { join as join17 } from "path";
2172
2297
  var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
2173
2298
  var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
2174
2299
  var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
@@ -2212,11 +2337,11 @@ function describeHolds(holds) {
2212
2337
  return `held ${holds.length} rule(s) at \`warn\` for this module, covering ${total} violation(s) that were already there: ${listed}${rest}. They are written to ${LINT_BASELINE_FILE}, they still report, and they return to \`error\` once fixed and re-initialized`;
2213
2338
  }
2214
2339
  function heldRules(cwd) {
2215
- const path = join16(cwd, LINT_BASELINE_FILE);
2216
- if (!existsSync13(path)) return [];
2340
+ const path = join17(cwd, LINT_BASELINE_FILE);
2341
+ if (!existsSync14(path)) return [];
2217
2342
  try {
2218
2343
  const parsed = parseJsonc(
2219
- readFileSync12(path, "utf8"),
2344
+ readFileSync13(path, "utf8"),
2220
2345
  LINT_BASELINE_FILE
2221
2346
  );
2222
2347
  return Object.keys(parsed.rules ?? {});
@@ -4039,14 +4164,14 @@ function downgradedRulesFor(preset) {
4039
4164
  }
4040
4165
 
4041
4166
  // src/roles/lint/extra-layers.ts
4042
- import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
4043
- import { isAbsolute as isAbsolute2, join as join17, resolve as resolve3 } from "path";
4167
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
4168
+ import { isAbsolute as isAbsolute2, join as join18, resolve as resolve3 } from "path";
4044
4169
  function ruleCount(cwd, specifier) {
4045
4170
  const path = isAbsolute2(specifier) ? specifier : resolve3(cwd, specifier);
4046
- if (!existsSync14(path)) return void 0;
4171
+ if (!existsSync15(path)) return void 0;
4047
4172
  try {
4048
4173
  const parsed = parseJsonc(
4049
- readFileSync13(path, "utf8"),
4174
+ readFileSync14(path, "utf8"),
4050
4175
  specifier
4051
4176
  );
4052
4177
  return Object.keys(parsed.rules ?? {}).length;
@@ -4066,7 +4191,7 @@ function extraLayers(cwd, extendsList) {
4066
4191
  function committedExtendsList(cwd, configFile) {
4067
4192
  try {
4068
4193
  const parsed = parseJsonc(
4069
- readFileSync13(join17(cwd, configFile), "utf8"),
4194
+ readFileSync14(join18(cwd, configFile), "utf8"),
4070
4195
  configFile
4071
4196
  );
4072
4197
  return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
@@ -4125,8 +4250,8 @@ function errorCountsByConfigRule(stdout) {
4125
4250
  }
4126
4251
 
4127
4252
  // src/core/config/read-adoption.ts
4128
- import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
4129
- import { join as join19 } from "path";
4253
+ import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
4254
+ import { join as join20 } from "path";
4130
4255
 
4131
4256
  // src/core/config/owned-keys.ts
4132
4257
  function presetOwnedKeys(config, permitted, presetSets) {
@@ -4141,12 +4266,12 @@ function localOnlyKeys(config, permitted, presetSets) {
4141
4266
  }
4142
4267
 
4143
4268
  // src/core/config/resolve-config-target.ts
4144
- import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
4145
- import { join as join18 } from "path";
4269
+ import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
4270
+ import { join as join19 } from "path";
4146
4271
  function readExtends(absolutePath) {
4147
4272
  let parsed;
4148
4273
  try {
4149
- parsed = parseJsonc(readFileSync14(absolutePath, "utf8"), absolutePath);
4274
+ parsed = parseJsonc(readFileSync15(absolutePath, "utf8"), absolutePath);
4150
4275
  } catch {
4151
4276
  return [];
4152
4277
  }
@@ -4160,8 +4285,8 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
4160
4285
  let existing;
4161
4286
  let existingExtendsSomething = false;
4162
4287
  for (const candidate of candidates) {
4163
- const absolutePath = join18(moduleDir, candidate);
4164
- if (!existsSync15(absolutePath)) continue;
4288
+ const absolutePath = join19(moduleDir, candidate);
4289
+ if (!existsSync16(absolutePath)) continue;
4165
4290
  const chain = readExtends(absolutePath);
4166
4291
  if (existing === void 0) {
4167
4292
  existing = candidate;
@@ -4194,12 +4319,12 @@ var NOT_ADOPTED3 = (configFile, unreadable = null) => ({
4194
4319
  });
4195
4320
  function readAdoption(cwd, options) {
4196
4321
  const target = resolveConfigTarget(cwd, options);
4197
- if (target.reason === "none" || !existsSync16(join19(cwd, target.path))) {
4322
+ if (target.reason === "none" || !existsSync17(join20(cwd, target.path))) {
4198
4323
  return NOT_ADOPTED3(target.reason === "none" ? null : target.path);
4199
4324
  }
4200
4325
  let parsed;
4201
4326
  try {
4202
- parsed = parseJsonc(readFileSync15(join19(cwd, target.path), "utf8"), target.path);
4327
+ parsed = parseJsonc(readFileSync16(join20(cwd, target.path), "utf8"), target.path);
4203
4328
  } catch (error) {
4204
4329
  const reason = error instanceof Error ? error.message : String(error);
4205
4330
  return NOT_ADOPTED3(target.path, `${target.path} could not be parsed (${reason})`);
@@ -4233,9 +4358,9 @@ function readLintAdoption(cwd) {
4233
4358
  }
4234
4359
 
4235
4360
  // src/roles/lint/resolve-oxlint.ts
4236
- import { existsSync as existsSync17 } from "fs";
4361
+ import { existsSync as existsSync18 } from "fs";
4237
4362
  import { createRequire as createRequire3 } from "module";
4238
- import { delimiter as delimiter2, dirname as dirname5, join as join20 } from "path";
4363
+ import { delimiter as delimiter2, dirname as dirname5, join as join21 } from "path";
4239
4364
  import { fileURLToPath as fileURLToPath2 } from "url";
4240
4365
  var PACKAGE_OF = {
4241
4366
  oxlint: "oxlint",
@@ -4260,30 +4385,30 @@ function tsgolintShim(cwd) {
4260
4385
  for (const owner of ["oxlint-tsgolint", "oxlint"]) {
4261
4386
  try {
4262
4387
  const packageDir = dirname5(require3.resolve(`${owner}/package.json`));
4263
- candidates.push(join20(packageDir, "node_modules", ".bin", "tsgolint"));
4264
- candidates.push(join20(packageDir, "..", ".bin", "tsgolint"));
4388
+ candidates.push(join21(packageDir, "node_modules", ".bin", "tsgolint"));
4389
+ candidates.push(join21(packageDir, "..", ".bin", "tsgolint"));
4265
4390
  } catch {
4266
4391
  }
4267
4392
  }
4268
4393
  try {
4269
4394
  const ownRoot = dirname5(require3.resolve("@hublo/sentinel/package.json"));
4270
- candidates.push(join20(ownRoot, "node_modules", ".bin", "tsgolint"));
4395
+ candidates.push(join21(ownRoot, "node_modules", ".bin", "tsgolint"));
4271
4396
  } catch {
4272
4397
  candidates.push(resolveBin(dirname5(fileURLToPath2(import.meta.url)), "tsgolint") ?? "");
4273
4398
  }
4274
- return candidates.find((candidate) => candidate !== "" && existsSync17(candidate));
4399
+ return candidates.find((candidate) => candidate !== "" && existsSync18(candidate));
4275
4400
  }
4276
4401
  function oxlintSearchPath(cwd) {
4277
4402
  return binSearchPath(cwd);
4278
4403
  }
4279
4404
 
4280
4405
  // src/roles/lint/adapters/oxlint/plan.ts
4281
- import { existsSync as existsSync19, readFileSync as readFileSync18 } from "fs";
4282
- import { join as join23 } from "path";
4406
+ import { existsSync as existsSync20, readFileSync as readFileSync19 } from "fs";
4407
+ import { join as join24 } from "path";
4283
4408
 
4284
4409
  // src/roles/lint/eslint-ignores.ts
4285
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
4286
- import { join as join21 } from "path";
4410
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
4411
+ import { join as join22 } from "path";
4287
4412
  import { parseSync as parseSync2 } from "oxc-parser";
4288
4413
  var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
4289
4414
  function toOxlintPattern(pattern) {
@@ -4295,11 +4420,11 @@ function readRootEslintIgnores(root) {
4295
4420
  return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
4296
4421
  }
4297
4422
  function readEslintIgnores(cwd) {
4298
- const config = ESLINT_CONFIG_FILES.map((name) => join21(cwd, name)).find((path) => existsSync18(path));
4423
+ const config = ESLINT_CONFIG_FILES.map((name) => join22(cwd, name)).find((path) => existsSync19(path));
4299
4424
  if (!config) return { patterns: [], unresolved: [] };
4300
4425
  let source;
4301
4426
  try {
4302
- source = readFileSync16(config, "utf8");
4427
+ source = readFileSync17(config, "utf8");
4303
4428
  } catch {
4304
4429
  return { patterns: [], unresolved: [] };
4305
4430
  }
@@ -4400,8 +4525,8 @@ function lintPresetFor(preset) {
4400
4525
  }
4401
4526
 
4402
4527
  // src/roles/lint/rename-suppressions.ts
4403
- import { readdirSync as readdirSync2, readFileSync as readFileSync17, statSync as statSync2 } from "fs";
4404
- import { join as join22, relative as relative3 } from "path";
4528
+ import { readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync2 } from "fs";
4529
+ import { join as join23, relative as relative5 } from "path";
4405
4530
  var SOURCE_EXTENSIONS = [
4406
4531
  ".ts",
4407
4532
  ".tsx",
@@ -4448,7 +4573,7 @@ function* sourceFiles(dir) {
4448
4573
  return;
4449
4574
  }
4450
4575
  for (const entry of entries) {
4451
- const full = join22(dir, entry);
4576
+ const full = join23(dir, entry);
4452
4577
  let isDirectory;
4453
4578
  try {
4454
4579
  isDirectory = statSync2(full).isDirectory();
@@ -4468,7 +4593,7 @@ function findSuppressionRenames(cwd, renames) {
4468
4593
  for (const file of sourceFiles(cwd)) {
4469
4594
  let content;
4470
4595
  try {
4471
- content = readFileSync17(file, "utf8");
4596
+ content = readFileSync18(file, "utf8");
4472
4597
  } catch {
4473
4598
  continue;
4474
4599
  }
@@ -4492,7 +4617,7 @@ function findSuppressionRenames(cwd, renames) {
4492
4617
  }
4493
4618
  if (hits.length === 0) return;
4494
4619
  found.push({
4495
- file: relative3(cwd, file),
4620
+ file: relative5(cwd, file),
4496
4621
  line: index + 1,
4497
4622
  from: line,
4498
4623
  to: line.replace(rules, renamed.join(", ")),
@@ -4596,7 +4721,7 @@ function plan2(context) {
4596
4721
  keys: removableDeps
4597
4722
  });
4598
4723
  }
4599
- const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync19(join23(context.cwd, name)));
4724
+ const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync20(join24(context.cwd, name)));
4600
4725
  for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
4601
4726
  operations.push(
4602
4727
  ...nxTargetOperations({
@@ -4694,7 +4819,7 @@ function lintScripts(cwd) {
4694
4819
  function committedExtends(cwd) {
4695
4820
  try {
4696
4821
  const parsed = parseJsonc(
4697
- readFileSync18(join23(cwd, LINT_CONFIG_FILE), "utf8"),
4822
+ readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
4698
4823
  LINT_CONFIG_FILE
4699
4824
  );
4700
4825
  return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
@@ -4705,7 +4830,7 @@ function committedExtends(cwd) {
4705
4830
  function committedIgnorePatterns(cwd) {
4706
4831
  try {
4707
4832
  const parsed = parseJsonc(
4708
- readFileSync18(join23(cwd, LINT_CONFIG_FILE), "utf8"),
4833
+ readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
4709
4834
  LINT_CONFIG_FILE
4710
4835
  );
4711
4836
  return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
@@ -4717,7 +4842,7 @@ function moduleEslintDependencies(cwd) {
4717
4842
  const isEslintPackage = (name) => name === "eslint" || name === "@types/eslint" || name === "typescript-eslint" || name.startsWith("@typescript-eslint/") || name.startsWith("eslint-plugin-") || name.startsWith("eslint-config-") || name.startsWith("@eslint/");
4718
4843
  let manifest;
4719
4844
  try {
4720
- manifest = JSON.parse(readFileSync18(join23(cwd, "package.json"), "utf8"));
4845
+ manifest = JSON.parse(readFileSync19(join24(cwd, "package.json"), "utf8"));
4721
4846
  } catch {
4722
4847
  return [];
4723
4848
  }
@@ -4851,20 +4976,20 @@ ${result.stderr ?? ""}`);
4851
4976
  * build the developer can see, rather than a silent half-adoption they cannot.
4852
4977
  */
4853
4978
  writeModuleBaseline(ctx, oxlint, env) {
4854
- const configPath = join24(ctx.cwd, LINT_CONFIG_FILE);
4855
- const baselinePath = join24(ctx.cwd, LINT_BASELINE_FILE);
4856
- if (!existsSync20(configPath)) return;
4979
+ const configPath = join25(ctx.cwd, LINT_CONFIG_FILE);
4980
+ const baselinePath = join25(ctx.cwd, LINT_BASELINE_FILE);
4981
+ if (!existsSync21(configPath)) return;
4857
4982
  let config;
4858
4983
  try {
4859
4984
  config = parseJsonc(
4860
- readFileSync19(configPath, "utf8"),
4985
+ readFileSync20(configPath, "utf8"),
4861
4986
  LINT_CONFIG_FILE
4862
4987
  );
4863
4988
  } catch {
4864
4989
  return;
4865
4990
  }
4866
4991
  const current = Array.isArray(config.extends) ? config.extends : [];
4867
- const measurePath = join24(ctx.cwd, LINT_MEASURE_FILE);
4992
+ const measurePath = join25(ctx.cwd, LINT_MEASURE_FILE);
4868
4993
  let measured;
4869
4994
  try {
4870
4995
  writeFileSync2(
@@ -4912,7 +5037,7 @@ ${result.stderr ?? ""}`);
4912
5037
  }
4913
5038
  }
4914
5039
  async run(ctx) {
4915
- if (!existsSync20(join24(ctx.cwd, LINT_CONFIG_FILE))) {
5040
+ if (!existsSync21(join25(ctx.cwd, LINT_CONFIG_FILE))) {
4916
5041
  process.stderr.write(
4917
5042
  `sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
4918
5043
  `
@@ -5060,7 +5185,7 @@ ${result.stderr ?? ""}`);
5060
5185
  let stub;
5061
5186
  try {
5062
5187
  stub = parseJsonc(
5063
- readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
5188
+ readFileSync20(join25(cwd, LINT_CONFIG_FILE), "utf8"),
5064
5189
  LINT_CONFIG_FILE
5065
5190
  );
5066
5191
  } catch {
@@ -5070,7 +5195,7 @@ ${result.stderr ?? ""}`);
5070
5195
  for (const entry of stub.extends ?? []) {
5071
5196
  try {
5072
5197
  const preset = parseJsonc(
5073
- readFileSync19(join24(cwd, entry), "utf8"),
5198
+ readFileSync20(join25(cwd, entry), "utf8"),
5074
5199
  entry
5075
5200
  );
5076
5201
  for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
@@ -5140,14 +5265,14 @@ ${result.stderr ?? ""}`);
5140
5265
  let parsed;
5141
5266
  try {
5142
5267
  parsed = parseJsonc(
5143
- readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
5268
+ readFileSync20(join25(cwd, LINT_CONFIG_FILE), "utf8"),
5144
5269
  LINT_CONFIG_FILE
5145
5270
  );
5146
5271
  } catch {
5147
5272
  return void 0;
5148
5273
  }
5149
5274
  const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
5150
- return targets.find((target) => !existsSync20(join24(cwd, target)));
5275
+ return targets.find((target) => !existsSync21(join25(cwd, target)));
5151
5276
  }
5152
5277
  /** Announce what is not enforced, so reduced coverage is never silent. */
5153
5278
  announceDisabled(preset) {
@@ -5179,9 +5304,9 @@ function registerLint() {
5179
5304
 
5180
5305
  // src/roles/typescript/adapters/tsc/tsc.adapter.ts
5181
5306
  import { spawnSync as spawnSync5 } from "child_process";
5182
- import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
5307
+ import { existsSync as existsSync22, readFileSync as readFileSync22 } from "fs";
5183
5308
  import { createRequire as createRequire4 } from "module";
5184
- import { join as join26 } from "path";
5309
+ import { join as join27 } from "path";
5185
5310
 
5186
5311
  // src/roles/typescript/presets/nest.json
5187
5312
  var nest_default2 = {
@@ -5362,8 +5487,8 @@ function readTsconfigAdoption(cwd) {
5362
5487
  }
5363
5488
 
5364
5489
  // src/roles/typescript/adapters/tsc/plan.ts
5365
- import { readFileSync as readFileSync20 } from "fs";
5366
- import { join as join25 } from "path";
5490
+ import { readFileSync as readFileSync21 } from "fs";
5491
+ import { join as join26 } from "path";
5367
5492
 
5368
5493
  // src/roles/typescript/typecheck-script.ts
5369
5494
  var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
@@ -5456,7 +5581,7 @@ function planAdoption(context) {
5456
5581
  };
5457
5582
  }
5458
5583
  const existing = parseJsonc(
5459
- readFileSync20(join25(context.cwd, target.path), "utf8"),
5584
+ readFileSync21(join26(context.cwd, target.path), "utf8"),
5460
5585
  target.path
5461
5586
  );
5462
5587
  const extendsChain = composeExtends(existing.extends, preset);
@@ -5594,7 +5719,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
5594
5719
  let chain;
5595
5720
  try {
5596
5721
  const parsed = parseJsonc(
5597
- readFileSync21(join26(cwd, target.path), "utf8"),
5722
+ readFileSync22(join27(cwd, target.path), "utf8"),
5598
5723
  target.path
5599
5724
  );
5600
5725
  chain = parsed.extends;
@@ -5607,7 +5732,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
5607
5732
  );
5608
5733
  if (preset === void 0) return void 0;
5609
5734
  try {
5610
- createRequire4(join26(cwd, "noop.js")).resolve(preset);
5735
+ createRequire4(join27(cwd, "noop.js")).resolve(preset);
5611
5736
  return void 0;
5612
5737
  } catch {
5613
5738
  return preset;
@@ -5703,7 +5828,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
5703
5828
  referencedProjects(cwd, config) {
5704
5829
  try {
5705
5830
  const parsed = parseJsonc(
5706
- readFileSync21(join26(cwd, config), "utf8"),
5831
+ readFileSync22(join27(cwd, config), "utf8"),
5707
5832
  config
5708
5833
  );
5709
5834
  const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
@@ -5719,7 +5844,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
5719
5844
  * check.
5720
5845
  */
5721
5846
  typecheckTarget(cwd) {
5722
- if (existsSync21(join26(cwd, "tsconfig.json"))) return "tsconfig.json";
5847
+ if (existsSync22(join27(cwd, "tsconfig.json"))) return "tsconfig.json";
5723
5848
  const target = resolveTsconfigTarget(cwd);
5724
5849
  return target.reason === "none" ? null : target.path;
5725
5850
  }
@@ -5923,7 +6048,7 @@ function replaceLines(current, replacements) {
5923
6048
  }
5924
6049
 
5925
6050
  // src/core/apply-plan.ts
5926
- import { existsSync as existsSync22, readFileSync as readFileSync22, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
6051
+ import { existsSync as existsSync23, readFileSync as readFileSync23, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
5927
6052
  import { resolve as resolve4, sep } from "path";
5928
6053
  import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
5929
6054
 
@@ -5942,7 +6067,7 @@ function resolveWithinRoot(cwd, relativePath) {
5942
6067
  return absolutePath;
5943
6068
  }
5944
6069
  function readIfExists(absolutePath) {
5945
- return existsSync22(absolutePath) ? readFileSync22(absolutePath, "utf8") : void 0;
6070
+ return existsSync23(absolutePath) ? readFileSync23(absolutePath, "utf8") : void 0;
5946
6071
  }
5947
6072
  function* leaves(value, prefix = []) {
5948
6073
  for (const [key, keyValue] of Object.entries(value)) {
@@ -6060,8 +6185,8 @@ function applyPlan(cwd, plan3) {
6060
6185
  }
6061
6186
 
6062
6187
  // src/core/config/preset-evidence.ts
6063
- import { existsSync as existsSync23, readdirSync as readdirSync3, readFileSync as readFileSync23 } from "fs";
6064
- import { join as join27 } from "path";
6188
+ import { existsSync as existsSync24, readdirSync as readdirSync3, readFileSync as readFileSync24 } from "fs";
6189
+ import { join as join28 } from "path";
6065
6190
  var PATH_SIGNALS = [
6066
6191
  {
6067
6192
  preset: "nest",
@@ -6075,11 +6200,11 @@ var DEPENDENCY_SIGNALS = [
6075
6200
  { preset: "nest", pattern: /^@nestjs\// }
6076
6201
  ];
6077
6202
  function dependencyNames(cwd) {
6078
- const path = join27(cwd, "package.json");
6079
- if (!existsSync23(path)) return [];
6203
+ const path = join28(cwd, "package.json");
6204
+ if (!existsSync24(path)) return [];
6080
6205
  try {
6081
6206
  const manifest = parseJsonc(
6082
- readFileSync23(path, "utf8"),
6207
+ readFileSync24(path, "utf8"),
6083
6208
  path
6084
6209
  );
6085
6210
  return [
@@ -6102,7 +6227,7 @@ function declaresJsx(cwd) {
6102
6227
  for (const name of entries) {
6103
6228
  try {
6104
6229
  const config = parseJsonc(
6105
- readFileSync23(join27(cwd, name), "utf8"),
6230
+ readFileSync24(join28(cwd, name), "utf8"),
6106
6231
  name
6107
6232
  );
6108
6233
  if (config.compilerOptions?.jsx !== void 0) return true;
@@ -6266,6 +6391,7 @@ export {
6266
6391
  readOwnVersion,
6267
6392
  readProjectPackageJson,
6268
6393
  moduleName,
6394
+ buildConfigFile,
6269
6395
  VERBS,
6270
6396
  TARGETS,
6271
6397
  LONG_RUNNING_TARGETS,
@@ -6281,4 +6407,4 @@ export {
6281
6407
  detectFramework,
6282
6408
  dispatch
6283
6409
  };
6284
- //# sourceMappingURL=chunk-A25QVPXF.js.map
6410
+ //# sourceMappingURL=chunk-6RH3LZQZ.js.map
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-A25QVPXF.js";
10
+ } from "./chunk-6RH3LZQZ.js";
11
11
  export {
12
12
  BaseAdapter,
13
13
  all,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.2.0-alpha.8",
3
+ "version": "1.2.0-alpha.9",
4
4
  "description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
5
5
  "type": "module",
6
6
  "license": "MIT",