@hublo/sentinel 1.4.0-alpha.26 → 1.4.0-alpha.28

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.
@@ -1,9 +1,32 @@
1
+ import {
2
+ JEST_CONFIG_FILES,
3
+ SENTINEL_OWNED_TEST_PACKAGES,
4
+ SENTINEL_TEST_COMMAND,
5
+ TEST_SCRIPT_NAME,
6
+ adoptedPackageName,
7
+ binFromOwnInstall,
8
+ binSearchPath,
9
+ hasReference,
10
+ listTestFiles,
11
+ manifestOperation,
12
+ manifestTargets,
13
+ moduleScripts,
14
+ readTestAdoption,
15
+ readTestConfigSource,
16
+ resolveBin,
17
+ resolveVitest,
18
+ rewriteVitestScript,
19
+ selfCommand,
20
+ suiteOf,
21
+ testConfigFile,
22
+ testTarget,
23
+ testToolchain
24
+ } from "./chunk-WYWE4VIL.js";
1
25
  import {
2
26
  DEFAULT_MAX_DIAGNOSTICS,
3
27
  WORKSPACE_ROOT_MARKER,
4
28
  findWorkspaceRoot,
5
29
  moduleName,
6
- readNxProjectName,
7
30
  readOwnPackage,
8
31
  readProjectPackageJson
9
32
  } from "./chunk-NW6UHNYX.js";
@@ -91,7 +114,7 @@ var BaseAdapter = class {
91
114
  };
92
115
 
93
116
  // src/core/domain.ts
