@hublo/sentinel 1.4.0-alpha.22 → 1.4.0-alpha.23

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.
@@ -16,7 +16,7 @@ import {
16
16
  registerAdapters,
17
17
  resolve,
18
18
  resolveBin
19
- } from "../chunk-SIGFE5KL.js";
19
+ } from "../chunk-7ADFV7P4.js";
20
20
  import {
21
21
  WORKSPACE_ROOT_MARKER,
22
22
  ensureWorkspacePrep,
@@ -5406,9 +5406,7 @@ function registerLint() {
5406
5406
 
5407
5407
  // src/roles/test/adapters/vitest/vitest.adapter.ts
5408
5408
  import { spawnSync as spawnSync5 } from "node:child_process";
5409
- import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync31 } from "node:fs";
5410
- import { tmpdir as tmpdir2 } from "node:os";
5411
- import { isAbsolute as isAbsolute3, join as join39, resolve as resolve6 } from "node:path";
5409
+ import { isAbsolute as isAbsolute3, resolve as resolve6 } from "node:path";
5412
5410
 
5413
5411
  // src/roles/test/presets/toolchain.json
5414
5412
  var toolchain_default2 = {
@@ -8705,166 +8703,6 @@ function readSnapshot(report) {
8705
8703
  claimedFailing: typeof claimed === "number" ? claimed : failing.length
8706
8704
  };
8707
8705
  }
8708
- function difference(left, right) {
8709
- const remaining = /* @__PURE__ */ new Map();
8710
- for (const name of right) remaining.set(name, (remaining.get(name) ?? 0) + 1);
8711
- const missing = [];
8712
- for (const name of left) {
8713
- const left_ = remaining.get(name) ?? 0;
8714
- if (left_ === 0) {
8715
- missing.push(name);
8716
- continue;
8717
- }
8718
- remaining.set(name, left_ - 1);
8719
- }
8720
- return missing.sort();
8721
- }
8722
- var withoutRenderingQuotes = (name) => name.replaceAll(String.raw`\'`, "'").replaceAll(String.raw`\"`, '"').replaceAll(/['"`]\s*\+\s*['"`]/g, "").replaceAll(/(^|[\s:,[{(])(''|"")($|[\s,)\]}])/g, "$1<empty-string>$3").replaceAll(/['"`]/g, "");
8723
- function withoutRenderingNoise(name) {
8724
- const spelled = withSortedKeys(
8725
- withoutUndefinedEntries(
8726
- withoutErrorRendering(
8727
- withoutNaN(
8728
- withoutTimestamps(withoutRenderingQuotes(withoutClassBodies(withoutStackFrames(name))))
8729
- )
8730
- )
8731
- )
8732
- );
8733
- return withoutArrayBrackets(
8734
- spelled.replaceAll(/\[\s+/g, "[").replaceAll(/\s+\]/g, "]").replaceAll(/\{\s+/g, "{").replaceAll(/\s+\}/g, "}").replaceAll(/(?<=[[{,\s])\+0\b/g, "0").replaceAll(/:\s+/g, ":").replaceAll(/,\s+/g, ",")
8735
- );
8736
- }
8737
- function withoutArrayBrackets(name) {
8738
- return name.replaceAll(
8739
- /\[([^[\]]*)\]/g,
8740
- (match, inner) => (
8741
- // `[Object]` and `[Array]` are the formatter saying it STOPPED, not an array literal.
8742
- // Stripping their brackets would erase the marker `collapsedOf` pairs on.
8743
- /^(?:Object|Array)$/.test(inner) ? match : inner.replaceAll(/,\s+/g, ",")
8744
- )
8745
- );
8746
- }
8747
- var withoutClassBodies = (name) => name.replaceAll(
8748
- /class\s+([A-Za-z_$][\w$]*)\s*\{\s*\}/g,
8749
- (_match, className) => `class ${className}`
8750
- ).replaceAll(
8751
- /class\s+([A-Za-z_$][\w$]*)[\s\S]*?\n\}/g,
8752
- (_match, className) => `class ${className}`
8753
- );
8754
- var withoutTimestamps = (name) => name.replaceAll(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z/g, "<instant>");
8755
- var withoutNaN = (name) => name.replaceAll(/\bNaN\b/g, "null");
8756
- function entriesOf(inner) {
8757
- const parts = [];
8758
- let depth = 0;
8759
- let start = 0;
8760
- for (let index = 0; index < inner.length; index += 1) {
8761
- const character = inner[index];
8762
- if (character === "[" || character === "(") depth += 1;
8763
- else if (character === "]" || character === ")") depth -= 1;
8764
- else if (character === "," && depth === 0) {
8765
- parts.push(inner.slice(start, index));
8766
- start = index + 1;
8767
- }
8768
- if (depth < 0) return void 0;
8769
- }
8770
- if (depth !== 0) return void 0;
8771
- parts.push(inner.slice(start));
8772
- return parts.map((part) => part.trim()).filter(Boolean);
8773
- }
8774
- var sortedGroup = (match, inner) => {
8775
- if (!inner.includes(":")) return match;
8776
- const parts = entriesOf(inner);
8777
- if (parts === void 0) return match;
8778
- if (parts.length < 2 || !parts.every((part) => part.includes(":"))) return match;
8779
- return `{${[...parts].sort().join(",")}}`;
8780
- };
8781
- var HELD = "\u241F";
8782
- var withSortedKeys = (name) => {
8783
- const held = [];
8784
- let current = name;
8785
- for (let depth = 0; depth < 24; depth += 1) {
8786
- const next = current.replaceAll(/\{([^{}]*)\}/g, (match, inner) => {
8787
- held.push(sortedGroup(match, inner));
8788
- return `${HELD}${held.length - 1}${HELD}`;
8789
- });
8790
- if (next === current) break;
8791
- current = next;
8792
- }
8793
- for (let depth = 0; depth < 24 && current.includes(HELD); depth += 1) {
8794
- current = current.replaceAll(
8795
- new RegExp(`${HELD}(\\d+)${HELD}`, "g"),
8796
- (_match, index) => held[Number(index)]
8797
- );
8798
- }
8799
- return current;
8800
- };
8801
- var withoutStackFrames = (name) => {
8802
- const frame = name.search(/\n\s+at\s/);
8803
- return frame === -1 ? name : name.slice(0, frame);
8804
- };
8805
- var withoutErrorRendering = (name) => name.replaceAll(/\[[A-Za-z]*(?:Error|Exception):[^\][]*\]/g, "{}");
8806
- var withoutUndefinedEntries = (name) => name.replaceAll(/([{,])\s*[A-Za-z_$][\w$]*\s*:\s*undefined\s*(?=[,}])/g, "$1").replaceAll(/\{\s*,/g, "{").replaceAll(/,\s*\}/g, "}").replaceAll(/\{\s*\}/g, "{}");
8807
- function collapsedOf(candidate, name) {
8808
- if (!/\[(?:Object|Array)\]/.test(candidate)) return false;
8809
- const pattern = candidate.split(/\[(?:Object|Array)\]/).map((part) => part.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
8810
- return new RegExp(`^${pattern}$`, "s").test(name);
8811
- }
8812
- function truncationOf(candidate, name) {
8813
- const cut = candidate.indexOf("\u2026");
8814
- if (cut === -1) return false;
8815
- const head = candidate.slice(0, cut);
8816
- const tail = candidate.slice(cut + 1);
8817
- return name.startsWith(head) && name.endsWith(tail);
8818
- }
8819
- function pairRenamed(missing, added) {
8820
- const remaining = [...added];
8821
- const renamed = [];
8822
- const unmatched = [];
8823
- for (const name of missing) {
8824
- const normalised = withoutRenderingNoise(name);
8825
- const at = remaining.findIndex((candidate) => withoutRenderingNoise(candidate) === normalised);
8826
- if (at === -1) {
8827
- unmatched.push(name);
8828
- continue;
8829
- }
8830
- renamed.push({ before: name, after: remaining[at] });
8831
- remaining.splice(at, 1);
8832
- }
8833
- const lost = [];
8834
- for (const name of unmatched) {
8835
- const normalised = withoutRenderingNoise(name);
8836
- const at = remaining.findIndex(
8837
- (candidate) => truncationOf(withoutRenderingNoise(candidate), normalised) || collapsedOf(normalised, withoutRenderingNoise(candidate))
8838
- );
8839
- if (at === -1) {
8840
- lost.push(name);
8841
- continue;
8842
- }
8843
- renamed.push({ before: name, after: remaining[at] });
8844
- remaining.splice(at, 1);
8845
- }
8846
- return { renamed, missing: lost, added: remaining };
8847
- }
8848
- function compareSnapshots(before, after) {
8849
- const beforeAll = [...before.passing, ...before.failing];
8850
- const afterAll = [...after.passing, ...after.failing];
8851
- const paired = pairRenamed(difference(beforeAll, afterAll), difference(afterAll, beforeAll));
8852
- const renamedBefore = new Set(paired.renamed.map((pair) => pair.before));
8853
- const regressed = difference(before.passing, after.passing).filter(
8854
- (name) => !paired.missing.includes(name) && !renamedBefore.has(name)
8855
- );
8856
- const failedBefore = new Set(before.failing);
8857
- const recovered = after.passing.filter((name) => failedBefore.has(name));
8858
- return {
8859
- identical: paired.missing.length === 0 && paired.added.length === 0 && regressed.length === 0,
8860
- missing: paired.missing,
8861
- added: paired.added,
8862
- regressed,
8863
- renamed: paired.renamed,
8864
- recovered,
8865
- alreadyFailing: before.failing.length
8866
- };
8867
- }
8868
8706
  function unusableAsBaseline(snapshot) {
8869
8707
  if (snapshot.unloadable.length > 0) {
8870
8708
  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`;
@@ -8877,45 +8715,6 @@ function unusableAsBaseline(snapshot) {
8877
8715
  }
8878
8716
  return void 0;
8879
8717
  }
8880
- function renamedExample(before, after) {
8881
- let shared = 0;
8882
- while (shared < before.length && shared < after.length && before[shared] === after[shared]) {
8883
- shared += 1;
8884
- }
8885
- const boundary = before.lastIndexOf(" ", shared);
8886
- const from = boundary > 0 ? boundary + 1 : 0;
8887
- const ellipsis = from > 0 ? "\u2026" : "";
8888
- return `"${ellipsis}${before.slice(from)}" -> "${ellipsis}${after.slice(from)}"`;
8889
- }
8890
- function describeComparison(comparison) {
8891
- const firstRename = comparison.renamed[0];
8892
- const renamedNote = firstRename === void 0 ? "" : ` ${comparison.renamed.length} test(s) kept their identity and changed name, because the runners render an each title's interpolated value differently (e.g. ${renamedExample(firstRename.before, firstRename.after)}).`;
8893
- const alreadyRedNote = comparison.alreadyFailing > 0 ? ` ${comparison.alreadyFailing} test(s) were already failing before the migration and are outside this comparison, which covers only what passed.` : "";
8894
- const recoveredNote = comparison.recovered.length > 0 ? ` ${comparison.recovered.length} test(s) were failing before and pass now.` : "";
8895
- if (comparison.identical) {
8896
- return `every test that ran before runs and passes now.${recoveredNote}${renamedNote}${alreadyRedNote}`;
8897
- }
8898
- const parts = [];
8899
- if (comparison.recovered.length > 0) {
8900
- parts.push(`${comparison.recovered.length} test(s) were failing before and pass now`);
8901
- }
8902
- if (comparison.missing.length > 0) {
8903
- parts.push(
8904
- `${comparison.missing.length} test(s) no longer exist: ${comparison.missing.slice(0, 3).join(", ")}`
8905
- );
8906
- }
8907
- if (comparison.added.length > 0) {
8908
- parts.push(
8909
- `${comparison.added.length} test(s) appeared, which a migration should not do: ${comparison.added.slice(0, 3).join(", ")}`
8910
- );
8911
- }
8912
- if (comparison.regressed.length > 0) {
8913
- parts.push(
8914
- `${comparison.regressed.length} test(s) passed before and do not now: ${comparison.regressed.slice(0, 3).join(", ")}`
8915
- );
8916
- }
8917
- return `${parts.join("; ")}.${renamedNote}${alreadyRedNote}`;
8918
- }
8919
8718
 
8920
8719
  // src/roles/test/reference.ts
8921
8720
  function referencePath(cwd) {
@@ -8973,26 +8772,6 @@ function captureReference(cwd, config) {
8973
8772
  message: `recorded ${snapshot.passing.length} passing test name(s) from ${config} as the reference. The next \`sentinel --run --test\` compares against it.`
8974
8773
  };
8975
8774
  }
8976
- function hasReference(cwd) {
8977
- return existsSync31(referencePath(cwd));
8978
- }
8979
- function compareAgainstReference(cwd, report) {
8980
- const path = referencePath(cwd);
8981
- let stored;
8982
- try {
8983
- stored = JSON.parse(readFileSync30(path, "utf8"));
8984
- } catch {
8985
- rmSync3(path, { force: true });
8986
- return { ok: true, message: `the recorded reference could not be read, so nothing was proved.` };
8987
- }
8988
- const comparison = compareSnapshots(stored.snapshot, readSnapshot(report));
8989
- if (comparison.identical) rmSync3(path, { force: true });
8990
- const kept = comparison.identical ? "" : ` The reference is KEPT until a run passes it, so this does not go green by running again. Fix the suite and re-run, or delete ${path} to abandon the proof on purpose.`;
8991
- return {
8992
- ok: comparison.identical,
8993
- message: `${describeComparison(comparison)} (reference: ${stored.config}, ${stored.takenAt})${kept}`
8994
- };
8995
- }
8996
8775
 
8997
8776
  // src/roles/test/adapters/vitest/vitest.adapter.ts
8998
8777
  function resolveVitest(cwd) {
@@ -9146,31 +8925,13 @@ var VitestAdapter = class extends BaseAdapter {
9146
8925
  });
9147
8926
  const { cwd: runFrom, options, paths } = fromWorkspaceRoot(ctx.cwd, typedOptions, typedPaths);
9148
8927
  const watching = options.includes("--watch") || options.includes("-w");
9149
- const proving = !watching && hasReference(ctx.cwd);
9150
- const reportFile = proving ? join39(mkdtempSync2(join39(tmpdir2(), "sentinel-run-")), "vitest.json") : void 0;
9151
- const reporters = reportFile === void 0 ? [] : ["--reporter=default", "--reporter=json", `--outputFile=${reportFile}`];
9152
- const argv = watching ? [...options, ...paths] : ["run", ...reporters, ...options, ...paths];
8928
+ const argv = watching ? [...options, ...paths] : ["run", ...options, ...paths];
9153
8929
  const result = spawnSync5(bin, argv, { cwd: runFrom, stdio: "inherit" });
9154
8930
  if (result.error) {
9155
8931
  this.say(`could not run vitest (${result.error.message})`);
9156
8932
  return Promise.resolve({ ok: false, code: 1 });
9157
8933
  }
9158
8934
  const code = result.status ?? 1;
9159
- if (reportFile !== void 0) {
9160
- let report;
9161
- try {
9162
- report = JSON.parse(readFileSync31(reportFile, "utf8"));
9163
- } catch {
9164
- report = void 0;
9165
- }
9166
- if (report === void 0) {
9167
- this.say(`vitest wrote no report, so this run proved nothing against the reference.`);
9168
- } else {
9169
- const outcome = compareAgainstReference(ctx.cwd, report);
9170
- this.say(outcome.message);
9171
- if (!outcome.ok) return Promise.resolve({ ok: false, code: code === 0 ? 1 : code });
9172
- }
9173
- }
9174
8935
  return Promise.resolve({ ok: code === 0, code });
9175
8936
  }
9176
8937
  say(message) {
@@ -9212,9 +8973,9 @@ function registerTest() {
9212
8973
 
9213
8974
  // src/roles/typescript/adapters/tsc/tsc.adapter.ts
9214
8975
  import { spawnSync as spawnSync6 } from "node:child_process";
9215
- import { existsSync as existsSync32, readFileSync as readFileSync33 } from "node:fs";
8976
+ import { existsSync as existsSync32, readFileSync as readFileSync32 } from "node:fs";
9216
8977
  import { createRequire as createRequire7 } from "node:module";
9217
- import { join as join41 } from "node:path";
8978
+ import { join as join40 } from "node:path";
9218
8979
 
9219
8980
  // src/roles/typescript/presets/nest.json
9220
8981
  var nest_default2 = {
@@ -9395,8 +9156,8 @@ function readTsconfigAdoption(cwd) {
9395
9156
  }
9396
9157
 
9397
9158
  // src/roles/typescript/adapters/tsc/plan.ts
9398
- import { readFileSync as readFileSync32 } from "node:fs";
9399
- import { join as join40 } from "node:path";
9159
+ import { readFileSync as readFileSync31 } from "node:fs";
9160
+ import { join as join39 } from "node:path";
9400
9161
 
9401
9162
  // src/roles/typescript/typecheck-script.ts
9402
9163
  var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
@@ -9489,7 +9250,7 @@ function planAdoption(context) {
9489
9250
  };
9490
9251
  }
9491
9252
  const existing = parseJsonc(
9492
- readFileSync32(join40(context.cwd, target.path), "utf8"),
9253
+ readFileSync31(join39(context.cwd, target.path), "utf8"),
9493
9254
  target.path
9494
9255
  );
9495
9256
  const extendsChain = composeExtends(existing.extends, preset);
@@ -9627,7 +9388,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9627
9388
  let chain;
9628
9389
  try {
9629
9390
  const parsed = parseJsonc(
9630
- readFileSync33(join41(cwd, target.path), "utf8"),
9391
+ readFileSync32(join40(cwd, target.path), "utf8"),
9631
9392
  target.path
9632
9393
  );
9633
9394
  chain = parsed.extends;
@@ -9640,7 +9401,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9640
9401
  );
9641
9402
  if (preset === void 0) return void 0;
9642
9403
  try {
9643
- createRequire7(join41(cwd, "noop.js")).resolve(preset);
9404
+ createRequire7(join40(cwd, "noop.js")).resolve(preset);
9644
9405
  return void 0;
9645
9406
  } catch {
9646
9407
  return preset;
@@ -9736,7 +9497,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9736
9497
  referencedProjects(cwd, config) {
9737
9498
  try {
9738
9499
  const parsed = parseJsonc(
9739
- readFileSync33(join41(cwd, config), "utf8"),
9500
+ readFileSync32(join40(cwd, config), "utf8"),
9740
9501
  config
9741
9502
  );
9742
9503
  const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
@@ -9752,7 +9513,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9752
9513
  * check.
9753
9514
  */
9754
9515
  typecheckTarget(cwd) {
9755
- if (existsSync32(join41(cwd, "tsconfig.json"))) return "tsconfig.json";
9516
+ if (existsSync32(join40(cwd, "tsconfig.json"))) return "tsconfig.json";
9756
9517
  const target = resolveTsconfigTarget(cwd);
9757
9518
  return target.reason === "none" ? null : target.path;
9758
9519
  }
@@ -9957,7 +9718,7 @@ function replaceLines(current, replacements) {
9957
9718
  }
9958
9719
 
9959
9720
  // src/core/apply-plan.ts
9960
- import { existsSync as existsSync33, readFileSync as readFileSync34, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
9721
+ import { existsSync as existsSync33, readFileSync as readFileSync33, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
9961
9722
  import { resolve as resolve7, sep } from "node:path";
9962
9723
  import { applyEdits as applyEdits3, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
9963
9724
 
@@ -9976,7 +9737,7 @@ function resolveWithinRoot(cwd, relativePath) {
9976
9737
  return absolutePath;
9977
9738
  }
9978
9739
  function readIfExists(absolutePath) {
9979
- return existsSync33(absolutePath) ? readFileSync34(absolutePath, "utf8") : void 0;
9740
+ return existsSync33(absolutePath) ? readFileSync33(absolutePath, "utf8") : void 0;
9980
9741
  }
9981
9742
  function* leaves(value, prefix = []) {
9982
9743
  for (const [key, keyValue] of Object.entries(value)) {
@@ -10094,8 +9855,8 @@ function applyPlan(cwd, plan4) {
10094
9855
  }
10095
9856
 
10096
9857
  // src/core/config/preset-evidence.ts
10097
- import { existsSync as existsSync34, readdirSync as readdirSync9, readFileSync as readFileSync35 } from "node:fs";
10098
- import { join as join42 } from "node:path";
9858
+ import { existsSync as existsSync34, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "node:fs";
9859
+ import { join as join41 } from "node:path";
10099
9860
  var PATH_SIGNALS = [
10100
9861
  {
10101
9862
  preset: "nest",
@@ -10129,12 +9890,12 @@ function sourceImports(cwd, pattern) {
10129
9890
  }
10130
9891
  for (const entry of entries) {
10131
9892
  if (entry.isDirectory()) {
10132
- if (!NOT_SCANNED.has(entry.name)) stack.push(join42(dir, entry.name));
9893
+ if (!NOT_SCANNED.has(entry.name)) stack.push(join41(dir, entry.name));
10133
9894
  continue;
10134
9895
  }
10135
9896
  if (!SOURCE_FILE2.test(entry.name)) continue;
10136
9897
  try {
10137
- if (pattern.test(readFileSync35(join42(dir, entry.name), "utf8"))) return true;
9898
+ if (pattern.test(readFileSync34(join41(dir, entry.name), "utf8"))) return true;
10138
9899
  } catch {
10139
9900
  continue;
10140
9901
  }
@@ -10143,11 +9904,11 @@ function sourceImports(cwd, pattern) {
10143
9904
  return false;
10144
9905
  }
10145
9906
  function dependencyNames(cwd) {
10146
- const path = join42(cwd, "package.json");
9907
+ const path = join41(cwd, "package.json");
10147
9908
  if (!existsSync34(path)) return [];
10148
9909
  try {
10149
9910
  const manifest = parseJsonc(
10150
- readFileSync35(path, "utf8"),
9911
+ readFileSync34(path, "utf8"),
10151
9912
  path
10152
9913
  );
10153
9914
  return [
@@ -10170,7 +9931,7 @@ function declaresJsx(cwd) {
10170
9931
  for (const name of entries) {
10171
9932
  try {
10172
9933
  const config = parseJsonc(
10173
- readFileSync35(join42(cwd, name), "utf8"),
9934
+ readFileSync34(join41(cwd, name), "utf8"),
10174
9935
  name
10175
9936
  );
10176
9937
  if (config.compilerOptions?.jsx !== void 0) return true;
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-SIGFE5KL.js";
10
+ } from "./chunk-7ADFV7P4.js";
11
11
  import "./chunk-NW6UHNYX.js";
12
12
  export {
13
13
  BaseAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.4.0-alpha.22",
3
+ "version": "1.4.0-alpha.23",
4
4
  "description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
5
5
  "type": "module",
6
6
  "license": "MIT",