94
- var VERBS = ["run", "inspect", "init", "migrate", "report", "status"];
117
+ var VERBS = ["run", "inspect", "init", "migrate", "validate", "report", "status"];
95
118
  var TARGETS = [
96
119
  "lint",
97
120
  "format",
@@ -196,8 +219,8 @@ function workspaceRootHops(cwd, target) {
196
219
  }
197
220
 
198
221
  // src/roles/build/read-adoption.ts
199
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
200
- import { join as join4 } from "node:path";
222
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
223
+ import { join as join3 } from "node:path";
201
224
 
202
225
  // src/core/config/module-type.ts
203
226
  function esmReadiness(fileName, packageType) {
@@ -210,68 +233,14 @@ function esmReadiness(fileName, packageType) {
210
233
  };
211
234
  }
212
235
 
213
- // src/core/config/manifest.ts
214
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
215
- import { basename, join as join2 } from "node:path";
216
- function moduleScripts(cwd) {
217
- try {
218
- const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
219
- return pkg.scripts ?? {};
220
- } catch {
221
- return {};
222
- }
223
- }
224
- function manifestTargets(cwd) {
225
- try {
226
- const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
227
- return pkg.nx?.targets ?? {};
228
- } catch {
229
- return {};
230
- }
231
- }
232
- function moduleName2(cwd) {
233
- try {
234
- const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
235
- return pkg.name;
236
- } catch {
237
- return void 0;
238
- }
239
- }
240
- function isSelfAdoption(cwd) {
241
- return moduleName2(cwd) === readOwnPackage().name;
242
- }
243
- function selfCommand(cwd, flags) {
244
- const own = readOwnPackage();
245
- if (!isSelfAdoption(cwd) || !own.bin) return void 0;
246
- return `node ${own.bin.replace(/^\.\//, "")} ${flags}`;
247
- }
248
- function adoptedPackageName(cwd) {
249
- return readProjectPackageJson(cwd).name ?? readNxProjectName(cwd) ?? basename(cwd);
250
- }
251
- function manifestOperation(cwd, scripts) {
252
- const own = readOwnPackage();
253
- const value = {
254
- scripts,
255
- devDependencies: { [own.name]: isSelfAdoption(cwd) ? "link:." : own.version }
256
- };
257
- if (existsSync2(join2(cwd, "package.json"))) {
258
- return { kind: "merge-json", path: "package.json", value };
259
- }
260
- return {
261
- kind: "merge-json",
262
- path: "package.json",
263
- value: { name: adoptedPackageName(cwd), private: true, ...value }
264
- };
265
- }
266
-
267
236
  // src/core/config/project-json.ts
268
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
269
- import { join as join3 } from "node:path";
237
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
238
+ import { join as join2 } from "node:path";
270
239
  function projectTargets(cwd) {
271
- const path = join3(cwd, "project.json");
272
- if (!existsSync3(path)) return void 0;
240
+ const path = join2(cwd, "project.json");
241
+ if (!existsSync2(path)) return void 0;
273
242
  try {
274
- return JSON.parse(readFileSync3(path, "utf8")).targets;
243
+ return JSON.parse(readFileSync2(path, "utf8")).targets;
275
244
  } catch {
276
245
  return void 0;
277
246
  }
@@ -374,12 +343,12 @@ var DEV_SCRIPT_NAME = "serve";
374
343
  var SENTINEL_DEV_COMMAND = "sentinel --run --dev";
375
344
  var BUILD_OUTPUT_CANDIDATES = ["dist", ".output", "build"];
376
345
  function buildTarget(options) {
377
- const { moduleName: moduleName3, configFiles = BUILD_CONFIG_FILES, outputs } = options;
346
+ const { moduleName: moduleName2, configFiles = BUILD_CONFIG_FILES, outputs } = options;
378
347
  return {
379
348
  [BUILD_SCRIPT_NAME]: {
380
349
  executor: "nx:run-commands",
381
350
  options: {
382
- command: `pnpm --filter ${moduleName3} run ${BUILD_SCRIPT_NAME}`,
351
+ command: `pnpm --filter ${moduleName2} run ${BUILD_SCRIPT_NAME}`,
383
352
  forwardAllArgs: false
384
353
  },
385
354
  cache: true,
@@ -454,7 +423,7 @@ var NOT_ADOPTED = (configFile, unreadable = null) => ({
454
423
  unreadable
455
424
  });
456
425
  function buildConfigFile(cwd) {
457
- return BUILD_CONFIG_FILES.find((name) => existsSync4(join4(cwd, name)));
426
+ return BUILD_CONFIG_FILES.find((name) => existsSync3(join3(cwd, name)));
458
427
  }
459
428
  function importsSpecifier(source, specifier) {
460
429
  const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -480,7 +449,7 @@ function readBuildConfigSource(cwd) {
480
449
  const configFile = buildConfigFile(cwd);
481
450
  if (!configFile) return void 0;
482
451
  try {
483
- return readFileSync4(join4(cwd, configFile), "utf8");
452
+ return readFileSync3(join3(cwd, configFile), "utf8");
484
453
  } catch {
485
454
  return void 0;
486
455
  }
@@ -490,7 +459,7 @@ function readBuildAdoption(cwd) {
490
459
  if (!configFile) return NOT_ADOPTED(null);
491
460
  let source;
492
461
  try {
493
- source = readFileSync4(join4(cwd, configFile), "utf8");
462
+ source = readFileSync3(join3(cwd, configFile), "utf8");
494
463
  } catch (error) {
495
464
  return NOT_ADOPTED(configFile, error instanceof Error ? error.message : String(error));
496
465
  }
@@ -526,7 +495,7 @@ function readBuildAdoption(cwd) {
526
495
  import { spawnSync } from "node:child_process";
527
496
 
528
497
  // src/core/config/tool-args.ts
529
- import { existsSync as existsSync5 } from "node:fs";
498
+ import { existsSync as existsSync4 } from "node:fs";
530
499
  import { isAbsolute, resolve as resolve2 } from "node:path";
531
500
  function splitToolArgs(cwd, toolArgs = [], { valueFlags = [] } = {}) {
532
501
  const takesValue = new Set(valueFlags);
@@ -536,53 +505,20 @@ function splitToolArgs(cwd, toolArgs = [], { valueFlags = [] } = {}) {
536
505
  for (const arg of toolArgs) {
537
506
  const looksLikeOption = arg.startsWith("-");
538
507
  const target = isAbsolute(arg) ? arg : resolve2(cwd, arg);
539
- if (!previousTakesValue && !looksLikeOption && existsSync5(target)) paths.push(arg);
508
+ if (!previousTakesValue && !looksLikeOption && existsSync4(target)) paths.push(arg);
540
509
  else options.push(arg);
541
510
  previousTakesValue = !arg.includes("=") && takesValue.has(arg);
542
511
  }
543
512
  return { options, paths };
544
513
  }
545
514
 
546
- // src/shared/resolve-bin.ts
547
- import { existsSync as existsSync6 } from "node:fs";
548
- import { createRequire } from "node:module";
549
- import { delimiter, dirname, join as join5 } from "node:path";
550
- var require2 = createRequire(import.meta.url);
551
- function resolveBin(fromDir, name) {
552
- let dir = fromDir;
553
- for (; ; ) {
554
- const candidate = join5(dir, "node_modules", ".bin", name);
555
- if (existsSync6(candidate)) return candidate;
556
- const parent = dirname(dir);
557
- if (parent === dir) return void 0;
558
- dir = parent;
559
- }
560
- }
561
- function binFromOwnInstall(packageName, binName) {
562
- try {
563
- const manifest = require2.resolve(`${packageName}/package.json`);
564
- const bin = require2(manifest).bin;
565
- const relative9 = typeof bin === "string" ? bin : bin?.[binName];
566
- if (!relative9) return void 0;
567
- const executable = join5(dirname(manifest), relative9);
568
- return existsSync6(executable) ? executable : void 0;
569
- } catch {
570
- return void 0;
571
- }
572
- }
573
- function binSearchPath(cwd) {
574
- return [`${cwd}${delimiter}node_modules/.bin (and parents)`, "sentinel's own install"].join(
575
- " then "
576
- );
577
- }
578
-
579
515
  // src/roles/build/plan.ts
580
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
581
- import { join as join10 } from "node:path";
516
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
517
+ import { join as join8 } from "node:path";
582
518
 
583
519
  // src/core/config/existing-command.ts
584
- import { readFileSync as readFileSync5 } from "node:fs";
585
- import { join as join6 } from "node:path";
520
+ import { readFileSync as readFileSync4 } from "node:fs";
521
+ import { join as join4 } from "node:path";
586
522
 
587
523
  // src/shared/jsonc.ts
588
524
  import { parse, printParseErrorCode } from "jsonc-parser";
@@ -601,7 +537,7 @@ function nxTargetCommand(cwd, target) {
601
537
  let project;
602
538
  try {
603
539
  project = parseJsonc(
604
- readFileSync5(join6(cwd, "project.json"), "utf8"),
540
+ readFileSync4(join4(cwd, "project.json"), "utf8"),
605
541
  "project.json"
606
542
  );
607
543
  } catch {
@@ -632,8 +568,8 @@ function existingCommand(cwd, target) {
632
568
  }
633
569
 
634
570
  // src/core/config/nx-target.ts
635
- import { existsSync as existsSync7 } from "node:fs";
636
- import { join as join7 } from "node:path";
571
+ import { existsSync as existsSync5 } from "node:fs";
572
+ import { join as join5 } from "node:path";
637
573
  function nxTargetMetadataOperations(options) {
638
574
  const { targets } = options;
639
575
  if (Object.keys(targets).length === 0) return [];
@@ -644,7 +580,7 @@ function nxTargetOperations(options) {
644
580
  const names = Object.keys(targets);
645
581
  if (names.length === 0) return [];
646
582
  const operations = [];
647
- if (existsSync7(join7(cwd, "project.json"))) {
583
+ if (existsSync5(join5(cwd, "project.json"))) {
648
584
  operations.push({
649
585
  kind: "remove-json-keys",
650
586
  path: "project.json",
@@ -698,8 +634,8 @@ function keepsOtherCommands(script, sentinelCommand) {
698
634
  }
699
635
 
700
636
  // src/roles/format/read-adoption.ts
701
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
702
- import { join as join8 } from "node:path";
637
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
638
+ import { join as join6 } from "node:path";
703
639
 
704
640
  // src/roles/format/config-policy.ts
705
641
  var FORMAT_CONFIG_FILE = ".oxfmtrc.json";
@@ -805,11 +741,11 @@ function sameValue(a, b) {
805
741
  return JSON.stringify(a) === JSON.stringify(b);
806
742
  }
807
743
  function readFormatAdoption(cwd) {
808
- const path = join8(cwd, FORMAT_CONFIG_FILE);
809
- if (!existsSync8(path)) return NOT_ADOPTED2(null);
744
+ const path = join6(cwd, FORMAT_CONFIG_FILE);
745
+ if (!existsSync6(path)) return NOT_ADOPTED2(null);
810
746
  let parsed;
811
747
  try {
812
- parsed = parseJsonc(readFileSync6(path, "utf8"), FORMAT_CONFIG_FILE);
748
+ parsed = parseJsonc(readFileSync5(path, "utf8"), FORMAT_CONFIG_FILE);
813
749
  } catch (error) {
814
750
  const reason = error instanceof Error ? error.message : String(error);
815
751
  return NOT_ADOPTED2(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
@@ -910,8 +846,8 @@ function devTargetOperations(cwd, devScript) {
910
846
  }
911
847
 
912
848
  // src/roles/build/nest/adopt.ts
913
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
914
- import { join as join9 } from "node:path";
849
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
850
+ import { join as join7 } from "node:path";
915
851
  var NEST_CONFIG_FILE = "vite.config.mts";
916
852
  function asSource(value, depth) {
917
853
  const pad = " ".repeat(depth);
@@ -960,9 +896,9 @@ function nestBuildOutputs(target) {
960
896
  }
961
897
  var OUT_DIR_IN_CONFIG = /^\s*outDir:\s*'([^']+)'/m;
962
898
  function nestOutputsFromConfig(cwd) {
963
- const configPath = join9(cwd, NEST_CONFIG_FILE);
964
- if (!existsSync9(configPath)) return void 0;
965
- const outDir = OUT_DIR_IN_CONFIG.exec(readFileSync7(configPath, "utf8"))?.[1];
899
+ const configPath = join7(cwd, NEST_CONFIG_FILE);
900
+ if (!existsSync7(configPath)) return void 0;
901
+ const outDir = OUT_DIR_IN_CONFIG.exec(readFileSync6(configPath, "utf8"))?.[1];
966
902
  return outDir === void 0 ? void 0 : [`{workspaceRoot}/${outDir}`];
967
903
  }
968
904
  function nestAdoptionOperations(cwd, target) {
@@ -1267,9 +1203,9 @@ function planNestService(context, webpackTarget) {
1267
1203
  };
1268
1204
  }
1269
1205
  function readConfigSource(cwd, configFile) {
1270
- if (!existsSync10(join10(cwd, configFile))) return "";
1206
+ if (!existsSync8(join8(cwd, configFile))) return "";
1271
1207
  try {
1272
- return readFileSync8(join10(cwd, configFile), "utf8");
1208
+ return readFileSync7(join8(cwd, configFile), "utf8");
1273
1209
  } catch {
1274
1210
  return "";
1275
1211
  }
@@ -1454,21 +1390,21 @@ var ViteDevAdapter = class extends ViteRoleAdapter {
1454
1390
  };
1455
1391
 
1456
1392
  // src/roles/build/declared-outputs.ts
1457
- import { existsSync as existsSync11, rmSync, statSync, writeFileSync } from "node:fs";
1458
- import { join as join11 } from "node:path";
1393
+ import { existsSync as existsSync9, rmSync, statSync, writeFileSync } from "node:fs";
1394
+ import { join as join9 } from "node:path";
1459
1395
  function declaredBuildTarget(cwd) {
1460
1396
  return readProjectPackageJson(cwd).nx?.targets?.[BUILD_SCRIPT_NAME];
1461
1397
  }
1462
1398
  function resolveOutputToken(token, cwd) {
1463
- if (token.startsWith("{projectRoot}/")) return join11(cwd, token.slice("{projectRoot}/".length));
1399
+ if (token.startsWith("{projectRoot}/")) return join9(cwd, token.slice("{projectRoot}/".length));
1464
1400
  if (token.startsWith("{workspaceRoot}/")) {
1465
1401
  const root = findWorkspaceRoot(cwd);
1466
- return root === void 0 ? void 0 : join11(root, token.slice("{workspaceRoot}/".length));
1402
+ return root === void 0 ? void 0 : join9(root, token.slice("{workspaceRoot}/".length));
1467
1403
  }
1468
- return join11(cwd, token);
1404
+ return join9(cwd, token);
1469
1405
  }
1470
1406
  function buildStamp(cwd) {
1471
- const marker = join11(cwd, `.sentinel-build-stamp-${process.pid}`);
1407
+ const marker = join9(cwd, `.sentinel-build-stamp-${process.pid}`);
1472
1408
  try {
1473
1409
  writeFileSync(marker, "");
1474
1410
  const stamp = statSync(marker).mtimeMs;
@@ -1479,7 +1415,7 @@ function buildStamp(cwd) {
1479
1415
  }
1480
1416
  }
1481
1417
  function writtenSince(path, startedAt) {
1482
- if (!existsSync11(path)) return false;
1418
+ if (!existsSync9(path)) return false;
1483
1419
  try {
1484
1420
  return statSync(path).mtimeMs >= startedAt;
1485
1421
  } catch {
@@ -1628,12 +1564,12 @@ function registerBuild() {
1628
1564
 
1629
1565
  // src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
1630
1566
  import { spawnSync as spawnSync3 } from "node:child_process";
1631
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
1632
- import { join as join16 } from "node:path";
1567
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
1568
+ import { join as join14 } from "node:path";
1633
1569
 
1634
1570
  // src/core/config/has-source.ts
1635
1571
  import { readdirSync } from "node:fs";
1636
- import { extname, join as join12, relative as relative2 } from "node:path";
1572
+ import { extname, join as join10, relative as relative2 } from "node:path";
1637
1573
  var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
1638
1574
  "node_modules",
1639
1575
  "dist",
@@ -1684,7 +1620,7 @@ function hasSourceFiles(cwd, extensions) {
1684
1620
  }
1685
1621
  for (const entry of entries) {
1686
1622
  if (entry.isDirectory()) {
1687
- if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join12(dir, entry.name));
1623
+ if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join10(dir, entry.name));
1688
1624
  continue;
1689
1625
  }
1690
1626
  if (wanted.has(extname(entry.name))) return true;
@@ -1705,7 +1641,7 @@ function listSourceFiles(cwd, extensions) {
1705
1641
  continue;
1706
1642
  }
1707
1643
  for (const entry of entries) {
1708
- const full = join12(dir, entry.name);
1644
+ const full = join10(dir, entry.name);
1709
1645
  if (entry.isDirectory()) {
1710
1646
  if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(full);
1711
1647
  continue;
@@ -1751,8 +1687,8 @@ function writesWhenRewritten(name, command) {
1751
1687
  var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
1752
1688
 
1753
1689
  // src/roles/format/inherited-ignores.ts
1754
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
1755
- import { join as join13 } from "node:path";
1690
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
1691
+ import { join as join11 } from "node:path";
1756
1692
  var ROOT_IGNORE_FILE = ".prettierignore";
1757
1693
  function isPattern(line) {
1758
1694
  const trimmed = line.trim();
@@ -1768,11 +1704,11 @@ function toModulePattern(pattern) {
1768
1704
  }
1769
1705
  function inheritedIgnorePatterns(workspaceRoot) {
1770
1706
  if (!workspaceRoot) return [];
1771
- const path = join13(workspaceRoot, ROOT_IGNORE_FILE);
1772
- if (!existsSync12(path)) return [];
1707
+ const path = join11(workspaceRoot, ROOT_IGNORE_FILE);
1708
+ if (!existsSync10(path)) return [];
1773
1709
  let contents;
1774
1710
  try {
1775
- contents = readFileSync9(path, "utf8");
1711
+ contents = readFileSync8(path, "utf8");
1776
1712
  } catch {
1777
1713
  return [];
1778
1714
  }
@@ -1817,13 +1753,13 @@ function needsSvelteCompiler(output) {
1817
1753
  }
1818
1754
 
1819
1755
  // src/roles/format/prettier-config.ts
1820
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
1821
- import { join as join15 } from "node:path";
1756
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
1757
+ import { join as join13 } from "node:path";
1822
1758
 
1823
1759
  // src/roles/format/resolve-oxfmt.ts
1824
- import { readFileSync as readFileSync10 } from "node:fs";
1825
- import { createRequire as createRequire2 } from "node:module";
1826
- import { dirname as dirname2, join as join14 } from "node:path";
1760
+ import { readFileSync as readFileSync9 } from "node:fs";
1761
+ import { createRequire } from "node:module";
1762
+ import { dirname, join as join12 } from "node:path";
1827
1763
  function resolveOxfmt(cwd) {
1828
1764
  return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
1829
1765
  }
@@ -1851,9 +1787,9 @@ function configSchema() {
1851
1787
  }
1852
1788
  function readConfigSchema() {
1853
1789
  try {
1854
- const manifest = createRequire2(import.meta.url).resolve("oxfmt/package.json");
1855
- const schemaPath = join14(dirname2(manifest), "configuration_schema.json");
1856
- return JSON.parse(readFileSync10(schemaPath, "utf8"));
1790
+ const manifest = createRequire(import.meta.url).resolve("oxfmt/package.json");
1791
+ const schemaPath = join12(dirname(manifest), "configuration_schema.json");
1792
+ return JSON.parse(readFileSync9(schemaPath, "utf8"));
1857
1793
  } catch {
1858
1794
  return void 0;
1859
1795
  }
@@ -1884,14 +1820,14 @@ function toOxfmtOverrides(value) {
1884
1820
  return { overrides, unresolved };
1885
1821
  }
1886
1822
  function readPrettierSettings(cwd) {
1887
- const file = PRETTIER_CONFIG_FILES.find((name) => existsSync13(join15(cwd, name)));
1823
+ const file = PRETTIER_CONFIG_FILES.find((name) => existsSync11(join13(cwd, name)));
1888
1824
  if (!file) return { options: {}, unresolved: [] };
1889
1825
  if (/\.(js|cjs|mjs)$/.test(file)) {
1890
1826
  return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
1891
1827
  }
1892
1828
  let parsed;
1893
1829
  try {
1894
- parsed = parseJsonc(readFileSync11(join15(cwd, file), "utf8"), file);
1830
+ parsed = parseJsonc(readFileSync10(join13(cwd, file), "utf8"), file);
1895
1831
  } catch {
1896
1832
  return { options: {}, file, unresolved: [`${file} could not be parsed`] };
1897
1833
  }
@@ -2002,7 +1938,7 @@ var OxfmtAdapter = class extends BaseAdapter {
2002
1938
  manifestOperation(context.cwd, this.formatScripts(context.cwd))
2003
1939
  ];
2004
1940
  const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
2005
- (name) => existsSync14(join16(context.cwd, name))
1941
+ (name) => existsSync12(join14(context.cwd, name))
2006
1942
  );
2007
1943
  for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
2008
1944
  const removableDeps = this.modulePrettierDependencies(context.cwd);
@@ -2242,7 +2178,7 @@ var OxfmtAdapter = class extends BaseAdapter {
2242
2178
  modulePrettierDependencies(cwd) {
2243
2179
  const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
2244
2180
  try {
2245
- const manifest = JSON.parse(readFileSync12(join16(cwd, "package.json"), "utf8"));
2181
+ const manifest = JSON.parse(readFileSync11(join14(cwd, "package.json"), "utf8"));
2246
2182
  return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
2247
2183
  } catch {
2248
2184
  return [];
@@ -2324,8 +2260,8 @@ function registerFormat() {
2324
2260
 
2325
2261
  // src/roles/lint/adapters/oxlint/oxlint.adapter.ts
2326
2262
  import { spawnSync as spawnSync4 } from "node:child_process";
2327
- import { existsSync as existsSync22, readFileSync as readFileSync20, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
2328
- import { join as join26 } from "node:path";
2263
+ import { existsSync as existsSync20, readFileSync as readFileSync19, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
2264
+ import { join as join24 } from "node:path";
2329
2265
 
2330
2266
  // src/core/config/deferred-rules.ts
2331
2267
  function deferredRuleNames(rules) {
@@ -2334,7 +2270,7 @@ function deferredRuleNames(rules) {
2334
2270
 
2335
2271
  // src/core/fix-report.ts
2336
2272
  import { statSync as statSync2 } from "node:fs";
2337
- import { join as join17 } from "node:path";
2273
+ import { join as join15 } from "node:path";
2338
2274
  var CONFIG_REFUSED = /failed to parse .*config|invalid config file/i;
2339
2275
  function readFailure(output) {
2340
2276
  const lines = output.split("\n").map((line2) => line2.trim());
@@ -2354,7 +2290,7 @@ function fingerprint(path) {
2354
2290
  function snapshotFiles(cwd, extensions) {
2355
2291
  const snapshot = /* @__PURE__ */ new Map();
2356
2292
  for (const file of listSourceFiles(cwd, extensions)) {
2357
- const mark = fingerprint(join17(cwd, file));
2293
+ const mark = fingerprint(join15(cwd, file));
2358
2294
  if (mark !== void 0) snapshot.set(file, mark);
2359
2295
  }
2360
2296
  return snapshot;
@@ -2362,7 +2298,7 @@ function snapshotFiles(cwd, extensions) {
2362
2298
  function changedSince(cwd, before, extensions) {
2363
2299
  const changed = [];
2364
2300
  for (const file of listSourceFiles(cwd, extensions)) {
2365
- const now = fingerprint(join17(cwd, file));
2301
+ const now = fingerprint(join15(cwd, file));
2366
2302
  if (now === void 0) continue;
2367
2303
  if (before.get(file) !== now) changed.push(file);
2368
2304
  }
@@ -2385,8 +2321,8 @@ function describeFixOutcome(outcome, toolLabel) {
2385
2321
  }
2386
2322
 
2387
2323
  // src/roles/lint/module-baseline.ts
2388
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
2389
- import { join as join18 } from "node:path";
2324
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
2325
+ import { join as join16 } from "node:path";
2390
2326
  var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
2391
2327
  var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
2392
2328
  var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
@@ -2430,11 +2366,11 @@ function describeHolds(holds) {
2430
2366
  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`;
2431
2367
  }
2432
2368
  function heldRules(cwd) {
2433
- const path = join18(cwd, LINT_BASELINE_FILE);
2434
- if (!existsSync15(path)) return [];
2369
+ const path = join16(cwd, LINT_BASELINE_FILE);
2370
+ if (!existsSync13(path)) return [];
2435
2371
  try {
2436
2372
  const parsed = parseJsonc(
2437
- readFileSync13(path, "utf8"),
2373
+ readFileSync12(path, "utf8"),
2438
2374
  LINT_BASELINE_FILE
2439
2375
  );
2440
2376
  return Object.keys(parsed.rules ?? {});
@@ -4260,14 +4196,14 @@ function downgradedRulesFor(preset) {
4260
4196
  }
4261
4197
 
4262
4198
  // src/roles/lint/extra-layers.ts
4263
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
4264
- import { isAbsolute as isAbsolute2, join as join19, resolve as resolve3 } from "node:path";
4199
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "node:fs";
4200
+ import { isAbsolute as isAbsolute2, join as join17, resolve as resolve3 } from "node:path";
4265
4201
  function ruleCount(cwd, specifier) {
4266
4202
  const path = isAbsolute2(specifier) ? specifier : resolve3(cwd, specifier);
4267
- if (!existsSync16(path)) return void 0;
4203
+ if (!existsSync14(path)) return void 0;
4268
4204
  try {
4269
4205
  const parsed = parseJsonc(
4270
- readFileSync14(path, "utf8"),
4206
+ readFileSync13(path, "utf8"),
4271
4207
  specifier
4272
4208
  );
4273
4209
  return Object.keys(parsed.rules ?? {}).length;
@@ -4287,7 +4223,7 @@ function extraLayers(cwd, extendsList) {
4287
4223
  function committedExtendsList(cwd, configFile) {
4288
4224
  try {
4289
4225
  const parsed = parseJsonc(
4290
- readFileSync14(join19(cwd, configFile), "utf8"),
4226
+ readFileSync13(join17(cwd, configFile), "utf8"),
4291
4227
  configFile
4292
4228
  );
4293
4229
  return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
@@ -4346,8 +4282,8 @@ function errorCountsByConfigRule(stdout) {
4346
4282
  }
4347
4283
 
4348
4284
  // src/core/config/read-adoption.ts
4349
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
4350
- import { join as join21 } from "node:path";
4285
+ import { existsSync as existsSync16, readFileSync as readFileSync15 } from "node:fs";
4286
+ import { join as join19 } from "node:path";
4351
4287
 
4352
4288
  // src/core/config/owned-keys.ts
4353
4289
  function presetOwnedKeys(config, permitted, presetSets) {
@@ -4362,12 +4298,12 @@ function localOnlyKeys(config, permitted, presetSets) {
4362
4298
  }
4363
4299
 
4364
4300
  // src/core/config/resolve-config-target.ts
4365
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
4366
- import { join as join20 } from "node:path";
4301
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "node:fs";
4302
+ import { join as join18 } from "node:path";
4367
4303
  function readExtends(absolutePath) {
4368
4304
  let parsed;
4369
4305
  try {
4370
- parsed = parseJsonc(readFileSync15(absolutePath, "utf8"), absolutePath);
4306
+ parsed = parseJsonc(readFileSync14(absolutePath, "utf8"), absolutePath);
4371
4307
  } catch {
4372
4308
  return [];
4373
4309
  }
@@ -4381,8 +4317,8 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
4381
4317
  let existing;
4382
4318
  let existingExtendsSomething = false;
4383
4319
  for (const candidate of candidates) {
4384
- const absolutePath = join20(moduleDir, candidate);
4385
- if (!existsSync17(absolutePath)) continue;
4320
+ const absolutePath = join18(moduleDir, candidate);
4321
+ if (!existsSync15(absolutePath)) continue;
4386
4322
  const chain = readExtends(absolutePath);
4387
4323
  if (existing === void 0) {
4388
4324
  existing = candidate;
@@ -4415,12 +4351,12 @@ var NOT_ADOPTED3 = (configFile, unreadable = null) => ({
4415
4351
  });
4416
4352
  function readAdoption(cwd, options) {
4417
4353
  const target = resolveConfigTarget(cwd, options);
4418
- if (target.reason === "none" || !existsSync18(join21(cwd, target.path))) {
4354
+ if (target.reason === "none" || !existsSync16(join19(cwd, target.path))) {
4419
4355
  return NOT_ADOPTED3(target.reason === "none" ? null : target.path);
4420
4356
  }
4421
4357
  let parsed;
4422
4358
  try {
4423
- parsed = parseJsonc(readFileSync16(join21(cwd, target.path), "utf8"), target.path);
4359
+ parsed = parseJsonc(readFileSync15(join19(cwd, target.path), "utf8"), target.path);
4424
4360
  } catch (error) {
4425
4361
  const reason = error instanceof Error ? error.message : String(error);
4426
4362
  return NOT_ADOPTED3(target.path, `${target.path} could not be parsed (${reason})`);
@@ -4454,15 +4390,15 @@ function readLintAdoption(cwd) {
4454
4390
  }
4455
4391
 
4456
4392
  // src/roles/lint/resolve-oxlint.ts
4457
- import { existsSync as existsSync19 } from "node:fs";
4458
- import { createRequire as createRequire3 } from "node:module";
4459
- import { delimiter as delimiter2, dirname as dirname3, join as join22 } from "node:path";
4393
+ import { existsSync as existsSync17 } from "node:fs";
4394
+ import { createRequire as createRequire2 } from "node:module";
4395
+ import { delimiter, dirname as dirname2, join as join20 } from "node:path";
4460
4396
  import { fileURLToPath } from "node:url";
4461
4397
  var PACKAGE_OF = {
4462
4398
  oxlint: "oxlint",
4463
4399
  tsgolint: "oxlint-tsgolint"
4464
4400
  };
4465
- var require3 = createRequire3(import.meta.url);
4401
+ var require2 = createRequire2(import.meta.url);
4466
4402
  function resolveOxlint(cwd, name = "oxlint") {
4467
4403
  return resolveBin(cwd, name) ?? binFromOwnInstall(PACKAGE_OF[name], name);
4468
4404
  }
@@ -4472,7 +4408,7 @@ function canRunTypeAware(cwd) {
4472
4408
  function oxlintPath(cwd, env) {
4473
4409
  const shim = tsgolintShim(cwd);
4474
4410
  if (!shim) return env.PATH;
4475
- return [dirname3(shim), env.PATH].filter(Boolean).join(delimiter2);
4411
+ return [dirname2(shim), env.PATH].filter(Boolean).join(delimiter);
4476
4412
  }
4477
4413
  function tsgolintShim(cwd) {
4478
4414
  const fromModule = resolveBin(cwd, "tsgolint");
@@ -4480,31 +4416,31 @@ function tsgolintShim(cwd) {
4480
4416
  const candidates = [];
4481
4417
  for (const owner of ["oxlint-tsgolint", "oxlint"]) {
4482
4418
  try {
4483
- const packageDir = dirname3(require3.resolve(`${owner}/package.json`));
4484
- candidates.push(join22(packageDir, "node_modules", ".bin", "tsgolint"));
4485
- candidates.push(join22(packageDir, "..", ".bin", "tsgolint"));
4419
+ const packageDir = dirname2(require2.resolve(`${owner}/package.json`));
4420
+ candidates.push(join20(packageDir, "node_modules", ".bin", "tsgolint"));
4421
+ candidates.push(join20(packageDir, "..", ".bin", "tsgolint"));
4486
4422
  } catch {
4487
4423
  }
4488
4424
  }
4489
4425
  try {
4490
- const ownRoot = dirname3(require3.resolve("@hublo/sentinel/package.json"));
4491
- candidates.push(join22(ownRoot, "node_modules", ".bin", "tsgolint"));
4426
+ const ownRoot = dirname2(require2.resolve("@hublo/sentinel/package.json"));
4427
+ candidates.push(join20(ownRoot, "node_modules", ".bin", "tsgolint"));
4492
4428
  } catch {
4493
- candidates.push(resolveBin(dirname3(fileURLToPath(import.meta.url)), "tsgolint") ?? "");
4429
+ candidates.push(resolveBin(dirname2(fileURLToPath(import.meta.url)), "tsgolint") ?? "");
4494
4430
  }
4495
- return candidates.find((candidate) => candidate !== "" && existsSync19(candidate));
4431
+ return candidates.find((candidate) => candidate !== "" && existsSync17(candidate));
4496
4432
  }
4497
4433
  function oxlintSearchPath(cwd) {
4498
4434
  return binSearchPath(cwd);
4499
4435
  }
4500
4436
 
4501
4437
  // src/roles/lint/adapters/oxlint/plan.ts
4502
- import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
4503
- import { join as join25 } from "node:path";
4438
+ import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
4439
+ import { join as join23 } from "node:path";
4504
4440
 
4505
4441
  // src/roles/lint/eslint-ignores.ts
4506
- import { existsSync as existsSync20, readFileSync as readFileSync17 } from "node:fs";
4507
- import { join as join23 } from "node:path";
4442
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
4443
+ import { join as join21 } from "node:path";
4508
4444
  import { parseSync as parseSync2 } from "oxc-parser";
4509
4445
 
4510
4446
  // src/shared/ast.ts
@@ -4533,11 +4469,11 @@ function readRootEslintIgnores(root) {
4533
4469
  return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
4534
4470
  }
4535
4471
  function readEslintIgnores(cwd) {
4536
- const config = ESLINT_CONFIG_FILES.map((name) => join23(cwd, name)).find((path) => existsSync20(path));
4472
+ const config = ESLINT_CONFIG_FILES.map((name) => join21(cwd, name)).find((path) => existsSync18(path));
4537
4473
  if (!config) return { patterns: [], unresolved: [] };
4538
4474
  let source;
4539
4475
  try {
4540
- source = readFileSync17(config, "utf8");
4476
+ source = readFileSync16(config, "utf8");
4541
4477
  } catch {
4542
4478
  return { patterns: [], unresolved: [] };
4543
4479
  }
@@ -4628,8 +4564,8 @@ function lintPresetFor(preset) {
4628
4564
  }
4629
4565
 
4630
4566
  // src/roles/lint/rename-suppressions.ts
4631
- import { readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync3 } from "node:fs";
4632
- import { join as join24, relative as relative3 } from "node:path";
4567
+ import { readdirSync as readdirSync2, readFileSync as readFileSync17, statSync as statSync3 } from "node:fs";
4568
+ import { join as join22, relative as relative3 } from "node:path";
4633
4569
  var SOURCE_EXTENSIONS = [
4634
4570
  ".ts",
4635
4571
  ".tsx",
@@ -4676,7 +4612,7 @@ function* sourceFiles(dir) {
4676
4612
  return;
4677
4613
  }
4678
4614
  for (const entry of entries) {
4679
- const full = join24(dir, entry);
4615
+ const full = join22(dir, entry);
4680
4616
  let isDirectory;
4681
4617
  try {
4682
4618
  isDirectory = statSync3(full).isDirectory();
@@ -4696,7 +4632,7 @@ function findSuppressionRenames(cwd, renames) {
4696
4632
  for (const file of sourceFiles(cwd)) {
4697
4633
  let content;
4698
4634
  try {
4699
- content = readFileSync18(file, "utf8");
4635
+ content = readFileSync17(file, "utf8");
4700
4636
  } catch {
4701
4637
  continue;
4702
4638
  }
@@ -4824,7 +4760,7 @@ function plan2(context) {
4824
4760
  keys: removableDeps
4825
4761
  });
4826
4762
  }
4827
- const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync21(join25(context.cwd, name)));
4763
+ const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync19(join23(context.cwd, name)));
4828
4764
  for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
4829
4765
  operations.push(
4830
4766
  ...nxTargetOperations({
@@ -4922,7 +4858,7 @@ function lintScripts(cwd) {
4922
4858
  function committedExtends(cwd) {
4923
4859
  try {
4924
4860
  const parsed = parseJsonc(
4925
- readFileSync19(join25(cwd, LINT_CONFIG_FILE), "utf8"),
4861
+ readFileSync18(join23(cwd, LINT_CONFIG_FILE), "utf8"),
4926
4862
  LINT_CONFIG_FILE
4927
4863
  );
4928
4864
  return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
@@ -4933,7 +4869,7 @@ function committedExtends(cwd) {
4933
4869
  function committedIgnorePatterns(cwd) {
4934
4870
  try {
4935
4871
  const parsed = parseJsonc(
4936
- readFileSync19(join25(cwd, LINT_CONFIG_FILE), "utf8"),
4872
+ readFileSync18(join23(cwd, LINT_CONFIG_FILE), "utf8"),
4937
4873
  LINT_CONFIG_FILE
4938
4874
  );
4939
4875
  return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
@@ -4945,7 +4881,7 @@ function moduleEslintDependencies(cwd) {
4945
4881
  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/");
4946
4882
  let manifest;
4947
4883
  try {
4948
- manifest = JSON.parse(readFileSync19(join25(cwd, "package.json"), "utf8"));
4884
+ manifest = JSON.parse(readFileSync18(join23(cwd, "package.json"), "utf8"));
4949
4885
  } catch {
4950
4886
  return [];
4951
4887
  }
@@ -5079,20 +5015,20 @@ ${result.stderr ?? ""}`);
5079
5015
  * build the developer can see, rather than a silent half-adoption they cannot.
5080
5016
  */
5081
5017
  writeModuleBaseline(ctx, oxlint, env) {
5082
- const configPath = join26(ctx.cwd, LINT_CONFIG_FILE);
5083
- const baselinePath = join26(ctx.cwd, LINT_BASELINE_FILE);
5084
- if (!existsSync22(configPath)) return;
5018
+ const configPath = join24(ctx.cwd, LINT_CONFIG_FILE);
5019
+ const baselinePath = join24(ctx.cwd, LINT_BASELINE_FILE);
5020
+ if (!existsSync20(configPath)) return;
5085
5021
  let config;
5086
5022
  try {
5087
5023
  config = parseJsonc(
5088
- readFileSync20(configPath, "utf8"),
5024
+ readFileSync19(configPath, "utf8"),
5089
5025
  LINT_CONFIG_FILE
5090
5026
  );
5091
5027
  } catch {
5092
5028
  return;
5093
5029
  }
5094
5030
  const current = Array.isArray(config.extends) ? config.extends : [];
5095
- const measurePath = join26(ctx.cwd, LINT_MEASURE_FILE);
5031
+ const measurePath = join24(ctx.cwd, LINT_MEASURE_FILE);
5096
5032
  let measured;
5097
5033
  try {
5098
5034
  writeFileSync2(
@@ -5140,7 +5076,7 @@ ${result.stderr ?? ""}`);
5140
5076
  }
5141
5077
  }
5142
5078
  async run(ctx) {
5143
- if (!existsSync22(join26(ctx.cwd, LINT_CONFIG_FILE))) {
5079
+ if (!existsSync20(join24(ctx.cwd, LINT_CONFIG_FILE))) {
5144
5080
  process.stderr.write(
5145
5081
  `sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
5146
5082
  `
@@ -5288,7 +5224,7 @@ ${result.stderr ?? ""}`);
5288
5224
  let stub;
5289
5225
  try {
5290
5226
  stub = parseJsonc(
5291
- readFileSync20(join26(cwd, LINT_CONFIG_FILE), "utf8"),
5227
+ readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
5292
5228
  LINT_CONFIG_FILE
5293
5229
  );
5294
5230
  } catch {
@@ -5298,7 +5234,7 @@ ${result.stderr ?? ""}`);
5298
5234
  for (const entry of stub.extends ?? []) {
5299
5235
  try {
5300
5236
  const preset = parseJsonc(
5301
- readFileSync20(join26(cwd, entry), "utf8"),
5237
+ readFileSync19(join24(cwd, entry), "utf8"),
5302
5238
  entry
5303
5239
  );
5304
5240
  for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
@@ -5368,14 +5304,14 @@ ${result.stderr ?? ""}`);
5368
5304
  let parsed;
5369
5305
  try {
5370
5306
  parsed = parseJsonc(
5371
- readFileSync20(join26(cwd, LINT_CONFIG_FILE), "utf8"),
5307
+ readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
5372
5308
  LINT_CONFIG_FILE
5373
5309
  );
5374
5310
  } catch {
5375
5311
  return void 0;
5376
5312
  }
5377
5313
  const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
5378
- return targets.find((target) => !existsSync22(join26(cwd, target)));
5314
+ return targets.find((target) => !existsSync20(join24(cwd, target)));
5379
5315
  }
5380
5316
  /** Announce what is not enforced, so reduced coverage is never silent. */
5381
5317
  announceDisabled(preset) {
@@ -5407,103 +5343,29 @@ function registerLint() {
5407
5343
 
5408
5344
  // src/roles/test/adapters/vitest/vitest.adapter.ts
5409
5345
  import { spawnSync as spawnSync5 } from "node:child_process";
5410
- import { isAbsolute as isAbsolute3, resolve as resolve9 } from "node:path";
5411
-
5412
- // src/roles/test/presets/toolchain.json
5413
- var toolchain_default2 = {
5414
- $comment: "The test toolchain sentinel owns. Same shape and same rule as the build one: a single table drives what `--init` removes from a module's package.json, which import specifiers move, and the name each default import is re-exported under, so the three cannot disagree.",
5415
- owned: [
5416
- {
5417
- package: "vitest",
5418
- specifier: "vitest/config",
5419
- named: ["defineConfig", "mergeConfig"],
5420
- types: ["ConfigEnv", "TestUserConfig", "ViteUserConfig"],
5421
- why: "the runner itself, declared at the workspace ROOT today, which is what stops a module being testable on its own. Note the package is `vitest` and the specifier is `vitest/config`: the same split as nitro on the build side, and the reason one table holds both."
5422
- },
5423
- {
5424
- package: "vite",
5425
- specifier: "vite",
5426
- named: ["loadEnv"],
5427
- why: "`loadEnv` has no equivalent in `vitest/config`, and two of the six configs import it from vite to build their test env. Re-exported here rather than claimed: the BUILD role owns the `vite` package, and two roles owning one package is how a version conflict gets written down twice."
5428
- }
5429
- ]
5430
- };
5431
-
5432
- // src/roles/test/preset-data.ts
5433
- var OWNED_TEST_PACKAGES = toolchain_default2.owned;
5434
-
5435
- // src/roles/test/config-policy.ts
5436
- var TEST_PRESET_SPECIFIERS = {
5437
- react: "@hublo/sentinel/test/react",
5438
- nest: "@hublo/sentinel/test/nest"
5439
- };
5440
- function testPresetSpecifier(preset) {
5441
- return TEST_PRESET_SPECIFIERS[preset] ?? TEST_PRESET_SPECIFIERS.react;
5442
- }
5443
- var ALL_TEST_PRESET_SPECIFIERS = Object.values(TEST_PRESET_SPECIFIERS);
5444
- var TEST_PRESET_SPECIFIER = TEST_PRESET_SPECIFIERS.react;
5445
- var TEST_CONFIG_FILES = [
5446
- "vitest.config.ts",
5447
- "vitest.config.mts",
5448
- "vitest.config.js",
5449
- "vitest.config.mjs"
5450
- ];
5451
- var TEST_OWNED_PACKAGES = OWNED_TEST_PACKAGES;
5452
- var SENTINEL_OWNED_TEST_PACKAGES = TEST_OWNED_PACKAGES.filter(
5453
- (entry) => entry.package !== "vite"
5454
- ).map((entry) => entry.package);
5455
- function testToolchain(preset) {
5456
- return { presetSpecifier: testPresetSpecifier(preset), owned: TEST_OWNED_PACKAGES };
5457
- }
5458
- var TEST_SCRIPT_NAME = "test";
5459
- var SENTINEL_TEST_COMMAND = "sentinel --run --test --";
5460
- var VITEST_INVOCATION = /(^|&&\s*|\|\|\s*|;\s*)(?:npx\s+|pnpm\s+(?:exec\s+|dlx\s+)?)?vitest\b([^&|;]*)/g;
5461
- function rewriteVitestScript(command) {
5462
- return command.replace(VITEST_INVOCATION, (_match, lead, tail) => {
5463
- const args = tail.trim().split(/\s+/).filter(Boolean);
5464
- const oneShot = args[0] === "run" || args.includes("--run");
5465
- const rest = args.filter((arg, index) => !(index === 0 && arg === "run") && arg !== "--run");
5466
- const passthrough = oneShot ? rest : ["--watch", ...rest];
5467
- const suffix = passthrough.length > 0 ? ` -- ${passthrough.join(" ")}` : "";
5468
- const trailing = /\s$/.test(tail) ? " " : "";
5469
- return `${lead}sentinel --run --test${suffix}${trailing}`;
5470
- });
5471
- }
5472
- function testTarget(options) {
5473
- const { name, configFiles, outputs, dependsOn, configurations } = options;
5474
- const cached = outputs !== void 0 && outputs.length > 0;
5475
- return {
5476
- [name ?? TEST_SCRIPT_NAME]: {
5477
- cache: cached,
5478
- inputs: ["default", "^default", ...configFiles.map((name2) => `{projectRoot}/${name2}`)],
5479
- ...cached ? { outputs: [...outputs] } : {},
5480
- ...dependsOn !== void 0 && dependsOn.length > 0 ? { dependsOn: [...dependsOn] } : {},
5481
- ...configurations !== void 0 && Object.keys(configurations).length > 0 ? { configurations: { ...configurations } } : {}
5482
- }
5483
- };
5484
- }
5346
+ import { isAbsolute as isAbsolute3, resolve as resolve8 } from "node:path";
5485
5347
 
5486
5348
  // src/roles/test/jest-migration.ts
5487
- import { existsSync as existsSync32, readFileSync as readFileSync31, readdirSync as readdirSync8 } from "node:fs";
5488
- import { createRequire as createRequire6 } from "node:module";
5489
- import { dirname as dirname11, join as join40, relative as relative8 } from "node:path";
5349
+ import { existsSync as existsSync29, readFileSync as readFileSync29, readdirSync as readdirSync7 } from "node:fs";
5350
+ import { createRequire as createRequire5 } from "node:module";
5351
+ import { dirname as dirname10, join as join37, relative as relative7 } from "node:path";
5490
5352
 
5491
5353
  // src/roles/test/codemod/migrate-module.ts
5492
- import { readdirSync as readdirSync6, readFileSync as readFileSync28, statSync as statSync6 } from "node:fs";
5493
- import { join as join35 } from "node:path";
5354
+ import { readdirSync as readdirSync5, readFileSync as readFileSync26, statSync as statSync6 } from "node:fs";
5355
+ import { join as join32 } from "node:path";
5494
5356
  import { parseSync as parseSync8 } from "oxc-parser";
5495
5357
 
5496
5358
  // src/roles/test/msw-lifecycle.ts
5497
- import { existsSync as existsSync23, readFileSync as readFileSync21 } from "node:fs";
5498
- import { join as join27 } from "node:path";
5359
+ import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
5360
+ import { join as join25 } from "node:path";
5499
5361
  var ROOT_SETUP = "jest.setup.after.env.js";
5500
5362
  function ownsMswLifecycle(cwd, setupFiles) {
5501
5363
  for (const raw of setupFiles) {
5502
5364
  if (raw.endsWith(ROOT_SETUP)) continue;
5503
- const file = join27(cwd, raw.replaceAll("<rootDir>/", "").replaceAll("<rootDir>", "."));
5504
- if (!existsSync23(file)) continue;
5365
+ const file = join25(cwd, raw.replaceAll("<rootDir>/", "").replaceAll("<rootDir>", "."));
5366
+ if (!existsSync21(file)) continue;
5505
5367
  try {
5506
- if (/\.listen\s*\(/.test(readFileSync21(file, "utf8"))) return true;
5368
+ if (/\.listen\s*\(/.test(readFileSync20(file, "utf8"))) return true;
5507
5369
  } catch {
5508
5370
  continue;
5509
5371
  }
@@ -5511,141 +5373,19 @@ function ownsMswLifecycle(cwd, setupFiles) {
5511
5373
  return false;
5512
5374
  }
5513
5375
 
5514
- // src/roles/test/read-adoption.ts
5515
- import { existsSync as existsSync24, readFileSync as readFileSync22, readdirSync as readdirSync3 } from "node:fs";
5516
- import { join as join28, relative as relative4 } from "node:path";
5517
- var JEST_CONFIG_FILES = [
5518
- "jest.config.ts",
5519
- "jest.config.js",
5520
- "jest.prisma.config.ts",
5521
- "jest.functional.config.ts",
5522
- "jest.integration-config.ts"
5523
- ];
5524
- function testConfigFile(cwd) {
5525
- return TEST_CONFIG_FILES.find((name) => existsSync24(join28(cwd, name)));
5526
- }
5527
- function jestConfigFiles(cwd) {
5528
- return JEST_CONFIG_FILES.filter((name) => existsSync24(join28(cwd, name)));
5529
- }
5530
- function rootJestConfigFiles(cwd) {
5531
- const root = findWorkspaceRoot(cwd);
5532
- if (root === void 0 || root === cwd) return [];
5533
- return JEST_CONFIG_FILES.filter((name) => existsSync24(join28(root, name)));
5534
- }
5535
- var TEST_FILE = /[.-](spec|test)\.[cm]?[jt]sx?$/;
5536
- var NOT_WALKED = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", ".git", ".nx", ".turbo"]);
5537
- function hasTestFiles(cwd) {
5538
- return walkTestFiles(cwd, true).length > 0;
5539
- }
5540
- function listTestFiles(cwd) {
5541
- return walkTestFiles(cwd, false).sort();
5542
- }
5543
- function walkTestFiles(cwd, stopAtFirst) {
5544
- const found = [];
5545
- const stack = [cwd];
5546
- while (stack.length > 0) {
5547
- const dir = stack.pop();
5548
- let entries;
5549
- try {
5550
- entries = readdirSync3(dir, { withFileTypes: true });
5551
- } catch {
5552
- continue;
5553
- }
5554
- for (const entry of entries) {
5555
- if (entry.isDirectory()) {
5556
- if (!NOT_WALKED.has(entry.name)) stack.push(join28(dir, entry.name));
5557
- } else if (TEST_FILE.test(entry.name)) {
5558
- found.push(relative4(cwd, join28(dir, entry.name)).replaceAll("\\", "/"));
5559
- if (stopAtFirst) return found;
5560
- }
5561
- }
5562
- }
5563
- return found;
5564
- }
5565
- function readTestConfigSource(cwd, fileName) {
5566
- try {
5567
- return readFileSync22(join28(cwd, fileName), "utf8");
5568
- } catch {
5569
- return "";
5570
- }
5571
- }
5572
- function importsPreset2(source) {
5573
- return ALL_TEST_PRESET_SPECIFIERS.some((specifier) => {
5574
- const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5575
- return new RegExp(String.raw`(?:from|import)\s*\(?\s*['"]${escaped}['"]`).test(source);
5576
- });
5577
- }
5578
- function readTestAdoption(cwd) {
5579
- const manifest = readProjectPackageJson(cwd);
5580
- const jestConfigs = jestConfigFiles(cwd);
5581
- const configFile = testConfigFile(cwd);
5582
- const declared = { ...manifest.dependencies, ...manifest.devDependencies };
5583
- const ownDeclarations = ["vitest", "jest", "@nx/jest", "jest-mock-extended", "@swc/jest"].filter((name) => declared[name] !== void 0).map((name) => ({ name, version: declared[name] }));
5584
- if (configFile === void 0) {
5585
- const hasTests = hasTestFiles(cwd);
5586
- const inheritsRootConfig = hasTests && jestConfigs.length === 0 && rootJestConfigFiles(cwd).length > 0;
5587
- return {
5588
- configFile: null,
5589
- preset: null,
5590
- adopted: false,
5591
- conformant: false,
5592
- drift: [],
5593
- unreadable: null,
5594
- state: hasTests ? "jest" : "no-tests",
5595
- runner: hasTests ? "jest" : "none",
5596
- jestConfigs,
5597
- inheritsRootConfig,
5598
- ownDeclarations
5599
- };
5600
- }
5601
- const source = readTestConfigSource(cwd, configFile);
5602
- if (source === "") {
5603
- return {
5604
- configFile,
5605
- preset: null,
5606
- adopted: false,
5607
- conformant: false,
5608
- drift: [],
5609
- unreadable: `${configFile} could not be read`,
5610
- state: "vitest-own-toolchain",
5611
- runner: "vitest",
5612
- jestConfigs,
5613
- inheritsRootConfig: false,
5614
- // it has a config of its own, readable or not
5615
- ownDeclarations
5616
- };
5617
- }
5618
- const adopted = importsPreset2(source);
5619
- const script = moduleScripts(cwd)["test"];
5620
- return {
5621
- configFile,
5622
- preset: adopted ? "react" : null,
5623
- adopted,
5624
- conformant: adopted && script !== void 0 && jestConfigs.length === 0,
5625
- drift: [],
5626
- unreadable: null,
5627
- state: adopted ? "adopted" : "vitest-own-toolchain",
5628
- runner: "vitest",
5629
- jestConfigs,
5630
- inheritsRootConfig: false,
5631
- // local always wins: this module is out of the root's reach
5632
- ownDeclarations
5633
- };
5634
- }
5635
-
5636
5376
  // src/roles/test/support-file-name.ts
5637
5377
  var JEST_PREFIX = "jest.";
5638
5378
  var VITEST_PREFIX = "vitest.";
5639
- function vitestSupportName(relative9) {
5640
- const cut = relative9.lastIndexOf("/");
5641
- const directory = cut === -1 ? "" : relative9.slice(0, cut + 1);
5642
- const base = relative9.slice(cut + 1);
5643
- if (!base.startsWith(JEST_PREFIX)) return relative9;
5379
+ function vitestSupportName(relative8) {
5380
+ const cut = relative8.lastIndexOf("/");
5381
+ const directory = cut === -1 ? "" : relative8.slice(0, cut + 1);
5382
+ const base = relative8.slice(cut + 1);
5383
+ if (!base.startsWith(JEST_PREFIX)) return relative8;
5644
5384
  return `${directory}${VITEST_PREFIX}${base.slice(JEST_PREFIX.length)}`;
5645
5385
  }
5646
5386
 
5647
5387
  // src/roles/test/codemod/migrate-file.ts
5648
- import { dirname as dirname8 } from "node:path";
5388
+ import { dirname as dirname7 } from "node:path";
5649
5389
  import { parseSync as parseSync7 } from "oxc-parser";
5650
5390
 
5651
5391
  // src/roles/test/codemod/assertion-return-value.ts
@@ -5774,12 +5514,38 @@ function rewriteAssertionReturnValue(source, program) {
5774
5514
  return { source: result, rewritten: captures.length - refusals.length, refusals };
5775
5515
  }
5776
5516
 
5777
- // src/roles/test/codemod/constructible-mocks.ts
5517
+ // src/roles/test/codemod/bare-mock-factory.ts
5778
5518
  var MEMBER_TYPES2 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5519
+ function isMockFactory(node) {
5520
+ if (node === void 0 || !MEMBER_TYPES2.has(node.type)) return false;
5521
+ const object = node.object;
5522
+ const property = node.property;
5523
+ if (object?.type !== "Identifier") return false;
5524
+ if (object.name !== "vi" && object.name !== "jest") return false;
5525
+ return property?.type === "Identifier" && property.name === "fn";
5526
+ }
5527
+ function invokeBareMockFactories(source, program) {
5528
+ const edits = [];
5529
+ walk(program, (node) => {
5530
+ if (node.type !== "CallExpression") return;
5531
+ for (const argument of node.arguments ?? []) {
5532
+ if (isMockFactory(argument)) edits.push(argument.end);
5533
+ }
5534
+ });
5535
+ if (edits.length === 0) return { source, rewritten: 0 };
5536
+ let result = source;
5537
+ for (const end of [...edits].sort((left, right) => right - left)) {
5538
+ result = `${result.slice(0, end)}()${result.slice(end)}`;
5539
+ }
5540
+ return { source: result, rewritten: edits.length };
5541
+ }
5542
+
5543
+ // src/roles/test/codemod/constructible-mocks.ts
5544
+ var MEMBER_TYPES3 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5779
5545
  function mockImplementationArrow(node) {
5780
5546
  if (node.type !== "CallExpression") return void 0;
5781
5547
  const callee = node.callee;
5782
- if (callee === void 0 || !MEMBER_TYPES2.has(callee.type)) return void 0;
5548
+ if (callee === void 0 || !MEMBER_TYPES3.has(callee.type)) return void 0;
5783
5549
  const property = callee.property;
5784
5550
  if (property?.type !== "Identifier" || property.name !== "mockImplementation") return void 0;
5785
5551
  const [argument] = node.arguments ?? [];
@@ -5788,7 +5554,7 @@ function mockImplementationArrow(node) {
5788
5554
  function assertedMockedClass(source, node) {
5789
5555
  if (node.type !== "CallExpression") return false;
5790
5556
  const callee = node.callee;
5791
- if (callee === void 0 || !MEMBER_TYPES2.has(callee.type)) return false;
5557
+ if (callee === void 0 || !MEMBER_TYPES3.has(callee.type)) return false;
5792
5558
  const object = callee.object;
5793
5559
  if (object === void 0) return false;
5794
5560
  return /\bMockedClass\b/.test(source.slice(object.start, object.end));
@@ -5864,16 +5630,16 @@ function normaliseDeepImports(source) {
5864
5630
  }
5865
5631
 
5866
5632
  // src/roles/test/codemod/each-table-rows.ts
5867
- var MEMBER_TYPES3 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5633
+ var MEMBER_TYPES4 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5868
5634
  var RUNNERS = /* @__PURE__ */ new Set(["it", "test", "describe"]);
5869
5635
  function eachTable(node) {
5870
5636
  if (node.type !== "CallExpression") return void 0;
5871
5637
  const callee = node.callee;
5872
- if (callee === void 0 || !MEMBER_TYPES3.has(callee.type)) return void 0;
5638
+ if (callee === void 0 || !MEMBER_TYPES4.has(callee.type)) return void 0;
5873
5639
  const property = callee.property;
5874
5640
  if (property?.type !== "Identifier" || property.name !== "each") return void 0;
5875
5641
  let object = callee.object;
5876
- while (object !== void 0 && MEMBER_TYPES3.has(object.type)) {
5642
+ while (object !== void 0 && MEMBER_TYPES4.has(object.type)) {
5877
5643
  object = object.object;
5878
5644
  }
5879
5645
  if (object?.type !== "Identifier" || !RUNNERS.has(object.name)) return void 0;
@@ -5900,13 +5666,13 @@ function rewriteEachTableRows(source, program) {
5900
5666
  }
5901
5667
 
5902
5668
  // src/roles/test/codemod/each-title.ts
5903
- var MEMBER_TYPES4 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5669
+ var MEMBER_TYPES5 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5904
5670
  function eachTitle(node) {
5905
5671
  if (node.type !== "CallExpression") return void 0;
5906
5672
  const inner = node.callee;
5907
5673
  if (inner?.type !== "CallExpression") return void 0;
5908
5674
  const member = inner.callee;
5909
- if (member === void 0 || !MEMBER_TYPES4.has(member.type)) return void 0;
5675
+ if (member === void 0 || !MEMBER_TYPES5.has(member.type)) return void 0;
5910
5676
  const property = member.property;
5911
5677
  if (property?.type !== "Identifier" || property.name !== "each") return void 0;
5912
5678
  const [title] = node.arguments ?? [];
@@ -5959,14 +5725,14 @@ function rewriteEachTitles(source, program) {
5959
5725
 
5960
5726
  // src/roles/test/codemod/empty-mock-implementation.ts
5961
5727
  var METHODS = /* @__PURE__ */ new Set(["mockImplementation", "mockImplementationOnce"]);
5962
- var MEMBER_TYPES5 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5728
+ var MEMBER_TYPES6 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5963
5729
  function rewriteEmptyMockImplementation(source, program) {
5964
5730
  const insertAt = [];
5965
5731
  walk(program, (node) => {
5966
5732
  if (node.type !== "CallExpression") return;
5967
5733
  if ((node.arguments ?? []).length > 0) return;
5968
5734
  const callee = node.callee;
5969
- if (callee === void 0 || !MEMBER_TYPES5.has(callee.type)) return;
5735
+ if (callee === void 0 || !MEMBER_TYPES6.has(callee.type)) return;
5970
5736
  const property = callee.property;
5971
5737
  if (property?.type !== "Identifier" || !METHODS.has(property.name)) return;
5972
5738
  insertAt.push(node.end - 1);
@@ -5979,12 +5745,12 @@ function rewriteEmptyMockImplementation(source, program) {
5979
5745
  }
5980
5746
 
5981
5747
  // src/roles/test/codemod/fake-timer-options.ts
5982
- var MEMBER_TYPES6 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5748
+ var MEMBER_TYPES7 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
5983
5749
  var TIMER_CALLS = /* @__PURE__ */ new Set(["useFakeTimers"]);
5984
5750
  function timerOptions(node) {
5985
5751
  if (node.type !== "CallExpression") return void 0;
5986
5752
  const callee = node.callee;
5987
- if (callee === void 0 || !MEMBER_TYPES6.has(callee.type)) return void 0;
5753
+ if (callee === void 0 || !MEMBER_TYPES7.has(callee.type)) return void 0;
5988
5754
  const object = callee.object;
5989
5755
  const property = callee.property;
5990
5756
  if (object?.type !== "Identifier") return void 0;
@@ -6328,7 +6094,7 @@ function hoistCapturedBindings(source, program, factories, bindings, imported) {
6328
6094
 
6329
6095
  // src/roles/test/codemod/hook-return-value.ts
6330
6096
  var HOOKS = /* @__PURE__ */ new Set(["beforeEach", "afterEach", "beforeAll", "afterAll"]);
6331
- var MEMBER_TYPES7 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6097
+ var MEMBER_TYPES8 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6332
6098
  function hookCallback(node) {
6333
6099
  if (node.type !== "CallExpression") return void 0;
6334
6100
  const callee = node.callee;
@@ -6339,7 +6105,7 @@ function hookCallback(node) {
6339
6105
  function configuresAMock(body) {
6340
6106
  if (body.type !== "CallExpression") return false;
6341
6107
  const callee = body.callee;
6342
- if (callee === void 0 || !MEMBER_TYPES7.has(callee.type)) return false;
6108
+ if (callee === void 0 || !MEMBER_TYPES8.has(callee.type)) return false;
6343
6109
  const property = callee.property;
6344
6110
  return property?.type === "Identifier" && /^mock[A-Z]/.test(property.name);
6345
6111
  }
@@ -6524,37 +6290,37 @@ ${source}`;
6524
6290
  }
6525
6291
 
6526
6292
  // src/roles/test/codemod/named-rules.ts
6527
- import { existsSync as existsSync25, readFileSync as readFileSync23 } from "node:fs";
6528
- import { createRequire as createRequire4 } from "node:module";
6529
- import { dirname as dirname4, join as join29 } from "node:path";
6293
+ import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
6294
+ import { createRequire as createRequire3 } from "node:module";
6295
+ import { dirname as dirname3, join as join26 } from "node:path";
6530
6296
  function isCommonJsPackage(fromFile, specifier) {
6531
6297
  if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
6532
6298
  let resolved;
6533
6299
  try {
6534
- resolved = createRequire4(fromFile).resolve(specifier);
6300
+ resolved = createRequire3(fromFile).resolve(specifier);
6535
6301
  } catch {
6536
6302
  return false;
6537
6303
  }
6538
6304
  if (!resolved.includes(`${"/"}node_modules${"/"}`)) return false;
6539
6305
  if (resolved.endsWith(".mjs")) return false;
6540
6306
  if (resolved.endsWith(".cjs")) return true;
6541
- let dir = dirname4(resolved);
6307
+ let dir = dirname3(resolved);
6542
6308
  for (let depth = 0; depth < 12; depth += 1) {
6543
- const manifest = join29(dir, "package.json");
6544
- if (existsSync25(manifest)) {
6309
+ const manifest = join26(dir, "package.json");
6310
+ if (existsSync22(manifest)) {
6545
6311
  try {
6546
- return JSON.parse(readFileSync23(manifest, "utf8")).type !== "module";
6312
+ return JSON.parse(readFileSync21(manifest, "utf8")).type !== "module";
6547
6313
  } catch {
6548
6314
  return false;
6549
6315
  }
6550
6316
  }
6551
- const parent = dirname4(dir);
6317
+ const parent = dirname3(dir);
6552
6318
  if (parent === dir) break;
6553
6319
  dir = parent;
6554
6320
  }
6555
6321
  return false;
6556
6322
  }
6557
- var MEMBER_TYPES8 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6323
+ var MEMBER_TYPES9 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6558
6324
  var ASYNC_REPLACEMENTS = {
6559
6325
  requireActual: "importActual",
6560
6326
  requireMock: "importMock"
@@ -6564,7 +6330,7 @@ function mockedSpecifiers(program) {
6564
6330
  walk(program, (node) => {
6565
6331
  if (node.type !== "CallExpression") return;
6566
6332
  const callee = node.callee;
6567
- if (callee === void 0 || !MEMBER_TYPES8.has(callee.type)) return;
6333
+ if (callee === void 0 || !MEMBER_TYPES9.has(callee.type)) return;
6568
6334
  const object = callee.object;
6569
6335
  const property = callee.property;
6570
6336
  if (object?.type !== "Identifier") return;
@@ -6601,7 +6367,7 @@ function bareRequire(node) {
6601
6367
  function mockFactoryReturn(node) {
6602
6368
  if (node.type !== "CallExpression") return void 0;
6603
6369
  const callee = node.callee;
6604
- if (callee === void 0 || !MEMBER_TYPES8.has(callee.type)) return void 0;
6370
+ if (callee === void 0 || !MEMBER_TYPES9.has(callee.type)) return void 0;
6605
6371
  const object = callee.object;
6606
6372
  const property = callee.property;
6607
6373
  if (object?.type !== "Identifier") return void 0;
@@ -6711,7 +6477,7 @@ function applyNamedRules(source, program, fileName = "") {
6711
6477
  }
6712
6478
  return;
6713
6479
  }
6714
- if (!MEMBER_TYPES8.has(node.type)) return;
6480
+ if (!MEMBER_TYPES9.has(node.type)) return;
6715
6481
  const object = node.object;
6716
6482
  if (object !== void 0) readsANameOffIt.add(object);
6717
6483
  });
@@ -6782,13 +6548,13 @@ function applyNamedRules(source, program, fileName = "") {
6782
6548
  }
6783
6549
 
6784
6550
  // src/roles/test/codemod/isolate-modules.ts
6785
- var MEMBER_TYPES9 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6551
+ var MEMBER_TYPES10 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6786
6552
  var ISOLATORS = /* @__PURE__ */ new Set(["isolateModules", "isolateModulesAsync"]);
6787
6553
  var FUNCTIONS2 = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression"]);
6788
6554
  function isolatingCall(node) {
6789
6555
  if (node.type !== "CallExpression") return void 0;
6790
6556
  const callee = node.callee;
6791
- if (callee === void 0 || !MEMBER_TYPES9.has(callee.type)) return void 0;
6557
+ if (callee === void 0 || !MEMBER_TYPES10.has(callee.type)) return void 0;
6792
6558
  const object = callee.object;
6793
6559
  const property = callee.property;
6794
6560
  if (object?.type !== "Identifier" || object.name !== "jest") return void 0;
@@ -6799,7 +6565,7 @@ function isolatingCall(node) {
6799
6565
  function unusableIsolatingCall(node) {
6800
6566
  if (node.type !== "CallExpression") return void 0;
6801
6567
  const callee = node.callee;
6802
- if (callee === void 0 || !MEMBER_TYPES9.has(callee.type)) return void 0;
6568
+ if (callee === void 0 || !MEMBER_TYPES10.has(callee.type)) return void 0;
6803
6569
  const object = callee.object;
6804
6570
  const property = callee.property;
6805
6571
  if (object?.type !== "Identifier" || object.name !== "jest") return void 0;
@@ -6850,11 +6616,11 @@ ${indent}await (${callback})()`
6850
6616
  }
6851
6617
 
6852
6618
  // src/roles/test/codemod/mock-as-value.ts
6853
- var MEMBER_TYPES10 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6619
+ var MEMBER_TYPES11 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6854
6620
  function isViMockCall(node) {
6855
6621
  if (node?.type !== "CallExpression") return false;
6856
6622
  const callee = node.callee;
6857
- if (callee === void 0 || !MEMBER_TYPES10.has(callee.type)) return false;
6623
+ if (callee === void 0 || !MEMBER_TYPES11.has(callee.type)) return false;
6858
6624
  const object = callee.object;
6859
6625
  const property = callee.property;
6860
6626
  return object?.type === "Identifier" && object.name === "vi" && property?.type === "Identifier" && (property.name === "mock" || property.name === "doMock");
@@ -6879,7 +6645,7 @@ function rewriteMockAsValue(source, program) {
6879
6645
  const memberUses = /* @__PURE__ */ new Map();
6880
6646
  const otherUses = /* @__PURE__ */ new Map();
6881
6647
  walk(program, (node) => {
6882
- if (MEMBER_TYPES10.has(node.type)) {
6648
+ if (MEMBER_TYPES11.has(node.type)) {
6883
6649
  const object = node.object;
6884
6650
  if (object?.type === "Identifier" && bindings.has(object.name)) {
6885
6651
  const list = memberUses.get(object.name) ?? [];
@@ -6925,12 +6691,12 @@ function rewriteMockAsValue(source, program) {
6925
6691
  }
6926
6692
 
6927
6693
  // src/roles/test/codemod/mock-factory-default.ts
6928
- var MEMBER_TYPES11 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6694
+ var MEMBER_TYPES12 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6929
6695
  var BINDING = "sentinelMockedModule";
6930
6696
  function mockCall(node) {
6931
6697
  if (node.type !== "CallExpression") return void 0;
6932
6698
  const callee = node.callee;
6933
- if (callee === void 0 || !MEMBER_TYPES11.has(callee.type)) return void 0;
6699
+ if (callee === void 0 || !MEMBER_TYPES12.has(callee.type)) return void 0;
6934
6700
  const property = callee.property;
6935
6701
  if (property?.type !== "Identifier" || property.name !== "mock") return void 0;
6936
6702
  const object = callee.object;
@@ -7043,7 +6809,7 @@ function rewriteMockFactoryDefault(source, program, defaultImported) {
7043
6809
 
7044
6810
  // src/roles/test/codemod/mock-promise-resolve.ts
7045
6811
  var MOCK_FACTORY = /* @__PURE__ */ new Set(["fn", "spyOn"]);
7046
- function isMockFactory(source, node) {
6812
+ function isMockFactory2(source, node) {
7047
6813
  if (node.type !== "CallExpression") return false;
7048
6814
  const callee = node.callee;
7049
6815
  if (callee?.type !== "MemberExpression") return false;
@@ -7086,7 +6852,7 @@ function resolvedValue(source, executor) {
7086
6852
  function rewriteMockPromiseResolve(source, program) {
7087
6853
  const edits = [];
7088
6854
  walk(program, (node) => {
7089
- if (!isMockFactory(source, node)) return;
6855
+ if (!isMockFactory2(source, node)) return;
7090
6856
  for (const argument of node.arguments ?? []) {
7091
6857
  walk(argument, (inner) => {
7092
6858
  if (inner.type !== "NewExpression") return;
@@ -7114,11 +6880,11 @@ function rewriteMockPromiseResolve(source, program) {
7114
6880
  }
7115
6881
 
7116
6882
  // src/roles/test/codemod/mocked-module-binding.ts
7117
- var MEMBER_TYPES12 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6883
+ var MEMBER_TYPES13 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
7118
6884
  function mockedSpecifier(source, node) {
7119
6885
  if (node.type !== "CallExpression") return void 0;
7120
6886
  const callee = node.callee;
7121
- if (callee === void 0 || !MEMBER_TYPES12.has(callee.type)) return void 0;
6887
+ if (callee === void 0 || !MEMBER_TYPES13.has(callee.type)) return void 0;
7122
6888
  const object = callee.object;
7123
6889
  const property = callee.property;
7124
6890
  if (object?.type !== "Identifier") return void 0;
@@ -7176,7 +6942,7 @@ function annotateMockedBindings(source, program) {
7176
6942
  // src/roles/test/codemod/propagate-await.ts
7177
6943
  import { parseSync as parseSync5 } from "oxc-parser";
7178
6944
  var FUNCTIONS3 = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression", "FunctionDeclaration"]);
7179
- var MEMBER_TYPES13 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
6945
+ var MEMBER_TYPES14 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
7180
6946
  var ALREADY_A_PROMISE = /* @__PURE__ */ new Set(["then", "catch", "finally"]);
7181
6947
  function asyncFunctionNames(program) {
7182
6948
  const names = /* @__PURE__ */ new Set();
@@ -7250,7 +7016,7 @@ function alreadyHandled(call, parent) {
7250
7016
  if (parent === void 0) return false;
7251
7017
  if (parent.type === "AwaitExpression") return true;
7252
7018
  if (parent.type === "ReturnStatement" || parent.type === "ArrowFunctionExpression") return true;
7253
- if (MEMBER_TYPES13.has(parent.type) && parent.object === call) {
7019
+ if (MEMBER_TYPES14.has(parent.type) && parent.object === call) {
7254
7020
  const property = parent.property;
7255
7021
  return property?.type === "Identifier" && ALREADY_A_PROMISE.has(property.name);
7256
7022
  }
@@ -7300,10 +7066,10 @@ function propagateAwait(fileName, source, original, elsewhere = /* @__PURE__ */
7300
7066
  }
7301
7067
 
7302
7068
  // src/roles/test/codemod/reexport-barrel-mock.ts
7303
- import { readFileSync as readFileSync24, readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
7304
- import { createRequire as createRequire5 } from "node:module";
7305
- import { dirname as dirname5, join as join30 } from "node:path";
7306
- var MEMBER_TYPES14 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
7069
+ import { readFileSync as readFileSync22, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
7070
+ import { createRequire as createRequire4 } from "node:module";
7071
+ import { dirname as dirname4, join as join27 } from "node:path";
7072
+ var MEMBER_TYPES15 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
7307
7073
  var PACKAGE_SUBPATH = /^(@[^/]+\/[^/]+|[^@.][^/]*)\/(.+)$/;
7308
7074
  var PURE_REEXPORT = /^module\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)\s*;?$/;
7309
7075
  function mockCalls(program) {
@@ -7311,7 +7077,7 @@ function mockCalls(program) {
7311
7077
  walk(program, (node) => {
7312
7078
  if (node.type !== "CallExpression") return;
7313
7079
  const callee = node.callee;
7314
- if (callee === void 0 || !MEMBER_TYPES14.has(callee.type)) return;
7080
+ if (callee === void 0 || !MEMBER_TYPES15.has(callee.type)) return;
7315
7081
  const object = callee.object;
7316
7082
  const property = callee.property;
7317
7083
  if (object?.type !== "Identifier") return;
@@ -7331,14 +7097,14 @@ function mockCalls(program) {
7331
7097
  }
7332
7098
  function packageRoot(fromFile, name) {
7333
7099
  try {
7334
- return dirname5(createRequire5(fromFile).resolve(`${name}/package.json`));
7100
+ return dirname4(createRequire4(fromFile).resolve(`${name}/package.json`));
7335
7101
  } catch {
7336
7102
  return void 0;
7337
7103
  }
7338
7104
  }
7339
7105
  function resolveFrom(fromFile, specifier) {
7340
7106
  try {
7341
- return createRequire5(fromFile).resolve(specifier);
7107
+ return createRequire4(fromFile).resolve(specifier);
7342
7108
  } catch {
7343
7109
  return void 0;
7344
7110
  }
@@ -7348,18 +7114,18 @@ function barrelsFor(fromFile, name, target) {
7348
7114
  if (root === void 0) return [];
7349
7115
  let entries;
7350
7116
  try {
7351
- entries = readdirSync4(root);
7117
+ entries = readdirSync3(root);
7352
7118
  } catch {
7353
7119
  return [];
7354
7120
  }
7355
7121
  const aliases = [];
7356
7122
  for (const entry of entries) {
7357
7123
  if (!entry.endsWith(".js")) continue;
7358
- const path = join30(root, entry);
7124
+ const path = join27(root, entry);
7359
7125
  let body;
7360
7126
  try {
7361
7127
  if (!statSync4(path).isFile()) continue;
7362
- body = readFileSync24(path, "utf8");
7128
+ body = readFileSync22(path, "utf8");
7363
7129
  } catch {
7364
7130
  continue;
7365
7131
  }
@@ -7438,17 +7204,17 @@ function repointMswServer(fileName, source) {
7438
7204
  }
7439
7205
 
7440
7206
  // src/roles/test/codemod/setup-static-imports.ts
7441
- import { dirname as dirname7, resolve as resolve6 } from "node:path";
7207
+ import { dirname as dirname6, resolve as resolve6 } from "node:path";
7442
7208
 
7443
7209
  // src/core/context.ts
7444
- import { existsSync as existsSync26 } from "node:fs";
7445
- import { dirname as dirname6, join as join32, resolve as resolve4 } from "node:path";
7210
+ import { existsSync as existsSync23 } from "node:fs";
7211
+ import { dirname as dirname5, join as join29, resolve as resolve4 } from "node:path";
7446
7212
 
7447
7213
  // src/core/discover-modules.ts
7448
7214
  import { execFileSync } from "node:child_process";
7449
- import { mkdtempSync, readFileSync as readFileSync25, rmSync as rmSync3 } from "node:fs";
7215
+ import { mkdtempSync, readFileSync as readFileSync23, rmSync as rmSync3 } from "node:fs";
7450
7216
  import { tmpdir } from "node:os";
7451
- import { join as join31 } from "node:path";
7217
+ import { join as join28 } from "node:path";
7452
7218
  function runNx(cwd, args) {
7453
7219
  const nx = resolveBin(cwd, "nx") ?? "nx";
7454
7220
  try {
@@ -7466,11 +7232,11 @@ function runNx(cwd, args) {
7466
7232
  }
7467
7233
  }
7468
7234
  function readGraph(cwd) {
7469
- const dir = mkdtempSync(join31(tmpdir(), "sentinel-nx-"));
7470
- const file = join31(dir, "graph.json");
7235
+ const dir = mkdtempSync(join28(tmpdir(), "sentinel-nx-"));
7236
+ const file = join28(dir, "graph.json");
7471
7237
  try {
7472
7238
  runNx(cwd, ["graph", "--file", file]);
7473
- const parsed = JSON.parse(readFileSync25(file, "utf8"));
7239
+ const parsed = JSON.parse(readFileSync23(file, "utf8"));
7474
7240
  const nodes = parsed.graph?.nodes;
7475
7241
  if (!nodes || typeof nodes !== "object") {
7476
7242
  throw new Error(
@@ -7479,7 +7245,7 @@ function readGraph(cwd) {
7479
7245
  }
7480
7246
  return Object.entries(nodes).map(([name, node]) => ({
7481
7247
  name,
7482
- root: join31(cwd, node.data.root)
7248
+ root: join28(cwd, node.data.root)
7483
7249
  }));
7484
7250
  } finally {
7485
7251
  rmSync3(dir, { recursive: true, force: true });
@@ -7497,20 +7263,20 @@ function discoverModules(cwd, options = {}) {
7497
7263
  // src/core/context.ts
7498
7264
  var MODULE_MARKERS = ["package.json", "project.json"];
7499
7265
  function isModuleDir(cwd) {
7500
- return MODULE_MARKERS.some((marker) => existsSync26(join32(cwd, marker)));
7266
+ return MODULE_MARKERS.some((marker) => existsSync23(join29(cwd, marker)));
7501
7267
  }
7502
7268
  function findModuleRoot(startDir, workspaceRoot) {
7503
7269
  let dir = resolve4(startDir);
7504
7270
  for (; ; ) {
7505
7271
  if (workspaceRoot !== void 0 && dir === resolve4(workspaceRoot)) return void 0;
7506
7272
  if (isModuleDir(dir)) return dir;
7507
- const parent = dirname6(dir);
7273
+ const parent = dirname5(dir);
7508
7274
  if (parent === dir) return void 0;
7509
7275
  dir = parent;
7510
7276
  }
7511
7277
  }
7512
7278
  function resolveContext(cwd, opts) {
7513
- const atRoot = existsSync26(join32(cwd, WORKSPACE_ROOT_MARKER));
7279
+ const atRoot = existsSync23(join29(cwd, WORKSPACE_ROOT_MARKER));
7514
7280
  if (!atRoot && isModuleDir(cwd)) {
7515
7281
  if (opts.module) {
7516
7282
  throw new Error(
@@ -7538,16 +7304,16 @@ function resolveContext(cwd, opts) {
7538
7304
  }
7539
7305
 
7540
7306
  // src/roles/test/codemod/workspace-specifier.ts
7541
- import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
7542
- import { join as join33, relative as relative5, resolve as resolve5 } from "node:path";
7307
+ import { existsSync as existsSync24, readFileSync as readFileSync24 } from "node:fs";
7308
+ import { join as join30, relative as relative4, resolve as resolve5 } from "node:path";
7543
7309
  var EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", "/index.ts", "/index.js"];
7544
7310
  function pathEntries(workspaceRoot) {
7545
- const file = join33(workspaceRoot, "tsconfig.base.json");
7546
- if (!existsSync27(file)) return [];
7311
+ const file = join30(workspaceRoot, "tsconfig.base.json");
7312
+ if (!existsSync24(file)) return [];
7547
7313
  let paths;
7548
7314
  try {
7549
7315
  paths = parseJsonc(
7550
- readFileSync26(file, "utf8"),
7316
+ readFileSync24(file, "utf8"),
7551
7317
  "tsconfig.base.json"
7552
7318
  )?.compilerOptions?.paths;
7553
7319
  } catch {
@@ -7575,7 +7341,7 @@ function workspaceSpecifier(workspaceRoot, file) {
7575
7341
  }
7576
7342
  continue;
7577
7343
  }
7578
- const rest = relative5(entry.target, absolute);
7344
+ const rest = relative4(entry.target, absolute);
7579
7345
  if (rest === "" || rest.startsWith("..") || rest.startsWith("/")) continue;
7580
7346
  const named = rest.replace(/\\/g, "/").replace(/\.(m|c)?(j|t)sx?$/, "");
7581
7347
  return `${entry.key}/${named}`;
@@ -7596,7 +7362,7 @@ function requireCall(source, node) {
7596
7362
  }
7597
7363
  function importable(written, fileName, workspaceRoot) {
7598
7364
  if (!written.startsWith(".")) return written;
7599
- const fileDir = dirname7(fileName);
7365
+ const fileDir = dirname6(fileName);
7600
7366
  const moduleRoot = findModuleRoot(fileDir, workspaceRoot) ?? fileDir;
7601
7367
  const target = resolve6(fileDir, written);
7602
7368
  if (target.startsWith(`${moduleRoot}/`)) return written;
@@ -7742,7 +7508,7 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
7742
7508
  fileName,
7743
7509
  source,
7744
7510
  beforeStatic,
7745
- findWorkspaceRoot(dirname8(fileName))
7511
+ findWorkspaceRoot(dirname7(fileName))
7746
7512
  );
7747
7513
  source = statics.source;
7748
7514
  const original = statics.rewritten === 0 ? beforeStatic : parse2(fileName, source);
@@ -7804,7 +7570,10 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
7804
7570
  const afterHoist = parse2(fileName, hoist.source);
7805
7571
  if (afterHoist === void 0) return unparsable("hoisting captured bindings");
7806
7572
  const mockedBindings = annotateMockedBindings(hoist.source, afterHoist);
7807
- const withImport = injectTypeImport(fileName, mockedBindings.source, [
7573
+ const afterBindings = parse2(fileName, mockedBindings.source);
7574
+ if (afterBindings === void 0) return unparsable("mocked module bindings");
7575
+ const bareFactories = invokeBareMockFactories(mockedBindings.source, afterBindings);
7576
+ const withImport = injectTypeImport(fileName, bareFactories.source, [
7808
7577
  ...renamed.typeImports,
7809
7578
  ...spyTypes.typeImports,
7810
7579
  ...mockedBindings.typeImports
@@ -7831,6 +7600,7 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
7831
7600
  ...hooks.rewritten > 0 ? ["hook body, so it does not return its mock"] : [],
7832
7601
  ...spyTypes.rewritten > 0 ? ["SpyInstance<T> as a signature, not a return type"] : [],
7833
7602
  ...mockedBindings.rewritten > 0 ? ["a mocked module binding, typed as mocked"] : [],
7603
+ ...bareFactories.rewritten > 0 ? ["vi.fn passed as a value, invoked"] : [],
7834
7604
  ...awaited.rewritten > 0 ? ["await propagated to a helper that became async"] : [],
7835
7605
  ...barrels.added.map((alias) => `mock carried to ${alias}, which re-exports the mocked file`),
7836
7606
  ...fakeTimers.translated
@@ -7858,10 +7628,10 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
7858
7628
  }
7859
7629
 
7860
7630
  // src/roles/test/codemod/test-support-files.ts
7861
- import { existsSync as existsSync28, readFileSync as readFileSync27, readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
7862
- import { dirname as dirname9, join as join34, relative as relative6, resolve as resolve7 } from "node:path";
7631
+ import { existsSync as existsSync25, readFileSync as readFileSync25, readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
7632
+ import { dirname as dirname8, join as join31, relative as relative5, resolve as resolve7 } from "node:path";
7863
7633
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", ".git", ".vite"]);
7864
- var TEST_FILE2 = /\.(spec|test)\.[cm]?[jt]sx?$/;
7634
+ var TEST_FILE = /\.(spec|test)\.[cm]?[jt]sx?$/;
7865
7635
  var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
7866
7636
  var EXTENSIONS2 = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"];
7867
7637
  function relativeSpecifiers(source) {
@@ -7878,14 +7648,14 @@ function relativeSpecifiers(source) {
7878
7648
  return found;
7879
7649
  }
7880
7650
  function resolveSpecifier(fromFile, specifier) {
7881
- const base = resolve7(dirname9(fromFile), specifier);
7882
- if (existsSync28(base) && statSync5(base).isFile()) return base;
7651
+ const base = resolve7(dirname8(fromFile), specifier);
7652
+ if (existsSync25(base) && statSync5(base).isFile()) return base;
7883
7653
  for (const extension of EXTENSIONS2) {
7884
- if (existsSync28(`${base}${extension}`)) return `${base}${extension}`;
7654
+ if (existsSync25(`${base}${extension}`)) return `${base}${extension}`;
7885
7655
  }
7886
7656
  for (const extension of EXTENSIONS2) {
7887
- const index = join34(base, `index${extension}`);
7888
- if (existsSync28(index)) return index;
7657
+ const index = join31(base, `index${extension}`);
7658
+ if (existsSync25(index)) return index;
7889
7659
  }
7890
7660
  return void 0;
7891
7661
  }
@@ -7896,15 +7666,15 @@ function moduleSources(cwd) {
7896
7666
  const dir = stack.pop();
7897
7667
  let entries;
7898
7668
  try {
7899
- entries = readdirSync5(dir, { withFileTypes: true });
7669
+ entries = readdirSync4(dir, { withFileTypes: true });
7900
7670
  } catch {
7901
7671
  continue;
7902
7672
  }
7903
7673
  for (const entry of entries) {
7904
7674
  if (entry.isDirectory()) {
7905
- if (!SKIPPED.has(entry.name)) stack.push(join34(dir, entry.name));
7675
+ if (!SKIPPED.has(entry.name)) stack.push(join31(dir, entry.name));
7906
7676
  } else if (SOURCE_FILE.test(entry.name)) {
7907
- found.push(join34(dir, entry.name));
7677
+ found.push(join31(dir, entry.name));
7908
7678
  }
7909
7679
  }
7910
7680
  }
@@ -7912,7 +7682,7 @@ function moduleSources(cwd) {
7912
7682
  }
7913
7683
  function read(path) {
7914
7684
  try {
7915
- return readFileSync27(path, "utf8");
7685
+ return readFileSync25(path, "utf8");
7916
7686
  } catch {
7917
7687
  return void 0;
7918
7688
  }
@@ -7922,9 +7692,9 @@ function setupFilesInside(cwd, named) {
7922
7692
  for (const raw of named) {
7923
7693
  const withoutToken = raw.replaceAll("<rootDir>/", "").replaceAll("<rootDir>", ".");
7924
7694
  const full = resolve7(cwd, withoutToken);
7925
- const inside = relative6(cwd, full);
7695
+ const inside = relative5(cwd, full);
7926
7696
  if (inside.startsWith("..") || inside === "") continue;
7927
- if (!existsSync28(full)) continue;
7697
+ if (!existsSync25(full)) continue;
7928
7698
  kept.push(inside.replaceAll("\\", "/"));
7929
7699
  }
7930
7700
  return kept;
@@ -7945,21 +7715,21 @@ function listTestSupportFiles(cwd, setupFiles = []) {
7945
7715
  }
7946
7716
  }
7947
7717
  const testOnly = /* @__PURE__ */ new Set();
7948
- const queue = sources.filter((file) => TEST_FILE2.test(file));
7718
+ const queue = sources.filter((file) => TEST_FILE.test(file));
7949
7719
  const isTestOnly = (file) => (importedBy.get(file) ?? []).every(
7950
- (importer) => TEST_FILE2.test(importer) || testOnly.has(importer)
7720
+ (importer) => TEST_FILE.test(importer) || testOnly.has(importer)
7951
7721
  );
7952
7722
  while (queue.length > 0) {
7953
7723
  const file = queue.shift();
7954
7724
  for (const specifier of relativeSpecifiers(contents.get(file) ?? "")) {
7955
7725
  const target = resolveSpecifier(file, specifier);
7956
- if (target === void 0 || TEST_FILE2.test(target) || testOnly.has(target)) continue;
7726
+ if (target === void 0 || TEST_FILE.test(target) || testOnly.has(target)) continue;
7957
7727
  if (!isTestOnly(target)) continue;
7958
7728
  testOnly.add(target);
7959
7729
  queue.push(target);
7960
7730
  }
7961
7731
  }
7962
- const reachable = [...testOnly].filter((file) => /\brequire\s*\(/.test(contents.get(file) ?? "")).map((file) => relative6(cwd, file).replaceAll("\\", "/"));
7732
+ const reachable = [...testOnly].filter((file) => /\brequire\s*\(/.test(contents.get(file) ?? "")).map((file) => relative5(cwd, file).replaceAll("\\", "/"));
7963
7733
  return [.../* @__PURE__ */ new Set([...reachable, ...setupFilesInside(cwd, setupFiles)])].sort();
7964
7734
  }
7965
7735
 
@@ -7970,7 +7740,7 @@ function parse3(source) {
7970
7740
  }
7971
7741
  function read2(path) {
7972
7742
  try {
7973
- return readFileSync28(path, "utf8");
7743
+ return readFileSync26(path, "utf8");
7974
7744
  } catch {
7975
7745
  return void 0;
7976
7746
  }
@@ -7982,13 +7752,13 @@ function defaultImportedSpecifiers(cwd) {
7982
7752
  const walk2 = (dir) => {
7983
7753
  let entries;
7984
7754
  try {
7985
- entries = readdirSync6(dir);
7755
+ entries = readdirSync5(dir);
7986
7756
  } catch {
7987
7757
  return;
7988
7758
  }
7989
7759
  for (const entry of entries) {
7990
7760
  if (SKIPPED2.has(entry)) continue;
7991
- const path = join35(dir, entry);
7761
+ const path = join32(dir, entry);
7992
7762
  let directory;
7993
7763
  try {
7994
7764
  directory = statSync6(path).isDirectory();
@@ -8018,22 +7788,22 @@ function migrateModuleTests(cwd, setupFiles = []) {
8018
7788
  let hoisted = 0;
8019
7789
  const defaultImported = defaultImportedSpecifiers(cwd);
8020
7790
  const asyncHelpers = /* @__PURE__ */ new Set();
8021
- for (const relative9 of listTestSupportFiles(cwd, setupFiles)) {
8022
- const source = read2(join35(cwd, relative9));
7791
+ for (const relative8 of listTestSupportFiles(cwd, setupFiles)) {
7792
+ const source = read2(join32(cwd, relative8));
8023
7793
  if (source === void 0) {
8024
- blockers.push({ file: relative9, reason: "this file could not be read" });
7794
+ blockers.push({ file: relative8, reason: "this file could not be read" });
8025
7795
  continue;
8026
7796
  }
8027
7797
  const migrated = migrateTestFile(
8028
- join35(cwd, relative9),
7798
+ join32(cwd, relative8),
8029
7799
  source,
8030
7800
  defaultImported,
8031
7801
  /* @__PURE__ */ new Set(),
8032
7802
  modulesOwnMsw
8033
7803
  );
8034
- const renamedTo = vitestSupportName(relative9);
8035
- if (!migrated.changed && renamedTo === relative9) continue;
8036
- if (renamedTo !== relative9) operations.push({ kind: "delete", path: relative9 });
7804
+ const renamedTo = vitestSupportName(relative8);
7805
+ if (!migrated.changed && renamedTo === relative8) continue;
7806
+ if (renamedTo !== relative8) operations.push({ kind: "delete", path: relative8 });
8037
7807
  const before = parse3(source);
8038
7808
  const after = parse3(migrated.source);
8039
7809
  if (before !== void 0 && after !== void 0) {
@@ -8046,14 +7816,14 @@ function migrateModuleTests(cwd, setupFiles = []) {
8046
7816
  renamed += migrated.renamed;
8047
7817
  operations.push({ kind: "write", path: renamedTo, contents: migrated.source });
8048
7818
  }
8049
- for (const relative9 of listTestFiles(cwd)) {
8050
- const source = read2(join35(cwd, relative9));
7819
+ for (const relative8 of listTestFiles(cwd)) {
7820
+ const source = read2(join32(cwd, relative8));
8051
7821
  if (source === void 0) {
8052
- blockers.push({ file: relative9, reason: "this file could not be read" });
7822
+ blockers.push({ file: relative8, reason: "this file could not be read" });
8053
7823
  continue;
8054
7824
  }
8055
7825
  const migrated = migrateTestFile(
8056
- join35(cwd, relative9),
7826
+ join32(cwd, relative8),
8057
7827
  source,
8058
7828
  defaultImported,
8059
7829
  asyncHelpers,
@@ -8061,27 +7831,27 @@ function migrateModuleTests(cwd, setupFiles = []) {
8061
7831
  );
8062
7832
  for (const member of migrated.unhandled) {
8063
7833
  blockers.push({
8064
- file: `${relative9}:${member.line}`,
7834
+ file: `${relative8}:${member.line}`,
8065
7835
  reason: member.rule === void 0 ? `${member.member} has no rule, and guessing one is how a suite goes green for the wrong reason` : `${member.member} needs ${member.rule}`
8066
7836
  });
8067
7837
  }
8068
7838
  collected.push(...migrated.warnings);
8069
7839
  for (const refusal of migrated.refusals) {
8070
- blockers.push({ file: relative9, reason: refusal.reason });
7840
+ blockers.push({ file: relative8, reason: refusal.reason });
8071
7841
  }
8072
7842
  if (!migrated.changed) continue;
8073
7843
  files++;
8074
7844
  renamed += migrated.renamed;
8075
7845
  if (migrated.hoisted.length > 0) hoisted++;
8076
- operations.push({ kind: "write", path: relative9, contents: migrated.source });
7846
+ operations.push({ kind: "write", path: relative8, contents: migrated.source });
8077
7847
  }
8078
7848
  const warnings = [...new Set(collected)];
8079
7849
  return blockers.length > 0 ? { operations: [], files, renamed, hoisted, blockers, warnings } : { operations, files, renamed, hoisted, blockers: [], warnings };
8080
7850
  }
8081
7851
 
8082
7852
  // src/roles/test/nest/generate-config.ts
8083
- import { readdirSync as readdirSync7, readFileSync as readFileSync29, statSync as statSync7 } from "node:fs";
8084
- import { join as join36, relative as relative7 } from "node:path";
7853
+ import { readdirSync as readdirSync6, readFileSync as readFileSync27, statSync as statSync7 } from "node:fs";
7854
+ import { join as join33, relative as relative6 } from "node:path";
8085
7855
  var NEST_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/nest";
8086
7856
  var REACT_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/react";
8087
7857
  function flavourOf(reading) {
@@ -8091,7 +7861,7 @@ var SENTINEL_TEST_SETUP = "@hublo/sentinel/test/setup/jest-parity";
8091
7861
  var SENTINEL_MSW_SETUP = "@hublo/sentinel/test/setup/msw-lifecycle";
8092
7862
  var SENTINEL_WORKSPACE_SETUP = "@hublo/sentinel/test/setup/workspace";
8093
7863
  function hopsToRoot(cwd, workspaceRoot) {
8094
- const up = relative7(cwd, workspaceRoot).replaceAll("\\", "/");
7864
+ const up = relative6(cwd, workspaceRoot).replaceAll("\\", "/");
8095
7865
  return up === "" ? "." : up;
8096
7866
  }
8097
7867
  function inlineBlock(packages) {
@@ -8119,13 +7889,13 @@ function readModuleTraits(cwd) {
8119
7889
  const walk2 = (dir) => {
8120
7890
  let entries;
8121
7891
  try {
8122
- entries = readdirSync7(dir);
7892
+ entries = readdirSync6(dir);
8123
7893
  } catch {
8124
7894
  return;
8125
7895
  }
8126
7896
  for (const entry of entries) {
8127
7897
  if (SKIPPED3.has(entry)) continue;
8128
- const path = join36(dir, entry);
7898
+ const path = join33(dir, entry);
8129
7899
  let directory;
8130
7900
  try {
8131
7901
  directory = statSync7(path).isDirectory();
@@ -8139,7 +7909,7 @@ function readModuleTraits(cwd) {
8139
7909
  if (!/\.tsx?$/.test(entry)) continue;
8140
7910
  let source;
8141
7911
  try {
8142
- source = readFileSync29(path, "utf8");
7912
+ source = readFileSync27(path, "utf8");
8143
7913
  } catch {
8144
7914
  continue;
8145
7915
  }
@@ -8270,8 +8040,8 @@ ${overrides.join("\n")}
8270
8040
  }
8271
8041
 
8272
8042
  // src/roles/test/nest/read-jest-config.ts
8273
- import { existsSync as existsSync29, readFileSync as readFileSync30 } from "node:fs";
8274
- import { join as join37 } from "node:path";
8043
+ import { existsSync as existsSync26, readFileSync as readFileSync28 } from "node:fs";
8044
+ import { join as join34 } from "node:path";
8275
8045
  var CARRIED = [
8276
8046
  "testEnvironment",
8277
8047
  "coverageDirectory",
@@ -8461,7 +8231,7 @@ function extendedConfig(source, cwd) {
8461
8231
  (name) => name.replace(/^\.\//, "")
8462
8232
  );
8463
8233
  const file = candidates.find(
8464
- (name) => /^jest[\w.-]*\.(ts|js)$/.test(name) && existsSync29(join37(cwd, name))
8234
+ (name) => /^jest[\w.-]*\.(ts|js)$/.test(name) && existsSync26(join34(cwd, name))
8465
8235
  );
8466
8236
  if (file === void 0) continue;
8467
8237
  if (!new RegExp(String.raw`\{\s*\.\.\.${binding}\b`).test(source)) continue;
@@ -8488,11 +8258,11 @@ function sideEffects(source, extended) {
8488
8258
  return found;
8489
8259
  }
8490
8260
  function readJestConfig(cwd, named) {
8491
- const file = named ?? JEST_CONFIG_FILES.find((name) => existsSync29(join37(cwd, name)));
8492
- if (file === void 0 || !existsSync29(join37(cwd, file))) return void 0;
8261
+ const file = named ?? JEST_CONFIG_FILES.find((name) => existsSync26(join34(cwd, name)));
8262
+ if (file === void 0 || !existsSync26(join34(cwd, file))) return void 0;
8493
8263
  let source;
8494
8264
  try {
8495
- source = readFileSync30(join37(cwd, file), "utf8");
8265
+ source = readFileSync28(join34(cwd, file), "utf8");
8496
8266
  } catch {
8497
8267
  return { file, refusals: [`${file} could not be read`], notes: [] };
8498
8268
  }
@@ -8573,13 +8343,13 @@ function readJestConfig(cwd, named) {
8573
8343
  }
8574
8344
 
8575
8345
  // src/roles/test/nest/run-modes.ts
8576
- import { existsSync as existsSync30 } from "node:fs";
8577
- import { basename as basename2, join as join38 } from "node:path";
8346
+ import { existsSync as existsSync27 } from "node:fs";
8347
+ import { basename, join as join35 } from "node:path";
8578
8348
  var JEST_EXECUTOR = "@nx/jest:jest";
8579
8349
  var PRIMARY_CONFIG_FILE = "vitest.config.mts";
8580
8350
  var PRIMARY_TARGET = "test";
8581
8351
  function configName(declared) {
8582
- return basename2(declared.replaceAll("\\", "/"));
8352
+ return basename(declared.replaceAll("\\", "/"));
8583
8353
  }
8584
8354
  function configFileFor(target) {
8585
8355
  if (target === PRIMARY_TARGET) return PRIMARY_CONFIG_FILE;
@@ -8595,7 +8365,7 @@ function runModes(cwd, fallback) {
8595
8365
  const jestConfig = entry.options?.jestConfig;
8596
8366
  if (jestConfig === void 0) continue;
8597
8367
  const name = configName(jestConfig);
8598
- if (!existsSync30(join38(cwd, name))) continue;
8368
+ if (!existsSync27(join35(cwd, name))) continue;
8599
8369
  declared.push({
8600
8370
  target,
8601
8371
  jestConfig: name,
@@ -8620,8 +8390,8 @@ function runModes(cwd, fallback) {
8620
8390
  }
8621
8391
 
8622
8392
  // src/roles/test/written-file-formatter.ts
8623
- import { existsSync as existsSync31 } from "node:fs";
8624
- import { dirname as dirname10, join as join39 } from "node:path";
8393
+ import { existsSync as existsSync28 } from "node:fs";
8394
+ import { dirname as dirname9, join as join36 } from "node:path";
8625
8395
  var PRETTIER_CONFIGS = [
8626
8396
  ".prettierrc",
8627
8397
  ".prettierrc.json",
@@ -8640,9 +8410,9 @@ function prettierConfigFrom(cwd) {
8640
8410
  let dir = cwd;
8641
8411
  for (; ; ) {
8642
8412
  for (const name of PRETTIER_CONFIGS) {
8643
- if (existsSync31(join39(dir, name))) return join39(dir, name);
8413
+ if (existsSync28(join36(dir, name))) return join36(dir, name);
8644
8414
  }
8645
- const parent = dirname10(dir);
8415
+ const parent = dirname9(dir);
8646
8416
  if (parent === dir) return void 0;
8647
8417
  dir = parent;
8648
8418
  }
@@ -8670,12 +8440,12 @@ function writtenFileFormatter(cwd) {
8670
8440
  var SPEC_TSCONFIG = "tsconfig.spec.json";
8671
8441
  var VITEST_GLOBAL_TYPES = "vitest/globals";
8672
8442
  function specTypesOperation(cwd) {
8673
- const path = join40(cwd, SPEC_TSCONFIG);
8674
- if (!existsSync32(path)) return void 0;
8443
+ const path = join37(cwd, SPEC_TSCONFIG);
8444
+ if (!existsSync29(path)) return void 0;
8675
8445
  let config;
8676
8446
  try {
8677
8447
  config = parseJsonc(
8678
- readFileSync31(path, "utf8"),
8448
+ readFileSync29(path, "utf8"),
8679
8449
  SPEC_TSCONFIG
8680
8450
  );
8681
8451
  } catch {
@@ -8695,10 +8465,10 @@ function specTypesOperation(cwd) {
8695
8465
  }
8696
8466
  function shippedTypesPath(cwd, declaration) {
8697
8467
  try {
8698
- const manifest = createRequire6(join40(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
8699
- const file = join40(dirname11(manifest), declaration);
8700
- if (existsSync32(file)) {
8701
- const path = relative8(cwd, file);
8468
+ const manifest = createRequire5(join37(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
8469
+ const file = join37(dirname10(manifest), declaration);
8470
+ if (existsSync29(file)) {
8471
+ const path = relative7(cwd, file);
8702
8472
  return path.startsWith(".") ? path : `./${path}`;
8703
8473
  }
8704
8474
  } catch {
@@ -8709,7 +8479,7 @@ function tsconfigFileListOperations(cwd) {
8709
8479
  const operations = [];
8710
8480
  let names;
8711
8481
  try {
8712
- names = readdirSync8(cwd).filter((name) => TSCONFIG_FILE.test(name));
8482
+ names = readdirSync7(cwd).filter((name) => TSCONFIG_FILE.test(name));
8713
8483
  } catch {
8714
8484
  return operations;
8715
8485
  }
@@ -8717,7 +8487,7 @@ function tsconfigFileListOperations(cwd) {
8717
8487
  let config;
8718
8488
  try {
8719
8489
  config = parseJsonc(
8720
- readFileSync31(join40(cwd, name), "utf8"),
8490
+ readFileSync29(join37(cwd, name), "utf8"),
8721
8491
  name
8722
8492
  );
8723
8493
  } catch {
@@ -9007,117 +8777,7 @@ function plan3(context) {
9007
8777
  return { operations, notes };
9008
8778
  }
9009
8779
 
9010
- // src/roles/test/reference.ts
9011
- import { execFileSync as execFileSync2 } from "node:child_process";
9012
- import { createHash } from "node:crypto";
9013
- import { existsSync as existsSync33, mkdirSync, mkdtempSync as mkdtempSync2, readFileSync as readFileSync32, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "node:fs";
9014
- import { tmpdir as tmpdir2 } from "node:os";
9015
- import { join as join41, resolve as resolve8 } from "node:path";
9016
-
9017
- // src/roles/test/baseline.ts
9018
- function readSnapshot(report) {
9019
- const files = report?.testResults ?? [];
9020
- const passing = [];
9021
- const failing = [];
9022
- const unloadable = [];
9023
- for (const file of files) {
9024
- const assertions = file?.assertionResults ?? [];
9025
- if (file?.status === "failed" && assertions.length === 0) {
9026
- unloadable.push(typeof file.name === "string" ? file.name : "an unnamed file");
9027
- }
9028
- for (const assertion of assertions) {
9029
- const name = assertion?.fullName;
9030
- if (typeof name !== "string" || name === "") continue;
9031
- if (assertion.status === "passed") passing.push(name);
9032
- else if (assertion.status === "failed") failing.push(name);
9033
- }
9034
- }
9035
- const claimed = report?.numFailedTests;
9036
- return {
9037
- files: files.length,
9038
- passing: passing.sort(),
9039
- failing: failing.sort(),
9040
- unloadable: unloadable.sort(),
9041
- claimedFailing: typeof claimed === "number" ? claimed : failing.length
9042
- };
9043
- }
9044
- function unusableAsBaseline(snapshot) {
9045
- if (snapshot.unloadable.length > 0) {
9046
- return `${snapshot.unloadable.length} file(s) in the reference run did not load, so they reported no test at all and the run is short by however many they hold: ${snapshot.unloadable.slice(0, 3).join(", ")}. A reference that quietly lost tests cannot prove anything about the run after it`;
9047
- }
9048
- if (snapshot.claimedFailing > snapshot.failing.length) {
9049
- return `the reference run says ${snapshot.claimedFailing} test(s) failed and names only ${snapshot.failing.length} of them, so the comparison has nothing to pair the rest against and would report them as invented by the migration`;
9050
- }
9051
- if (snapshot.passing.length === 0) {
9052
- return `the reference run reports no passing test at all, so a green run after would prove nothing: there is no claim to preserve`;
9053
- }
9054
- return void 0;
9055
- }
9056
-
9057
- // src/roles/test/reference.ts
9058
- function referencePath(cwd) {
9059
- const key = createHash("sha256").update(resolve8(cwd)).digest("hex").slice(0, 16);
9060
- return join41(tmpdir2(), "sentinel-test-reference", `${key}.json`);
9061
- }
9062
- function runJest(cwd, config) {
9063
- const bin = resolveBin(cwd, "jest");
9064
- if (bin === void 0) return void 0;
9065
- const root = findWorkspaceRoot(cwd) ?? cwd;
9066
- const out = join41(mkdtempSync2(join41(tmpdir2(), "sentinel-reference-")), "jest.json");
9067
- try {
9068
- execFileSync2(bin, ["-c", join41(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
9069
- cwd: root,
9070
- stdio: ["ignore", "ignore", "ignore"],
9071
- maxBuffer: 1 << 28
9072
- });
9073
- } catch {
9074
- }
9075
- if (!existsSync33(out)) return void 0;
9076
- try {
9077
- return JSON.parse(readFileSync32(out, "utf8"));
9078
- } catch {
9079
- return void 0;
9080
- }
9081
- }
9082
- function captureReference(cwd, config) {
9083
- const report = runJest(cwd, config);
9084
- if (report === void 0) {
9085
- return {
9086
- captured: false,
9087
- message: `could not run jest to record a reference, so this migration cannot be PROVED. It is still applied: a module whose jest run is broken can move, it just cannot be compared.`
9088
- };
9089
- }
9090
- const snapshot = readSnapshot(report);
9091
- const unusable = unusableAsBaseline(snapshot);
9092
- if (unusable !== void 0) {
9093
- return {
9094
- captured: false,
9095
- message: `no reference recorded: ${unusable} The migration is applied anyway, unproved.`
9096
- };
9097
- }
9098
- const stored = {
9099
- runner: "jest",
9100
- config,
9101
- takenAt: (/* @__PURE__ */ new Date()).toISOString(),
9102
- snapshot
9103
- };
9104
- const path = referencePath(cwd);
9105
- mkdirSync(join41(path, ".."), { recursive: true });
9106
- writeFileSync3(path, `${JSON.stringify(stored, void 0, 2)}
9107
- `);
9108
- return {
9109
- captured: true,
9110
- message: `recorded ${snapshot.passing.length} passing test name(s) from ${config} as the reference. The next \`sentinel --run --test\` compares against it.`
9111
- };
9112
- }
9113
-
9114
8780
  // src/roles/test/adapters/vitest/vitest.adapter.ts
9115
- function resolveVitest(cwd) {
9116
- const fromSentinel = binFromOwnInstall("vitest", "vitest");
9117
- if (fromSentinel !== void 0) return { bin: fromSentinel, origin: "sentinel" };
9118
- const fromModule = resolveBin(cwd, "vitest");
9119
- return fromModule !== void 0 ? { bin: fromModule, origin: "module" } : { bin: void 0, origin: "module" };
9120
- }
9121
8781
  var FORMATTABLE = /\.(m|c)?(j|t)sx?$|\.json$/;
9122
8782
  var FORMAT_BATCH = 25;
9123
8783
  var VitestAdapter = class extends BaseAdapter {
@@ -9127,19 +8787,28 @@ var VitestAdapter = class extends BaseAdapter {
9127
8787
  return true;
9128
8788
  }
9129
8789
  /**
9130
- * Record what this module's tests do TODAY, at the last moment it is still true.
8790
+ * Say, once, that this migration is about to become unprovable if nobody recorded a reference.
8791
+ *
8792
+ * ⚠️ It used to RECORD one here, which welded a measurement to an act that does not own it.
8793
+ * Héla, 2026-09-25: the verification phase is isolated behind its own verb, so it extends
8794
+ * without destroying. Two things were wrong with recording here, and they are one thing seen
8795
+ * twice: a migration that could not be proved was still reported as done, because the
8796
+ * measurement was a side effect; and the reference was taken the way the migration found
8797
+ * convenient rather than the way the module runs its tests, which is how it came to record 2958
8798
+ * passing on a module whose own target sees 2957.
9131
8799
  *
9132
- * The plan about to be applied deletes the jest config, and with it any way of asking again. A
9133
- * migration that cannot be compared to what came before is a migration nobody can check, which
9134
- * is the whole reason this role has a baseline gate at all.
8800
+ * So this warns and writes nothing. The plan about to be applied deletes the jest config, and
8801
+ * with it any way of asking again, which is precisely why the warning is here and not later.
9135
8802
  */
9136
- async beforeInit(ctx) {
8803
+ beforeInit(ctx) {
9137
8804
  const adoption = readTestAdoption(ctx.cwd);
9138
8805
  if (adoption.state !== "jest") return Promise.resolve();
9139
- const [config] = adoption.jestConfigs;
9140
- if (config === void 0) return Promise.resolve();
9141
- this.say(`recording the jest reference before migrating...`);
9142
- this.say(captureReference(ctx.cwd, config).message);
8806
+ if (adoption.jestConfigs.every((config) => hasReference(ctx.cwd, suiteOf(config)))) {
8807
+ return Promise.resolve();
8808
+ }
8809
+ this.say(
8810
+ `no reference is recorded for this module, so this migration will not be provable: the jest config this deletes is the only place the old suite could be read from. Record one first with \`sentinel --validate --test\`, then migrate. Proceeding without it.`
8811
+ );
9143
8812
  return Promise.resolve();
9144
8813
  }
9145
8814
  /**
@@ -9282,7 +8951,7 @@ function fromWorkspaceRoot(moduleDir, options, paths) {
9282
8951
  if (root === void 0 || root === moduleDir) {
9283
8952
  return { cwd: moduleDir, options: [...options], paths: [...paths] };
9284
8953
  }
9285
- const absolute = (value) => isAbsolute3(value) ? value : resolve9(moduleDir, value);
8954
+ const absolute = (value) => isAbsolute3(value) ? value : resolve8(moduleDir, value);
9286
8955
  const moved = [];
9287
8956
  let previousWasConfig = false;
9288
8957
  for (const option of options) {
@@ -9311,9 +8980,9 @@ function registerTest() {
9311
8980
 
9312
8981
  // src/roles/typescript/adapters/tsc/tsc.adapter.ts
9313
8982
  import { spawnSync as spawnSync6 } from "node:child_process";
9314
- import { existsSync as existsSync34, readFileSync as readFileSync34 } from "node:fs";
9315
- import { createRequire as createRequire7 } from "node:module";
9316
- import { join as join43 } from "node:path";
8983
+ import { existsSync as existsSync30, readFileSync as readFileSync31 } from "node:fs";
8984
+ import { createRequire as createRequire6 } from "node:module";
8985
+ import { join as join39 } from "node:path";
9317
8986
 
9318
8987
  // src/roles/typescript/presets/nest.json
9319
8988
  var nest_default2 = {
@@ -9494,8 +9163,8 @@ function readTsconfigAdoption(cwd) {
9494
9163
  }
9495
9164
 
9496
9165
  // src/roles/typescript/adapters/tsc/plan.ts
9497
- import { readFileSync as readFileSync33 } from "node:fs";
9498
- import { join as join42 } from "node:path";
9166
+ import { readFileSync as readFileSync30 } from "node:fs";
9167
+ import { join as join38 } from "node:path";
9499
9168
 
9500
9169
  // src/roles/typescript/typecheck-script.ts
9501
9170
  var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
@@ -9588,7 +9257,7 @@ function planAdoption(context) {
9588
9257
  };
9589
9258
  }
9590
9259
  const existing = parseJsonc(
9591
- readFileSync33(join42(context.cwd, target.path), "utf8"),
9260
+ readFileSync30(join38(context.cwd, target.path), "utf8"),
9592
9261
  target.path
9593
9262
  );
9594
9263
  const extendsChain = composeExtends(existing.extends, preset);
@@ -9726,7 +9395,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9726
9395
  let chain;
9727
9396
  try {
9728
9397
  const parsed = parseJsonc(
9729
- readFileSync34(join43(cwd, target.path), "utf8"),
9398
+ readFileSync31(join39(cwd, target.path), "utf8"),
9730
9399
  target.path
9731
9400
  );
9732
9401
  chain = parsed.extends;
@@ -9739,7 +9408,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9739
9408
  );
9740
9409
  if (preset === void 0) return void 0;
9741
9410
  try {
9742
- createRequire7(join43(cwd, "noop.js")).resolve(preset);
9411
+ createRequire6(join39(cwd, "noop.js")).resolve(preset);
9743
9412
  return void 0;
9744
9413
  } catch {
9745
9414
  return preset;
@@ -9835,7 +9504,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9835
9504
  referencedProjects(cwd, config) {
9836
9505
  try {
9837
9506
  const parsed = parseJsonc(
9838
- readFileSync34(join43(cwd, config), "utf8"),
9507
+ readFileSync31(join39(cwd, config), "utf8"),
9839
9508
  config
9840
9509
  );
9841
9510
  const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
@@ -9851,7 +9520,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9851
9520
  * check.
9852
9521
  */
9853
9522
  typecheckTarget(cwd) {
9854
- if (existsSync34(join43(cwd, "tsconfig.json"))) return "tsconfig.json";
9523
+ if (existsSync30(join39(cwd, "tsconfig.json"))) return "tsconfig.json";
9855
9524
  const target = resolveTsconfigTarget(cwd);
9856
9525
  return target.reason === "none" ? null : target.path;
9857
9526
  }
@@ -10000,7 +9669,7 @@ function detectFramework(packageJson) {
10000
9669
  }
10001
9670
 
10002
9671
  // src/core/dispatch.ts
10003
- import { basename as basename3 } from "node:path";
9672
+ import { basename as basename2 } from "node:path";
10004
9673
 
10005
9674
  // src/shared/text.ts
10006
9675
  function ensureLines(current, lines) {
@@ -10056,8 +9725,8 @@ function replaceLines(current, replacements) {
10056
9725
  }
10057
9726
 
10058
9727
  // src/core/apply-plan.ts
10059
- import { existsSync as existsSync35, readFileSync as readFileSync35, renameSync, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "node:fs";
10060
- import { resolve as resolve10, sep } from "node:path";
9728
+ import { existsSync as existsSync31, readFileSync as readFileSync32, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "node:fs";
9729
+ import { resolve as resolve9, sep } from "node:path";
10061
9730
  import { applyEdits as applyEdits3, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
10062
9731
 
10063
9732
  // src/shared/deep-merge.ts
@@ -10067,15 +9736,15 @@ function isPlainObject(value) {
10067
9736
 
10068
9737
  // src/core/apply-plan.ts
10069
9738
  function resolveWithinRoot(cwd, relativePath) {
10070
- const root = resolve10(cwd);
10071
- const absolutePath = resolve10(root, relativePath);
9739
+ const root = resolve9(cwd);
9740
+ const absolutePath = resolve9(root, relativePath);
10072
9741
  if (absolutePath !== root && !absolutePath.startsWith(root + sep)) {
10073
9742
  throw new Error(`Refusing to write outside the module root: "${relativePath}".`);
10074
9743
  }
10075
9744
  return absolutePath;
10076
9745
  }
10077
9746
  function readIfExists(absolutePath) {
10078
- return existsSync35(absolutePath) ? readFileSync35(absolutePath, "utf8") : void 0;
9747
+ return existsSync31(absolutePath) ? readFileSync32(absolutePath, "utf8") : void 0;
10079
9748
  }
10080
9749
  function* leaves(value, prefix = []) {
10081
9750
  for (const [key, keyValue] of Object.entries(value)) {
@@ -10177,14 +9846,14 @@ function preparePlan(cwd, plan4) {
10177
9846
  }
10178
9847
  function writeFileAtomic(absolutePath, contents) {
10179
9848
  const tempPath = `${absolutePath}.sentinel-${process.pid}.tmp`;
10180
- writeFileSync4(tempPath, contents);
9849
+ writeFileSync3(tempPath, contents);
10181
9850
  renameSync(tempPath, absolutePath);
10182
9851
  }
10183
9852
  function applyPlan(cwd, plan4) {
10184
9853
  const changed = preparePlan(cwd, plan4).filter((file) => file.before !== file.after);
10185
9854
  for (const file of changed) {
10186
9855
  if (file.deleted) {
10187
- rmSync5(file.absolutePath, { force: true });
9856
+ rmSync4(file.absolutePath, { force: true });
10188
9857
  continue;
10189
9858
  }
10190
9859
  writeFileAtomic(file.absolutePath, file.after);
@@ -10193,8 +9862,8 @@ function applyPlan(cwd, plan4) {
10193
9862
  }
10194
9863
 
10195
9864
  // src/core/config/preset-evidence.ts
10196
- import { existsSync as existsSync36, readdirSync as readdirSync9, readFileSync as readFileSync36 } from "node:fs";
10197
- import { join as join44 } from "node:path";
9865
+ import { existsSync as existsSync32, readdirSync as readdirSync8, readFileSync as readFileSync33 } from "node:fs";
9866
+ import { join as join40 } from "node:path";
10198
9867
  var PATH_SIGNALS = [
10199
9868
  {
10200
9869
  preset: "nest",
@@ -10222,18 +9891,18 @@ function sourceImports(cwd, pattern) {
10222
9891
  const dir = stack.pop();
10223
9892
  let entries;
10224
9893
  try {
10225
- entries = readdirSync9(dir, { withFileTypes: true });
9894
+ entries = readdirSync8(dir, { withFileTypes: true });
10226
9895
  } catch {
10227
9896
  continue;
10228
9897
  }
10229
9898
  for (const entry of entries) {
10230
9899
  if (entry.isDirectory()) {
10231
- if (!NOT_SCANNED.has(entry.name)) stack.push(join44(dir, entry.name));
9900
+ if (!NOT_SCANNED.has(entry.name)) stack.push(join40(dir, entry.name));
10232
9901
  continue;
10233
9902
  }
10234
9903
  if (!SOURCE_FILE2.test(entry.name)) continue;
10235
9904
  try {
10236
- if (pattern.test(readFileSync36(join44(dir, entry.name), "utf8"))) return true;
9905
+ if (pattern.test(readFileSync33(join40(dir, entry.name), "utf8"))) return true;
10237
9906
  } catch {
10238
9907
  continue;
10239
9908
  }
@@ -10242,11 +9911,11 @@ function sourceImports(cwd, pattern) {
10242
9911
  return false;
10243
9912
  }
10244
9913
  function dependencyNames(cwd) {
10245
- const path = join44(cwd, "package.json");
10246
- if (!existsSync36(path)) return [];
9914
+ const path = join40(cwd, "package.json");
9915
+ if (!existsSync32(path)) return [];
10247
9916
  try {
10248
9917
  const manifest = parseJsonc(
10249
- readFileSync36(path, "utf8"),
9918
+ readFileSync33(path, "utf8"),
10250
9919
  path
10251
9920
  );
10252
9921
  return [
@@ -10260,7 +9929,7 @@ function dependencyNames(cwd) {
10260
9929
  function declaresJsx(cwd) {
10261
9930
  let entries;
10262
9931
  try {
10263
- entries = readdirSync9(cwd).filter(
9932
+ entries = readdirSync8(cwd).filter(
10264
9933
  (name) => name.startsWith("tsconfig") && name.endsWith(".json")
10265
9934
  );
10266
9935
  } catch {
@@ -10269,7 +9938,7 @@ function declaresJsx(cwd) {
10269
9938
  for (const name of entries) {
10270
9939
  try {
10271
9940
  const config = parseJsonc(
10272
- readFileSync36(join44(cwd, name), "utf8"),
9941
+ readFileSync33(join40(cwd, name), "utf8"),
10273
9942
  name
10274
9943
  );
10275
9944
  if (config.compilerOptions?.jsx !== void 0) return true;
@@ -10416,7 +10085,7 @@ async function dispatch(opts) {
10416
10085
  }
10417
10086
  if (adapter.beforeInit) {
10418
10087
  await adapter.beforeInit({
10419
- module: basename3(opts.cwd),
10088
+ module: basename2(opts.cwd),
10420
10089
  cwd: opts.cwd,
10421
10090
  preset,
10422
10091
  ci: false,
@@ -10435,7 +10104,7 @@ async function dispatch(opts) {
10435
10104
  process.stderr.write(` fixing what ${opts.target} can fix automatically...
10436
10105
  `);
10437
10106
  await adapter.afterInit({
10438
- module: basename3(opts.cwd),
10107
+ module: basename2(opts.cwd),
10439
10108
  cwd: opts.cwd,
10440
10109
  preset,
10441
10110
  ci: false,